diff --git a/README.md b/README.md index 70620ec..f7985a1 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/cookiecutter.json b/cookiecutter.json new file mode 100644 index 0000000..5d9060e --- /dev/null +++ b/cookiecutter.json @@ -0,0 +1,33 @@ +{ + "package_name": "my_python_package", + "project_short_description": "A modern Python package", + "author_name": "Your Name", + "author_email": "your.email@example.com", + "github_username": "yourusername", + "version": "0.1.0", + "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"], + "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" + ] +} diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py new file mode 100755 index 0000000..654d600 --- /dev/null +++ b/hooks/post_gen_project.py @@ -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() diff --git a/pyproject.toml b/pyproject.toml index 557ec67..0a48b8a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dev = [ "pytest-cov", "pytest-sugar", "hypothesis>=6.112.0", + "cookiecutter", ] docs = [ "mkdocs", diff --git a/{{cookiecutter.package_name}}/.github/workflows/build_docs.yaml b/{{cookiecutter.package_name}}/.github/workflows/build_docs.yaml new file mode 100644 index 0000000..417dcb7 --- /dev/null +++ b/{{cookiecutter.package_name}}/.github/workflows/build_docs.yaml @@ -0,0 +1,58 @@ +name: Build Docs + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + +jobs: + 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 + + 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 diff --git a/{{cookiecutter.package_name}}/.github/workflows/test.yaml b/{{cookiecutter.package_name}}/.github/workflows/test.yaml new file mode 100644 index 0000000..ab82709 --- /dev/null +++ b/{{cookiecutter.package_name}}/.github/workflows/test.yaml @@ -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 %} diff --git a/{{cookiecutter.package_name}}/.pre-commit-config.yaml b/{{cookiecutter.package_name}}/.pre-commit-config.yaml new file mode 100644 index 0000000..0afe921 --- /dev/null +++ b/{{cookiecutter.package_name}}/.pre-commit-config.yaml @@ -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] diff --git a/{{cookiecutter.package_name}}/CHANGELOG.md b/{{cookiecutter.package_name}}/CHANGELOG.md new file mode 100644 index 0000000..e95ba6f --- /dev/null +++ b/{{cookiecutter.package_name}}/CHANGELOG.md @@ -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 diff --git a/{{cookiecutter.package_name}}/LICENSE b/{{cookiecutter.package_name}}/LICENSE new file mode 100644 index 0000000..b78597d --- /dev/null +++ b/{{cookiecutter.package_name}}/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Mathew Shen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/{{cookiecutter.package_name}}/README.md b/{{cookiecutter.package_name}}/README.md new file mode 100644 index 0000000..a5bb940 --- /dev/null +++ b/{{cookiecutter.package_name}}/README.md @@ -0,0 +1,67 @@ +# {{ cookiecutter.package_name }} + +[![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) + +[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) +[![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) +{%- if cookiecutter.include_pre_commit == "y" %} +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/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/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}/branch/master/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}) +{%- endif %} + +## About + +{{ cookiecutter.project_short_description }} + +## Get Started + +1. Set up your development environment: + + ```bash + # Clone your repository + git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}.git + cd {{ cookiecutter.package_name }} + + # Set up environment using uv + uv venv + source .venv/bin/activate # On Windows: .venv\Scripts\activate + + # Install development dependencies with uv + uv sync --all-extras --dev + {%- if cookiecutter.include_pre_commit == "y" %} + + # Initialize pre-commit hooks + uv run pre-commit install + {%- endif %} + ``` + +2. Start developing your package by modifying the code structure as needed. + +3. Run tests to ensure everything is working correctly: + + ```bash + uv run pytest + ``` + {%- if cookiecutter.include_docs == "y" %} + +4. Preview documentation locally: + + ```bash + uv run mkdocs serve + ``` + + Then visit [http://127.0.0.1:8000](http://127.0.0.1:8000) in your browser. + {%- endif %} + +For more details on customization options, refer to the documentation. diff --git a/{{cookiecutter.package_name}}/docs/index.md b/{{cookiecutter.package_name}}/docs/index.md new file mode 100644 index 0000000..88aa97f --- /dev/null +++ b/{{cookiecutter.package_name}}/docs/index.md @@ -0,0 +1,36 @@ +# {{ cookiecutter.package_name }} + +{{ cookiecutter.project_short_description }} + +## Installation + +```bash +pip install {{ cookiecutter.package_name }} +``` + +## Quick Start + +```python +from {{ cookiecutter.package_name }} import hello + +print(hello()) +``` + +## Development + +To set up for development: + +```bash +git clone https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}.git +cd {{ cookiecutter.package_name }} + +# Set up environment using uv +uv venv +source .venv/bin/activate # On Windows: .venv\Scripts\activate + +# Install development dependencies +uv sync --all-extras --dev + +# Run tests +uv run pytest +``` diff --git a/{{cookiecutter.package_name}}/mkdocs.yml b/{{cookiecutter.package_name}}/mkdocs.yml new file mode 100644 index 0000000..ba9bc8d --- /dev/null +++ b/{{cookiecutter.package_name}}/mkdocs.yml @@ -0,0 +1,72 @@ +site_name: {{ cookiecutter.package_name }} +repo_url: https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }} +repo_name: {{ cookiecutter.github_username }}/{{ cookiecutter.package_name }} +site_description: {{ cookiecutter.project_short_description }} +site_author: {{ cookiecutter.author_name }} +copyright: Copyright © {% now 'utc', '%Y' %} {{ cookiecutter.author_name }} + +# Page tree +nav: + - Home: index.md + +theme: + name: material + icon: + repo: fontawesome/brands/github + language: en + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: teal + accent: deep purple + toggle: + icon: material/weather-sunny + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: cyan + accent: deep purple + toggle: + icon: material/weather-night + name: Switch to light mode + 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 + +docs_dir: docs + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/{{ cookiecutter.github_username }}/ + +markdown_extensions: + - toc: + permalink: true + - pymdownx.highlight: + linenums_style: pymdownx.inline + - pymdownx.superfences + - admonition + - attr_list + +plugins: + - search diff --git a/{{cookiecutter.package_name}}/pyproject.toml b/{{cookiecutter.package_name}}/pyproject.toml new file mode 100644 index 0000000..58818c7 --- /dev/null +++ b/{{cookiecutter.package_name}}/pyproject.toml @@ -0,0 +1,157 @@ +[project] +name = "{{ cookiecutter.package_name }}" +version = "{{ cookiecutter.version }}" +description = "{{ cookiecutter.project_short_description }}" +authors = [{ name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}" }] +license = "{{ cookiecutter.license }}" +readme = "README.md" + + +classifiers = [ + "Development Status :: {{ cookiecutter.development_status }}", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + # PYTHON_VERSION_CLASSIFIERS_PLACEHOLDER +] +requires-python = ">= {{ cookiecutter.python_min_version }}" +dependencies = [] + +[project.optional-dependencies] +dev = [ + {%- if cookiecutter.include_pre_commit == "y" %} + "pre-commit", + {%- endif %} + "ipython", + "mypy", + "ruff", + "pytest", + "pytest-cov", + "pytest-sugar", + "hypothesis>=6.112.0", +] +{%- if cookiecutter.include_docs == "y" %} +docs = [ + "mkdocs", + "mkdocs-material", + "mkdocs-material-extensions", + "mkdocstrings", + "mkdocs-bibtex", + "mkdocstrings-python", + "mkdocs-autorefs", + "mkdocs-git-committers-plugin-2", + "mkdocs-git-revision-date-localized-plugin", +] +{%- endif %} + +[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 }}" +"Source Code" = "https://github.com/{{ cookiecutter.github_username }}/{{ cookiecutter.package_name }}" +"Release Notes" = "https://{{ cookiecutter.github_username }}.github.io/{{ cookiecutter.package_name }}/changelog/" + +[tool.setuptools] +zip-safe = true +include-package-data = true + +[build-system] +requires = ["setuptools >= 61.0"] +build-backend = "setuptools.build_meta" + + +[tool.setuptools.packages.find] +include = ["{{ cookiecutter.package_name }}*"] +namespaces = false + + +[tool.mypy] +ignore_missing_imports = true +strict = true + + +# Ruff configuration: https://docs.astral.sh/ruff/configuration/#configuring-ruff +[tool.ruff] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", +] + +# Same as Black. +line-length = 120 +indent-width = 4 + +# Assume Python 3.9 +target-version = "py39" + +[tool.ruff.lint] +select = [ + 'F', # Pyflakes + 'E', # pycodestyle (Error) + 'I', # isort + 'D', # pydocstyle + 'UP', # pyupgrade +] +pydocstyle = { convention = "google" } + +[tool.ruff.lint.per-file-ignores] +'docs/*' = ["D"] +'tests/*' = ["D"] + + +# Allow fix for all enabled rules (when `--fix`) is provided. +fixable = ["ALL"] +unfixable = [] + + +[tool.ruff.format] +# Like Black, use double quotes for strings. +quote-style = "double" + +# Like Black, indent with spaces, rather than tabs. +indent-style = "space" + +# Like Black, respect magic trailing commas. +skip-magic-trailing-comma = false + +# Like Black, automatically detect the appropriate line ending. +line-ending = "auto" + +# Enable auto-formatting of code examples in docstrings. Markdown, +# reStructuredText code/literal blocks and doctests are all supported. +# +# This is currently disabled by default, but it is planned for this +# to be opt-out in the future. +docstring-code-format = true + +# Set the line length limit used when formatting code snippets in +# docstrings. +# +# This only has an effect when the `docstring-code-format` setting is +# enabled. +docstring-code-line-length = "dynamic" diff --git a/{{cookiecutter.package_name}}/tests/test_hello.py b/{{cookiecutter.package_name}}/tests/test_hello.py new file mode 100644 index 0000000..009fae9 --- /dev/null +++ b/{{cookiecutter.package_name}}/tests/test_hello.py @@ -0,0 +1,6 @@ +from {{ cookiecutter.package_name }}.hello import hello + + +def test_hello() -> None: + msg = hello() + assert isinstance(msg, str) diff --git a/{{cookiecutter.package_name}}/tests/test_sort.py b/{{cookiecutter.package_name}}/tests/test_sort.py new file mode 100644 index 0000000..1636f9d --- /dev/null +++ b/{{cookiecutter.package_name}}/tests/test_sort.py @@ -0,0 +1,13 @@ +from hypothesis import given, settings +from hypothesis.strategies import floats, lists + +from {{ cookiecutter.package_name }}.sort import quick_sort + +float_list = lists(floats(allow_nan=False, allow_infinity=False), min_size=0, max_size=50) + + +class TestQuickSort: + @given(items=float_list) + @settings(max_examples=300) + def test_float(self, items: list[float]) -> None: + assert quick_sort(items) == sorted(items) diff --git a/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py new file mode 100644 index 0000000..21a4035 --- /dev/null +++ b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/__init__.py @@ -0,0 +1 @@ +"""MPPT Module.""" diff --git a/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py new file mode 100644 index 0000000..536b28e --- /dev/null +++ b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/hello.py @@ -0,0 +1,33 @@ +"""Hello World module.""" + + +def hello() -> str: + """Returns a greeting. + + Returns: + str: Hello, World! + + Examples: + >>> hello() + 'Hello, World!' + """ + return "Hello, World!" + + +def add(a: float, b: float) -> float: + """Adds two numbers together. + + Args: + a: First number + b: Second number + + Returns: + The sum of a and b + + Examples: + >>> add(1, 2) + 3 + >>> add(2, 3) + 5 + """ + return a + b diff --git a/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/py.typed b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py new file mode 100644 index 0000000..60bb67c --- /dev/null +++ b/{{cookiecutter.package_name}}/{{cookiecutter.package_name}}/sort.py @@ -0,0 +1,21 @@ +"""Sorting algorithms.""" + + +def quick_sort(xs: list[float]) -> list[float]: + """Quick sort: A very radical implementation. + + Args: + xs: comparable list + + Returns: + sorted list + + Examples: + >>> quick_sort([1, 3, 2, 4, 5]) + [1, 2, 3, 4, 5] + >>> quick_sort([1.1, 3.3, 2.2, 4.4, 5.5]) + [1.1, 2.2, 3.3, 4.4, 5.5] + """ + 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]])]