Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,43 @@

## Get Started

1. Use this template repository to create your own project repository by clicking the "Use this template" button on GitHub or visiting [MPPT](https://github.com/shenxiangzhuang/mppt).
### Option 1: Using Cookiecutter (Recommended)

The easiest way to use this template is with cookiecutter, which will interactively prompt you for all the necessary information:

1. Install cookiecutter:
```bash
pip install cookiecutter
# or
uv tool install cookiecutter
```

2. Generate your project:
```bash
cookiecutter https://github.com/shenxiangzhuang/mppt.git
```

3. Answer the prompts to customize your project:
- Project name
- Author information
- GitHub username
- Package description
- Python versions to support
- Optional features (docs, pre-commit, GitHub Actions, etc.)

4. Navigate to your new project and set up the development environment:
```bash
cd your-project-name
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv sync --all-extras --dev
```

### Option 2: Using GitHub Template

Alternatively, you can use this as a GitHub template:

1. Click the "Use this template" button on GitHub or visit [MPPT](https://github.com/shenxiangzhuang/mppt).

2. Replace all instances of MPPT, shenxiangzhuang, and other template-specific details with your own information:
- Project name, author, and GitHub username in all files
Expand Down
33 changes: 33 additions & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"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",
Comment on lines +2 to +7
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 }}.

"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"],
Comment on lines +8 to +9
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.

"license": ["MIT", "Apache-2.0", "BSD-3-Clause", "GPL-3.0", "LGPL-2.1"],
"include_docs": ["y", "n"],
"include_pre_commit": ["y", "n"],
"include_github_actions": ["y", "n"],
"include_codecov": ["y", "n"],
"development_status": [
"1 - Planning",
"2 - Pre-Alpha",
"3 - Alpha",
"4 - Beta",
"5 - Production/Stable",
"6 - Mature",
"7 - Inactive"
],
"command_line_interface": ["Click", "Argparse", "None"],
"_copy_without_render": [
"*.svg",
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
"docs/overrides/main.html"
]
}
84 changes: 84 additions & 0 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env python3
"""Post-generation hook for cookiecutter template."""

import os
import shutil
import re


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):
shutil.rmtree(file_path)
else:
os.remove(file_path)


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


def update_pyproject_toml():
"""Update pyproject.toml with correct Python version configuration."""
min_version = "{{ cookiecutter.python_min_version }}"
max_version = "{{ cookiecutter.python_max_version }}"

# Generate the list of supported Python versions
python_versions = generate_python_versions(min_version, max_version)

# Read the current pyproject.toml
with open("pyproject.toml", "r") as f:
content = f.read()

# 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)
content = content.replace(" # PYTHON_VERSION_CLASSIFIERS_PLACEHOLDER", replacement)

# Write back the updated content
with open("pyproject.toml", "w") as f:
f.write(content)


def main():
"""Remove files/directories based on configuration and fix Python versions."""

# Update Python version configuration
update_pyproject_toml()

# Remove docs if not included
if "{{ cookiecutter.include_docs }}" == "n":
remove_file_or_dir("docs")
remove_file_or_dir("mkdocs.yml")

# Remove pre-commit config if not included
if "{{ cookiecutter.include_pre_commit }}" == "n":
remove_file_or_dir(".pre-commit-config.yaml")

# Remove GitHub Actions if not included
if "{{ cookiecutter.include_github_actions }}" == "n":
remove_file_or_dir(".github")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dev = [
"pytest-cov",
"pytest-sugar",
"hypothesis>=6.112.0",
"cookiecutter",
]
docs = [
"mkdocs",
Expand Down
58 changes: 58 additions & 0 deletions {{cookiecutter.package_name}}/.github/workflows/build_docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Build Docs

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

jobs:
Comment on lines +1 to +9
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.

build-docs:
runs-on: ubuntu-latest

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
Comment on lines +13 to +31
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.


deploy-docs:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
needs: build-docs

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
Comment on lines +38 to +58
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.

44 changes: 44 additions & 0 deletions {{cookiecutter.package_name}}/.github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Test

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

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${% raw %}{{ matrix.python-version }}{% endraw %}
uses: actions/setup-python@v4
with:
python-version: ${% raw %}{{ matrix.python-version }}{% endraw %}

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

- name: Install dependencies
run: |
uv sync --all-extras --dev

- name: Run tests
run: |
uv run pytest --cov={{ cookiecutter.package_name }} --cov-report=xml
{%- if cookiecutter.include_codecov == "y" %}

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
{%- endif %}
21 changes: 21 additions & 0 deletions {{cookiecutter.package_name}}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-added-large-files

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
additional_dependencies: [types-all]
84 changes: 84 additions & 0 deletions {{cookiecutter.package_name}}/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Changelog

## [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
Loading
Loading