diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 270718a..c65d0b0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,11 @@ +# pre-commit-config.yaml +# See https://pre-commit.com for docs and https://pre-commit.com/hooks.html for available hooks + +# ====================================================== +# ======= repository hooks ======== +# ====================================================== repos: +# Official pre-commit-hooks for general checks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: @@ -6,8 +13,14 @@ repos: args: [--no-sort-keys, --indent=4, --autofix] - id: end-of-file-fixer - id: trailing-whitespace +# Format TOML and YAML files with pretty-format hooks - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks rev: v2.14.0 hooks: - id: pretty-format-yaml - args: [--autofix, --indent=4] + args: + - --autofix + - --indent + - '4' + stages: [pre-commit] + files: \.ya?ml$ diff --git a/LICENSE b/LICENSE index c856bad..604efec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2022-2023, Saez Lab +Copyright (c) 2022-2025, Saez Lab All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md new file mode 100644 index 0000000..1dd62b3 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Python project template (Saez-Rodriguez Group) + +## Description + +This is a Cookiecutter template to create Python projects. It has been tailored by the [Saez-Rodriguez Group](https://saezlab.org/) at Universität Heidelberg. + +This template provides tools to streamline setup and maintenance, letting you focus on your project instead of getting bogged down by technical details. It includes: + +- Documentation + - [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/): A sleek, responsive theme for MkDocs documentation sites. + +- Code Quality/Automation + - [Pre-commit hooks](https://pre-commit.com/): A framework for managing and running code quality hooks before commits. + +- Release Management + - [Bump2version](https://github.com/c4urself/bump2version): A tool to automate version number management in your project. + +- Testing + - [Pytest](https://docs.pytest.org/en/stable/): A powerful testing framework for writing and running Python tests. + + +## Pre-requisites +> **Note:** We strongly recommend you have the following pre-requisites before using this template. + +| Pre-requisite | Description | +| ------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| [uv](https://docs.astral.sh/uv/getting-started/installation/) | A high-performance tool for managing Python packages and virtual environments. | +| [Cruft](https://cruft.github.io/cruft/#installation) | A CLI tool to scaffold new projects using customizable templates. | +| [GitHub CLI](https://github.com/cli/cli#installation) | A command-lIne tool to interact with GitHub repositories, issues, and workflows. | + +## How-to use this template? + +In six easy steps you will have a ready to use Python project with batteries included. + +**1. Generate Your Project from the Template** + + Run the following command and follow the prompts in your terminal: + ```bash + cruft create https://github.com/saezlab/python-project.git --checkout master + ``` + +**2. Navigate to Your New Project Directory** +```bash +cd # replace with the name of your project +``` + +**3. Set Up and Activate a Virtual Environment using `uv`** +```bash +uv venv .venv +source .venv/bin/activate +``` +> This creates and activates a lightweight virtual environment in `.venv`. + +**4. Install Project Dependencies listed in the `pyproject.toml` file** + + Install all required and optional dependencies (development, testing, docs): + ```bash + uv pip install ".[dev,tests,docs]" + ``` + +**5. Install and update pre-commit hooks** +```bash +git init +pre-commit install +pre-commit autoupdate +``` + +**6. Initialize Git and Push to GitHub** +```bash + +git add . +git commit -m "Initial commit" +gh repo create / --public --source=. --push +``` + + +🎉 Congratulations! Wishing you every success as you begin your project journey 🚀 + +Saez-Rodriguez Group Team! + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first +to discuss what you would like to change. + +Please make sure to update tests as appropriate. + +## License + +Python-project template has a [BSD3](https://opensource.org/license/bsd-3-clause) license, as found in the [LICENSE](./LICENSE) file. diff --git a/README.rst b/README.rst deleted file mode 100644 index e339486..0000000 --- a/README.rst +++ /dev/null @@ -1,337 +0,0 @@ -####################### -Python project template -####################### - -.. note:: - - This document is better readable in `its Sphinx rendered version - `_. - -Create your new Python project by forking this repo. This way you start with -a number of helpful development tools set up for you. These tools are: - -* `Poetry build system `_ -* `Tox testing `_ -* `Pre-commit hooks `_ for formatting, linting, - not only Python but also YAML and rST -* `Tests with pytest `_ -* `Bump2version `_ version - increment tool -* Documentation build with `Sphinx `_ - -This repo is part of the effort towards improving development practices in -our group. For more information on these aims and the tools themselves, check -out the `Efficient development `_ page of the group Howto, and the Tools -page in `this spreadsheet `_ (or alternatively in the `saezbook `_). - -Setup -===== - -This template uses `Cookiecutter `, a tool to create projects from templates. To create a project, first -clone this repo: - -.. code:: bash - - git clone https://github.com/saezlab/python-project - -Then edit the ``cookiecutter.json`` to enter your project's metadata: - -.. code:: bash - - $EDITOR python-project/cookiecutter.json - -Finally, use ``cookiecutter`` to create your project: - -.. code:: bash - - cookiecutter python-project - -Your project will be created in a new directory, its name will depend on the -title of the project. - -Alternatively, a single command, interactive way of creating projects is -available: - -.. code:: bash - - cookiecutter gh:saezlab/python-project - -Then you'll be asked interactively for the metadata, and the final outcome will -be the same as with the previous method. - -Manual setup -============ - -Below you can read the original instructions about how to set up a project -manually from this template. This work can be done in a second with -``cookiecutter``, as presented above. We still keep the guide below because it -shows many important details about the tools included in the template. Also, -``poetry`` and ``tox`` you have to install unless these are already available -on your machine. - -Project name ------------- - -Find a name for your project. Below we use the placeholder ``new_project``. - -Install tools -------------- - -Make sure the necessary management tools are installed on your system: - -.. code:: bash - - curl -sSL https://install.python-poetry.org | python3 - - pip install --user tox pre-commit - -Copy the template ------------------ - -Clone the repo in your new project directory, and enter the directory: - -.. code:: bash - - git clone https://github.com/saezlab/python-project new_project - cd new_project - -Project git repo ----------------- - -Create a repo for your project and make the cloned repo point to this repo: - -.. code:: bash - - git remote set-url origin git@github.com:saezlab/new_project - -Rename it ---------- - -Change the placeholder ``project_name`` used in this template in all files. -Also rename the Python module directory. - -.. note:: - - If you are on a non-BSD system (e.g. GNU), remove the first argument for - ``sed -i``. That is the backup file extension, an argument that does not - exist on GNU systems. - -.. code:: bash - - find . -depth -type f ! -path '*/.git/*' -exec sed -i '' 's/project_name/new_project/g' {} + - git mv project_name new_project - git add -u - git commit -nm 'set project name' - -Add your name -------------- - -Change the author name and email in ``pyproject.toml`` and the headers of -all files in the module directory. Commit the changes. - -.. code:: bash - - find . -depth -type f ! -path '*/.git/*' -exec sed -i '' 's/Denes Turei/Your Name/g' {} + - find . -depth -type f ! -path '*/.git/*' -exec sed -i '' 's/turei\.denes@gmail\.com/your@email/g' {} + - git add -u - git commit -nm 'set author' - -Edit metadata -------------- - -In the ``pyproject.toml`` file edit the *Description, Repository* and *Bug -Tracker* fields. - -License -------- - -Change the license if necessary (by default it's GNU GPL v3). Copy over the -``LICENSE`` file with the text of your license and edit the license field in -``pyproject.toml``. Find a `list of licenses here -`_ and the `notation used by -Poetry here `_. Commit -the changes. E.g. if you want *MIT* license, copy `this text -`_ to the ``LICENSE`` file and do the -changes below: - -.. code:: bash - - find . -depth -type f ! -path '*/.git/*' -exec sed -i '' 's/GPLv3/MIT/g' {} + - sed -i '' 's/GPL-3\.0-only/MIT/g' pyproject.toml - git add LICENSE - git add pyproject.toml - git commit -nm 'set license to MIT' - -Set up the tools ----------------- - -Initialize ``poetry`` and ``tox``: - -.. code:: bash - - poetry update - poetry install - tox - git add -u - git commit -nm 'updated poetry lock' - -Edit the readme. If you prefer markdown over rST, replace it by a markdown -file and change the ``readme`` field under the ``tool.poetry`` section of -``pyproject.toml``. Commit the changes. - -Initialize ``pre-commit``. So far we run all commits with the ``-n`` switch -to disable hooks. If you skip this switch at your next commit, pre-commit -will come into action, install all the tools listed in -``.pre-commit-config.yaml``, and run them according to the settings. - -.. code:: bash - - pre-commit install - -.. note:: - - If you addressed errors pointed out by ``pre-commit``, run ``git add`` - again. ``pre-commit`` always runs on the staged state, if you don't - ``git add`` again, you will run it on the previously staged version of - the files. - -.. warning:: - - If you staged not all modified tracked files in your commit, ``pre-commit`` - will stash the unstaged ones. This is to run the checks on the contents - as it will be committed. In such cases do not interrupt the run of - ``pre-commit`` as then the unstaged changes remain stashed. - -Choose your code formatter --------------------------- - -In the config there are three code formatter set up but all disabled. These -are YAPF, Black and fixit. To enable one of them, remove the -``stages: [manual]`` from its hook. In this case the code formatter will run -and change your files upon each commit. If you prefer to run it only manually, -you can do it by the command below (in this example YAPF): - -.. code:: bash - - pre-commit run yapf --hook-stage manual - -Do not use two code formatters at the same time: one will do changes on your -file, the other will do different changes on the same line, and they will do -it back and forth just useless. Ultimately you will always commit the outcome -of the last code formatter. - -Set up your linter ------------------- - -In the ``tool.flake8`` section of ``pyproject.toml``, -add the codes of general or directory or file specific exceptions. In -code files for individual cases use the ``# noqa:`` tags. - -Rewrite the readme ------------------- - -Since you cloned the template repo, the ``README.rst`` has exactly the -contents that you're reading right now. Delete this whole content, add a -new main title, and add some contents about your new project, at least a -one sentence rationale. - -Docs with Sphinx ----------------- - -A Github action is set up to build and publish your documentation on Github. -Edit ``docs/src/index.rst``, the main page of your documentation. You can -decide to leave the current readme included or write a completely different -document in ``docs/src/index.rst``. - -Usage -===== - -Once you finished the setup above, you can start developing your project. -You can read more about the usage of each tool on their webpages. See below -a handful of the most important tasks: - -Do a commit without running pre-commit hooks --------------------------------------------- - -Use the ``-n`` switch: - -.. code:: bash - - git commit -nm 'commit message...' - -Run the tests -------------- - -With ``tox`` you can run the tests in an automatized way, potentially in -multiple environments. Calling ``tox`` runs everything that you set up in -``tox.ini``. - -.. code:: bash - - tox - -To run the tests directly via ``pytest``, simply do: - -.. code:: bash - - poetry run pytest -v - -Add a new dependency --------------------- - -First add the new third party dependency to the ``tool.poetry.dependencies`` -section of ``pyproject.toml``, by default with the ``"*"`` version -specification. Then let Poetry update the lock file and the virtual -environment. Finally, commit these changes. - -.. code:: bash - - poetry update - poetry install - git add -u - git commit -nm 'new dependency: some-package' - -Build the package ------------------ - -Poetry builds the package for you, by default it creates and ``sdist`` and -a ``whl``: - -.. code:: bash - - poetry build - -Poetry is also happy to publish your package on PyPI. You can get a PyPI API -token, configure Poetry to use it, and push your pacakge updates to PyPI: - -.. code:: bash - - poetry config pypi-token.pypi my-token - poetry publish - -Build the docs --------------- - -The docs are build automatically by the Github action after each push. To -build them also locally and manually: - -.. code:: bash - - poetry run make html --directory docs/ - -Why should I run everything by ``poetry run``? ----------------------------------------------- - -Poetry maintains a virtual environment for your project. By running commands -with ``poetry run ...``, you run them in this virtual environment, where all -the dependencies are installed, as defined in ``poetry.lock``, along with the -latest version of your project. It means you can run Python in the virtual -environment of your project, this way all the dependencies will be imported -from this environment, so their versions meet all the criteria defined by -you. - -.. code:: bash - - poetry run python diff --git a/cookiecutter.json b/cookiecutter.json index 0751ab9..3cf82e6 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -2,22 +2,64 @@ "project_name": "Project Name", "project_slug": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}", "package_name": "{{ cookiecutter.project_slug|lower|replace('-', '_') }}", - "short_description": "A great new project", + "short_description": "A great new project.", "readme": "{{ cookiecutter.short_description }}", "author_full_name": "Your Name", "author_email": "your@email", "github_organization": "saezlab", "project_repo": "https://github.com/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}", "license": [ - "GNU Lesser General Public License Version 3", - "Apache License Version 2.0", "MIT License", "BSD 2-Clause License", "BSD 3-Clause License", + "Apache License Version 2.0", "GNU General Public License Version 3", + "GNU Lesser General Public License Version 3", "ISC License", "Unlicense" ], + "_license": { + "MIT License": { + "license_short": "MIT", + "license_classifiers": "MIT License", + "license_spdx": "MIT" + }, + "BSD 2-Clause License": { + "license_short": "BSD-2-Clause", + "license_classifiers": "BSD License", + "license_spdx": "BSD-2-Clause" + }, + "BSD 3-Clause License": { + "license_short": "BSD-3-Clause", + "license_classifiers": "BSD License", + "license_spdx": "BSD-3-Clause" + }, + "Apache License Version 2.0": { + "license_short": "Apache-2.0", + "license_classifiers": "Apache Software License", + "license_spdx": "Apache-2.0" + }, + "GNU General Public License Version 3": { + "license_short": "GPL-3.0-or-later", + "license_classifiers": "GNU General Public License v3 (GPLv3)", + "license_spdx": "GPL-3.0-or-later" + }, + "GNU Lesser General Public License Version 3": { + "license_short": "LGPL-3.0-or-later", + "license_classifiers": "GNU Lesser General Public License v3 (LGPLv3)", + "license_spdx": "LGPL-3.0-or-later" + }, + "ISC License": { + "license_short": "ISC", + "license_classifiers": "ISC License (ISCL)", + "license_spdx": "ISC" + }, + "Unlicense": { + "license_short": "Unlicense", + "license_classifiers": "The Unlicense (Unlicense)", + "license_spdx": "Unlicense" + } + }, "python_version": [ "3.13", "3.12", @@ -31,38 +73,8 @@ "cookiecutter.extensions.TimeExtension" ], "_copy_without_render": [ - ".github/workflows/**.yaml" - ], - "_licenses_short": { - "MIT License": "MIT", - "BSD 2-Clause License": "BSD-2-Clause", - "BSD 3-Clause License": "BSD-3-Clause", - "Apache License Version 2.0": "Apache-2.0", - "GNU General Public License Version 3": "GPL-3.0-or-later", - "GNU Lesser General Public License Version 3": "LGPL-3.0-or-later", - "ISC License": "ISC", - "Unlicense": "Unlicense" - }, - "_LICENSE_CLASSIFIERS": { - "MIT License": "MIT License", - "BSD 2-Clause License": "BSD License", - "BSD 3-Clause License": "BSD License", - "Apache License Version 2.0": "Apache Software License", - "GNU General Public License Version 3": "GNU General Public License v3 or later (GPLv3+)", - "GNU Lesser General Public License Version 3": "GNU Lesser General Public License v3 or later (LGPLv3+)", - "ISC License": "ISC License (ISCL)", - "Unlicense": "The Unlicense (Unlicense)" - }, - "_LICENSE_SPDX": { - "MIT License": "MIT", - "BSD 2-Clause License": "BSD-2-Clause", - "BSD 3-Clause License": "BSD-3-Clause", - "Apache License Version 2.0": "Apache-2.0", - "GNU General Public License Version 3": "GPL-3.0-or-later", - "GNU Lesser General Public License Version 3": "LGPL-3.0-or-later", - "ISC License": "ISC", - "Unlicense": "Unlicense" - }, - "_license_classifier": "{{ cookiecutter._LICENSE_CLASSIFIERS.get(cookiecutter.license) }}", - "_license_spdx": "{{ cookiecutter._LICENSE_SPDX.get(cookiecutter.license) }}" + ".github/workflows/*.yml", + ".github/workflows/**.yaml", + ".github/actions/*" + ] } diff --git a/hooks/post_gen_project.sh b/hooks/post_gen_project.sh deleted file mode 100644 index 845e578..0000000 --- a/hooks/post_gen_project.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -# Update pre commit hooks -pre-commit autoupdate -c .pre-commit-config.yaml -pre-commit install diff --git a/hooks/pre_gen_project.py b/hooks/pre_gen_project.py deleted file mode 100644 index 2f18e36..0000000 --- a/hooks/pre_gen_project.py +++ /dev/null @@ -1,22 +0,0 @@ -LICENSE_CLASSIFIERS = { - 'MIT': 'MIT License', - 'BSD-2-Clause': 'BSD License', - 'BSD 3-Clause': 'BSD License', - 'Apache-2.0': 'Apache Software License', - 'GPL-3.0-or-later': 'GNU General Public License v3 or later (GPLv3+)', - 'LGPL-3.0-or-later': 'GNU Lesser General Public License v3 or later (LGPLv3+)', - 'ISC': 'ISC License (ISCL)', - 'Unlicense': 'The Unlicense (Unlicense)', -} - -LICENSE_SPDX = { - 'MIT License': 'MIT', - 'BSD 2-Clause License': 'BSD-2-Clause', - 'BSD 3-Clause License': 'BSD-3-Clause', - 'Apache License Version 2.0': 'Apache-2.0', - 'GNU General Public License Version 3': 'GPL-3.0-or-later', - 'ISC License': 'ISC', - 'Unlicense': 'Unlicense', -} - -license = '{{ cookiecutter.license }}' diff --git a/hooks/pre_gen_project.sh b/hooks/pre_gen_project.sh deleted file mode 100644 index 26e5cc7..0000000 --- a/hooks/pre_gen_project.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -git init -b main . diff --git a/{{cookiecutter.project_slug}}/.github/actions/setup/action.yml b/{{cookiecutter.project_slug}}/.github/actions/setup/action.yml new file mode 100644 index 0000000..d86a42f --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/actions/setup/action.yml @@ -0,0 +1,21 @@ +name: Setup Python and Install Dependencies +description: Sets up Python and installs dependencies using uv +runs: + using: composite + steps: + - uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + - name: Install uv + shell: bash + run: curl -LsSf https://astral.sh/uv/install.sh | sh + - name: Create virtualenv and install dependencies + shell: bash + run: | + uv venv .venv + source .venv/bin/activate + uv pip install ".[dev,tests,docs]" +inputs: + python-version: + required: true + description: Python version to use in the matrix. diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci-docs.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci-docs.yml new file mode 100644 index 0000000..8901506 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci-docs.yml @@ -0,0 +1,30 @@ +name: Build MkDocs documentation + +on: + push: + branches: [main, master] + +permissions: + contents: write + +jobs: + build-documentation: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Configure Git Credentials + run: | + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + - uses: ./.github/actions/setup + with: + python-version: '3.12' + - name: configure mkdocs-material cache + uses: actions/cache@v4 + with: + key: mkdocs-material-${{ github.run_id }} + path: .cache + restore-keys: | + mkdocs-material- + - name: Build documentation with mkdocs + run: .venv/bin/mkdocs gh-deploy --force diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci-linting.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci-linting.yml new file mode 100644 index 0000000..15ffdf8 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci-linting.yml @@ -0,0 +1,16 @@ +name: Linting + +on: [push, pull_request] + +jobs: + linting: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + with: + python-version: '3.13' + - name: Run Ruff (lint + formatting + import order) + run: .venv/bin/ruff check . + - name: Run Ruff format check (like Black) + run: .venv/bin/ruff format --check . diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci-security.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci-security.yml new file mode 100644 index 0000000..21f332a --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci-security.yml @@ -0,0 +1,16 @@ +name: Security Scan + +on: [push, pull_request] + +jobs: + security-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install Bandit + run: pip install bandit + - name: Run Bandit + run: bandit -r . --exclude venv,.venv,.tox --skip B101 diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci-testing-unit.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci-testing-unit.yml new file mode 100644 index 0000000..68a468e --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci-testing-unit.yml @@ -0,0 +1,36 @@ +name: CI testing [unit testing] + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + with: + python-version: '3.13' + - name: Run Ruff (lint + formatting + import order) + run: .venv/bin/ruff check . + - name: Run Ruff format check (like Black) + run: .venv/bin/ruff format --check . + + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.11', '3.12', '3.13'] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup + with: + python-version: ${{ matrix.python-version }} + - name: Run tests with coverage + run: | + source .venv/bin/activate + pytest --cov=omnigraph tests/ + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: .coverage* diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci.yaml b/{{cookiecutter.project_slug}}/.github/workflows/ci.yaml deleted file mode 100644 index e18b8b3..0000000 --- a/{{cookiecutter.project_slug}}/.github/workflows/ci.yaml +++ /dev/null @@ -1,105 +0,0 @@ -name: Test - -on: - schedule: - - cron: 0 3 * * * - push: - branches: [main] - tags: [v*] - pull_request: - branches: [main] - -jobs: - - tests: - - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash -e {0} - - strategy: - fail-fast: false - max-parallel: 4 - matrix: - os: [ubuntu-latest, macos-latest] - python: ['3.9', '3.10', '3.11', '3.12', '3.13'] - exclude: - - os: macos-latest - include: - - os: macos-latest - python: '3.13' - - env: - OS: ${{ matrix.os }} - PYTHON: ${{ matrix.python }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - enable-cache: true - python-version: ${{ matrix.python }} - - - name: Install Python {{ matrix.python }} - run: | - uv python install --python-preference only-managed ${{ matrix.python }} - - - name: Install dependencies - run: | - uv sync --all-extras - uv pip install codecov - uv tool install \ - --python-preferences only-managed \ - --python ${{ matrix.python }} \ - --with tox-uv \ - --with tox-gh \ - tox - - - name: Install pip dependencies - run: | - python -m pip install --upgrade pip - - - name: Run tests - env: - MPLBACKEND: agg - PLATFORM: ${{ matrix.os }} - DISPLAY: :42 - TOX_GH_MAJOR_MINOR: ${{ matrix.python }} - run: | - tox run -vv --skip-pkg-install - - - name: Upload coverage report to Codecov - if: success() - env: - CODECOV_NAME: ${{ matrix.python }}-${{ matrix.os }} - run: | - uv run codecovcli --verbose upload-process -t ${{ secrets.CODECOV_TOKEN }} -n '${{ env.CODECOV_NAME }}' -F unittests - - deploy: - - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - needs: tests - runs-on: ubuntu-latest - - steps: - - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - enable-cache: true - - - name: Build and deploy - run: | - uv build - # uv publish # see https://docs.pypi.org/trusted-publishers/adding-a-publisher/ diff --git a/{{cookiecutter.project_slug}}/.github/workflows/sphinx_autodoc.yaml b/{{cookiecutter.project_slug}}/.github/workflows/sphinx_autodoc.yaml deleted file mode 100644 index 770a6c6..0000000 --- a/{{cookiecutter.project_slug}}/.github/workflows/sphinx_autodoc.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: Sphinx build docs on push -on: -- push - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Check out main - uses: actions/checkout@main - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: 3.10.5 - - name: Load cached Poetry installation - uses: actions/cache@v2 - with: - path: ~/.local - key: poetry-0 - - name: Install Poetry - uses: snok/install-poetry@v1 - with: - virtualenvs-create: true - virtualenvs-in-project: true - installer-parallel: true - - name: Poetry disable modern-installation - run: poetry config installer.modern-installation false - - name: Load cached venv - id: cached-poetry-dependencies - uses: actions/cache@v2 - with: - path: .venv - key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} - - name: Install dependencies - if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: poetry install --no-interaction --no-root - - name: Install library - run: poetry install --no-interaction - - name: Build documentation - run: poetry run make html --directory docs/ - - name: Commit files - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - touch docs/_build/html/.nojekyll - git add -f docs/_build/ - git commit -m "Update autodoc" -a - # using https://github.com/marketplace/actions/push-git-subdirectory-as-branch - - name: Deploy - uses: s0/git-publish-subdir-action@develop - env: - REPO: self - BRANCH: gh-pages - FOLDER: docs/_build/html - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index 27c42c8..35de627 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -1,61 +1,97 @@ -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks +# pre-commit-config.yaml +# See https://pre-commit.com for docs and https://pre-commit.com/hooks.html for available hooks + +# ====================================================== +# ======= pre-commit configuration ======== +# ====================================================== fail_fast: false +minimum_pre_commit_version: 3.0.0 default_language_version: python: python3 default_stages: - pre-commit - pre-push -minimum_pre_commit_version: 3.0.0 + +# ====================================================== +# ======= repository hooks ======== +# ====================================================== +# UPDATE all the hooks regularly by running in the +# terminal: +# pre-commit autoupdate + repos: + # Fast Python linter and formatter with auto-fix support - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 # Check for latest version + rev: v0.11.13 hooks: - id: ruff args: [--fix, --show-fixes] + stages: [pre-commit] + files: \.py$ - id: ruff-format + stages: [pre-commit] + files: \.py$ + + # Go code cleaner (removes unused exports) - repo: https://github.com/deeenes/unexport - rev: 0.4.0-patch0-8 + rev: 0.4.0-patch0-3 hooks: - id: unexport args: [--refactor, --single_quotes] exclude: __init__.py$ + + # Official pre-commit-hooks for general checks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v5.0.0 # Check for updates regularly hooks: - - id: detect-private-key - - id: check-ast - - id: end-of-file-fixer - id: check-added-large-files - - id: mixed-line-ending - args: [--fix=lf] - exclude: ^docs/make.bat$ - - id: check-merge-conflict + stages: [pre-commit, pre-push] + - id: check-ast + stages: [pre-commit] - id: check-case-conflict + stages: [pre-commit] + - id: check-merge-conflict + stages: [pre-commit, pre-push] - id: check-symlinks + stages: [pre-commit] - id: check-yaml args: [--unsafe] - - id: check-ast + stages: [pre-commit] + files: \.ya?ml$ + - id: detect-private-key + stages: [pre-commit] + - id: end-of-file-fixer + stages: [pre-commit] + - id: mixed-line-ending + args: [--fix=lf] + exclude: ^docs/make.bat$ + stages: [pre-commit] - id: requirements-txt-fixer -- repo: https://github.com/rstcheck/rstcheck - rev: v6.2.4 - hooks: - - id: rstcheck - exclude: docs + stages: [pre-commit] + + # Format code blocks in documentation files - repo: https://github.com/asottile/blacken-docs rev: 1.19.1 hooks: - id: blacken-docs + stages: [pre-commit] + files: \.(md|rst)$ + + # Format TOML and YAML files with pretty-format hooks - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks rev: v2.14.0 hooks: - - id: pretty-format-yaml - args: [--autofix, --indent, '4'] - id: pretty-format-toml - args: [--autofix, --indent, '4'] -- repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.10.0 - hooks: - - id: rst-backticks - - id: rst-directive-colons - - id: rst-inline-touching-normal + args: + - --autofix + - --indent + - '4' + stages: [pre-commit] + files: \.toml$ + - id: pretty-format-yaml + args: + - --autofix + - --indent + - '4' + stages: [pre-commit] + files: \.ya?ml$ diff --git a/{{cookiecutter.project_slug}}/.readthedocs.yaml b/{{cookiecutter.project_slug}}/.readthedocs.yaml deleted file mode 100644 index 56363c4..0000000 --- a/{{cookiecutter.project_slug}}/.readthedocs.yaml +++ /dev/null @@ -1,22 +0,0 @@ -version: 2 - -sphinx: - builder: html - configuration: docs/source/conf.py - fail_on_warning: true - -formats: -- htmlzip -- pdf - -build: - os: ubuntu-22.04 - tools: - python: 3.10 - -python: - install: - - method: pip - path: . - extra_requirements: - - docs diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index 8a7c282..fcea5b4 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -1,11 +1,60 @@ +![project-banner](./docs/assets/project-banner-readme.png) + # {{ cookiecutter.project_name }} -[![Tests][badge-tests]][link-tests] -[![Documentation][badge-docs]][link-docs] +- [ ] TODO: Add badges to your project. + +[![Tests](https://img.shields.io/github/actions/workflow/status/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}/test.yml?branch=master)](https://github.com/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}/actions/workflows/test.yml) +[![Docs](https://img.shields.io/badge/docs-MkDocs-blue)](https://saezlab.github.io/{{ cookiecutter.project_slug }}/) +![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit) +![PyPI](https://img.shields.io/pypi/v/{{ cookiecutter.project_slug }}) +![Python](https://img.shields.io/pypi/pyversions/{{ cookiecutter.project_slug }}) +![License](https://img.shields.io/github/license/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}) +![Issues](https://img.shields.io/github/issues/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}) +![Last Commit](https://img.shields.io/github/last-commit/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}) + +## Description + +{{ cookiecutter.short_description }} + +## Installation + +- [ ] TODO: Add installation instructions for your project, if applicable. + +```bash +# Example +pip install +``` + +## Usage + +- [ ] TODO: Add usage instructions for your project. + +```python +import foobar + +# returns 'words' +foobar.pluralize('word') + +# returns 'geese' +foobar.pluralize('goose') + +# returns 'phenomenon' +foobar.singularize('phenomena') +``` + +## Contributing + +Pull requests are welcome. For major changes, please open an issue first +to discuss what you would like to change. + +Please make sure to update tests as appropriate. + +- [ ] TODO: add contribution guidelines. All of them can be modified in the mkdocs documentation (./docs/community) + +## License -[badge-tests]: https://img.shields.io/github/actions/workflow/status/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}/test.yaml?branch=main -[link-tests]: {{ cookiecutter.project_repo }}/actions/workflows/test.yml -[badge-docs]: https://img.shields.io/readthedocs/{{ cookiecutter.project_slug }} -[link-docs]: https://{{ cookiecutter.project_slug }}.readthedocs.io +[MIT](https://choosealicense.com/licenses/mit/) -{{ cookiecutter.readme }} +- [ ] TODO: Modify this based on the license you choose. +- [ ] TODO: Modify the LICENSE file based on the license you choose. diff --git a/{{cookiecutter.project_slug}}/docs/Makefile b/{{cookiecutter.project_slug}}/docs/Makefile deleted file mode 100644 index c7c0696..0000000 --- a/{{cookiecutter.project_slug}}/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = src -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/{{cookiecutter.project_slug}}/docs/about.md b/{{cookiecutter.project_slug}}/docs/about.md new file mode 100644 index 0000000..22e6574 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/about.md @@ -0,0 +1,3 @@ +# About + +This project was created by {{ cookiecutter.author_full_name }}. diff --git a/{{cookiecutter.project_slug}}/docs/assets/project-banner-readme.png b/{{cookiecutter.project_slug}}/docs/assets/project-banner-readme.png new file mode 100644 index 0000000..9f77947 Binary files /dev/null and b/{{cookiecutter.project_slug}}/docs/assets/project-banner-readme.png differ diff --git a/{{cookiecutter.project_slug}}/docs/community/contribute-codebase.md b/{{cookiecutter.project_slug}}/docs/community/contribute-codebase.md new file mode 100644 index 0000000..75ede12 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/community/contribute-codebase.md @@ -0,0 +1,333 @@ +# Developer Guide + +Thank you for considering to contribute to the project! This guide will help you +to get started with the development of the project. If you have any questions, +please feel free to ask them in the issue tracker on GitHub. + +## Small Contributions + +If you want to contribute a small change (e.g., a bugfix), you can probably +immediately go ahead and create a [pull request](#submitting-a-pull-request). +For more substantial changes or additions, please read on. + +## Larger Contributions + +If you want to contribute a larger change, please create an +[issue](./contribute.md#categories) first. This will allow us to discuss the +change and make sure that it fits into the project. It can happen that +development for a feature is already in progress, so it is important to check +first to avoid duplicate work. If you have any questions, feel free to approach +us in any way you like. + +## Dependency management + +We use [Poetry](https://python-poetry.org) for dependency management. Please +make sure that you have installed Poetry and set up the environment correctly +before starting development. + +### Setting up the environment + +- Install dependencies from the lock file: `poetry install` + +- Use the environment: You can either run commands directly with `poetry run +` or open a shell with `poetry shell` and then run commands directly. + +### Updating the environment + +If you want to fix dependency issues, please do so in the Poetry +framework. If Poetry does not work for you for some reason, please let us know. + +The Poetry dependencies are organized in groups. There are groups with +dependencies needed for running {{ cookiecutter.project_slug}} (`[tool.poetry.dependencies]` with the +group name `main`) and a group with dependencies needed for development +(`[tool.poetry.group.dev.dependencies]` with the group name `dev`). + +For adding new dependencies: + +- Add new dependencies via `poetry add`: +`poetry add --group `. This will update the `pyproject.toml` +and lock file automatically. + +- Add new dependencies via `pyproject.toml`: Add the dependency to the +`pyproject.toml` file in the correct group, including version. Then update the +lock file: `poetry lock` and install the dependencies: `poetry install`. + +## Code quality and formal requirements + +For ensuring code quality, the following tools are used: + +- [isort](https://isort.readthedocs.io/en/latest/) for sorting imports + +- [black](https://black.readthedocs.io/en/stable/) for automated code formatting + +- [pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks) for +ensuring some general rules + +- [pep585-upgrade](https://github.com/snok/pep585-upgrade) for automatically +upgrading type hints to the new native types defined in PEP 585 + +- [pygrep-hooks](https://github.com/pre-commit/pygrep-hooks) for ensuring some +general naming rules + +- [Ruff](https://docs.astral.sh/ruff/) An extremely fast Python linter +and code formatter, written in Rust + +We recommend configuring your IDE to execute Ruff on save/type, which will +automatically keep your code clean and fix some linting errors as you type. This +is made possible by the fast execution of Ruff and removes the need to run a +dedicated pre-commit step. For instance, in VSCode or Cursor, you can add this +to your `.vscode/settings.json`: + +```json +{ + "editor.formatOnType": true, + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff" +} +``` + +Alternatively, pre-commit hooks can be used to automatically or manually run +these tools before each commit. They are defined in `.pre-commit-config.yaml`. +To install the hooks run `poetry run pre-commit install`. The hooks are then +executed before each commit. For running the hook for all project files (not +only the changed ones) run `poetry run pre-commit run --all-files`. Our CI runs +the pre-commit hooks, so running them locally is a good way to check if your +code conforms to the formatting rules. + +## Testing + +The project uses [pytest](https://docs.pytest.org/en/stable/) for testing. To +run the tests, please run `pytest` in the root directory of the project. We are +developing {{ cookiecutter.project_slug}} using test-driven development. Please make sure that you +add tests for your code before submitting a pull request. + +The existing tests can also help you to understand how the code works. If you +have any questions, please feel free to ask them in the issue tracker or on +Zulip. + +**Before submitting a pull request, please make sure that all tests pass and +that the documentation builds correctly.** + +## Versioning + +We use [semantic versioning](https://semver.org/) for the project. This means +that the version number is incremented according to the following scheme: + +- Increment the major version number if you make incompatible API changes. + +- Increment the minor version number if you add functionality in a backwards- + compatible manner. Since we are still in the 0.x.y version range, most of the + significant changes will increase the minor version number. + +- Increment the patch version number if you make backwards-compatible bug fixes. + +We use the `bumpversion` tool to update the version number in the +`pyproject.toml` file. This will create a new git tag automatically. Usually, +versioning is done by the maintainers, so please do not increment versions in +pull requests by default. + +## Finding an issue to contribute to + +If you are brand new to {{ cookiecutter.project_slug}} or open-source development, we recommend +searching the GitHub "Issues" tab to find issues that interest you. Unassigned +issues labeled `Docs` and `good first` are typically good for newer contributors. + +Once you've found an interesting issue, it's a good idea to assign the issue to +yourself, so nobody else duplicates the work on it. + +If for whatever reason you are not able to continue working with the issue, +please unassign it, so other people know it's available again. If you want to +work on an issue that is currently assigned but you're unsure whether work is +actually being done, feel free to kindly ask the current assignee if you can +take over (please allow at least a week of inactivity before getting in touch). + + +## Submitting a Pull Request + +### Tips for a successful pull request + +To improve the chances of your pull request being reviewed, you should: + +- **Reference an open issue** for non-trivial changes to clarify the PR's purpose. +- **Ensure you have appropriate tests**. Tests should be the focus of any PR (apart from documentation changes). +- **Keep your pull requests as simple as possible**. Larger PRs take longer to review. +- **Ensure that CI is in a green state**. Reviewers may tell you to fix the CI before looking at anything else. + +### Version control, Git, and GitHub + +{{ cookiecutter.project_slug}} is hosted on GitHub, and to contribute, you will need to sign up for a +[free GitHub account](https://github.com/signup/free). We use +[Git](https://git-scm.com/) for version control to allow many people to work +together on the project. + +If you are new to Git, you can reference some of these resources for learning +Git. Feel free to reach out to the contributor community for help if needed: + +- [Git documentation](https://git-scm.com/doc). + + +The project follows a forking workflow further described on this page whereby +contributors fork the repository, make changes and then create a Pull Request. +So please be sure to read and follow all the instructions in this guide. + +If you are new to contributing to projects through forking on GitHub, take a +look at the [GitHub documentation for contributing to +projects](https://docs.github.com/en/get-started/quickstart/contributing-to-projects). +GitHub provides a quick tutorial using a test repository that may help you +become more familiar with forking a repository, cloning a fork, creating a +feature branch, pushing changes and making Pull Requests. + +Below are some useful resources for learning more about forking and Pull +Requests on GitHub: + +- the [GitHub documentation for forking a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo). + +- the [GitHub documentation for collaborating with Pull Requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests). + +- the [GitHub documentation for working with forks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks). + +There are also many unwritten rules and conventions that are helpful in +interacting with other open-source contributors. These +[lessons](https://www.pyopensci.org/lessons/) from PyOpenSci are a good resource +for learning more about how to interact with other open-source contributors in +scientific computing. + +### Getting started with Git + +[GitHub has +instructions](https://docs.github.com/en/get-started/quickstart/set-up-git) for +installing git, setting up your SSH key, and configuring git. All these steps +need to be completed before you can work seamlessly between your local +repository and GitHub. + +### Create a fork of {{ cookiecutter.project_slug }} + +You will need your own fork of {{ cookiecutter.project_slug}}in order to eventually open a Pull +Request. Go to the {{ cookiecutter.project_slug}} project page and hit the Fork button. Please +uncheck the box to copy only the main branch before selecting Create Fork. You +will then want to clone your fork to your machine. + +```bash +git clone https://github.com/your-user-name/{{ cookiecutter.project_slug }}.git +cd {{ cookiecutter.project_slug }} +git remote add upstream https://github.com/{{ cookiecutter.project_slug }}/{{ cookiecutter.project_slug }}.git +git fetch upstream +``` + +This creates the directory `{{ cookiecutter.project_slug }}` and connects your repository to the +upstream (main project) *{{ cookiecutter.project_slug }}* repository. They have the same name, but +your local repository and fork are separate from the upstream repository. + +### Creating a feature branch + +Your local `main` branch should always reflect the current state of {{ cookiecutter.project_slug}} +repository. First ensure it's up-to-date with the main {{ cookiecutter.project_slug}} repository. + +```bash +git checkout main +git pull upstream main --ff-only +``` + +Then, create a feature branch for making your changes. For example, we are going +to create a branch called `my-new-feature-for-{{ cookiecutter.project_slug }}` + +```bash +git checkout -b my-new-feature-for-{{ cookiecutter.project_slug }} +``` + +This changes your working branch from `main` to the +`my-new-feature-for-{{ cookiecutter.project_slug }}` branch. Keep any changes in this branch specific +to one bug or feature so it is clear what the branch brings to *{{ cookiecutter.project_slug}}*. You +can have many feature branches and switch between them using the `git +checkout` command. + +### Making code changes + +Before modifying any code, ensure you follow the contributing environment +guidelines to set up an appropriate development environment. + +When making changes, follow these {{ cookiecutter.project_slug}}-specific guidelines: + +1. Keep changes of that branch/PR focused on a single feature or bug fix. + +2. Follow roughly the [conventional commit message conventions](https://www.conventionalcommits.org/en/v1.0.0/). + +### Pushing your changes + +When you want your [committed](https://git-scm.com/docs/git-commit) changes to +appear publicly on your GitHub page, you can +[push](https://git-scm.com/docs/git-push) your forked feature branch's commits +to your forked repository on GitHub. + +Now your code is on GitHub, but it is not yet a part of the {{ cookiecutter.project_slug}} project. +For that to happen, a Pull Request (PR) needs to be submitted. + +### Opening a Pull Request (PR) + +If everything looks good according to the general guidelines, you are ready to +make a Pull Request. A Pull Request is how code from your fork becomes available +to the project maintainers to review and merge into the project to appear in the +next release. To submit a Pull Request: + +1. Navigate to your repository on GitHub. + +1. Click on the Compare & Pull Request button. + +1. You can then click on Commits and Files Changed to make sure everything looks okay one last time. + +1. Write a descriptive title that includes prefixes. {{ cookiecutter.project_slug}} uses a convention for title prefixes, most commonly, `feat:` for features, `fix:` for bug fixes, and `refactor:` for refactoring. + +1. Write a description of your changes in the `Preview Discussion` tab. This description will inform the reviewers about the changes you made, so please include all relevant information, including the motivation, implementation details, and references to any issues that you are addressing. + +1. Make sure to `Allow edits from maintainers`; this allows the maintainers to make changes to your PR directly, which is useful if you are not sure how to fix the PR. + +1. Click `Send Pull Request`. + +1. Optionally, you can assign reviewers to your PR, if you know who should review it. + +This request then goes to the repository maintainers, and they will review the code. + +### Updating your Pull Request + +Based on the review you get on your pull request, you will probably need to make +some changes to the code. You can follow the steps above again to address any +feedback and update your pull request. + +### Parallel changes in the upstream `main` branch + +In case of simultaneous changes to the upstream code, it is important that +these changes are reflected in your pull request. To update your feature +branch with changes in the {{ cookiecutter.project_slug}} `main` branch, run: + +```shell + + git checkout my-new-feature-for-{{ cookiecutter.project_slug }} + git fetch upstream + git merge upstream/main +``` + +If there are no conflicts (or they could be fixed automatically), a file with a +default commit message will open, and you can simply save and quit this file. + +If there are merge conflicts, you need to resolve those conflicts. See +[here](https://help.github.com/articles/resolving-a-merge-conflict-using-the-command-line/) +for an explanation on how to do this. + +Once the conflicts are resolved, run: + +1. `git add -u` to stage any files you've updated; +2. `git commit` to finish the merge. + +After the feature branch has been updated locally, you can now update your pull +request by pushing to the branch on GitHub: + +```shell + git push origin my-new-feature-for-{{ cookiecutter.project_slug }} +``` + +Any `git push` will automatically update your pull request with your branch's changes +and restart the `Continuous Integration` checks. diff --git a/{{cookiecutter.project_slug}}/docs/community/contribute-docs.md b/{{cookiecutter.project_slug}}/docs/community/contribute-docs.md new file mode 100644 index 0000000..6e48410 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/community/contribute-docs.md @@ -0,0 +1,34 @@ +# Contributing to the documentation + +Contributing to the documentation benefits everyone who uses {{ cookiecutter.project_slug }}. We +encourage you to help us improve the documentation, and you don't have to be an +expert on {{ cookiecutter.project_slug }} to do so! In fact, there are sections of the docs that are +worse off after being written by experts. If something in the docs doesn't make +sense to you, updating the relevant section after you figure it out is a great +way to ensure it will help the next person. + + +## How to contribute to the documentation + +The documentation is written in **Markdown**, which is almost like writing in +plain English, and built using [Material for +MkDocs](https://squidfunk.github.io/mkdocs-material/). The simplest way to +contribute to the docs is to click on the `Edit` button (pen and paper) at the +top right of any page. This will take you to the source file on GitHub, where +you can make your changes and create a pull request using GitHub's web +interface (the `Commit changes...` button). + +Some other important things to know about the docs: + +- The {{ cookiecutter.project_slug }} documentation consists of two parts: the docstrings in the code + itself and the docs in the `docs/` folder. The docstrings provide a clear + explanation of the usage of the individual functions, while the documentation + website you are looking at is built from the `docs/` folder. + +- The docstrings follow a convention, based on the [Google Docstring + Standard](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings). + +- Our API documentation files in `docs/reference/source` contain the + instructions for the auto-generated documentation from the docstrings. For + classes, there are a few subtleties around controlling which methods and + attributes have pages auto-generated. diff --git a/{{cookiecutter.project_slug}}/docs/community/contribute.md b/{{cookiecutter.project_slug}}/docs/community/contribute.md new file mode 100644 index 0000000..dc2b9a6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/community/contribute.md @@ -0,0 +1,51 @@ +# How to Start Contributing + +There are many valuable ways to contribute besides writing code. Thank you for +dedicating your time to improve our project! + +## :octicons-issue-opened-24:{ .lg .middle } Bug reports and enhancement requests + +Bug reports and enhancement requests are an important part of making any +software more stable. We curate them though Github issues. When opening an +issue or request, please select the appropriate category and fill out the issue +form fully to ensure others and the core development team can fully understand +the scope of the issue. If your category is not listed, you can create a blank +issue. + +The issue will then show up to the {{ cookiecutter.project_slug}} community and be open to +comments/ideas from others. + +### Categories + +- [Bug Report](https://www.google.com): Report incorrect behavior in the {{ cookiecutter.project_slug}} library +- [Register New Component](https://www.google.com): Register a new component in the {{ cookiecutter.project_slug}} ecosystem, either one you have created, or one that you would like to see added +- Documentation Improvement: Report wrong or missing documentation +- Feature Request: Suggest an idea for {{ cookiecutter.project_slug}} + +## :octicons-checklist-24:{ .lg .middle } Detailed Guides + +
+ +- :octicons-book-24:{ .lg .middle } __Contributing to the Documentation__ + + --- + + A simple way to get started is to contribute to the documentation. Please + follow the guide [here](./contribute-docs.md) to learn how to do so. + + [:octicons-arrow-right-24: To the contribution guide](./contribute-docs.md) + +
+ +
+ +- :octicons-code-24:{ .lg .middle } __Contributing to the Code Base__ + + --- + + The best way to contribute code is to open a pull request on Github. Please + follow the guide [here](./contribute-codebase.md) to learn how to do so. + + [:octicons-arrow-right-24: To the contribution guide](./contribute-codebase.md) + +
diff --git a/{{cookiecutter.project_slug}}/docs/community/index.md b/{{cookiecutter.project_slug}}/docs/community/index.md new file mode 100644 index 0000000..9598a6f --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/community/index.md @@ -0,0 +1,25 @@ +Welcome to the {{ cookiecutter.project_slug}} community! We follow open-source principles and +encourage any sort of contribution. We communicate on GitHub, where we also +organise our projects. + +
+ +- :octicons-book-24:{ .lg .middle } __Where to Start__ + + --- + + If you'd like to learn how to contribute to our projects, please follow + the steps outlined in the contribution guide. + + [:octicons-arrow-right-24: To the contribution guide](contribute.md) + +
+ + +## Contributing Guidelines GitHub Links + +- [Contribution guidelines](https://github.com/{{ cookiecutter.project_slug}}/{{ cookiecutter.project_slug}}/blob/main/CONTRIBUTING.md) + +- [Code of Conduct](https://github.com/{{ cookiecutter.project_slug}}/{{ cookiecutter.project_slug}}/blob/main/CODE_OF_CONDUCT.md) + +- [Developer Guide](https://github.com/{{ cookiecutter.project_slug}}/{{ cookiecutter.project_slug}}/blob/main/DEVELOPER.md) diff --git a/{{cookiecutter.project_slug}}/docs/index.md b/{{cookiecutter.project_slug}}/docs/index.md new file mode 100644 index 0000000..ee53440 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/index.md @@ -0,0 +1,3 @@ +# Welcome to {{ cookiecutter.project_name }} + +This is the main documentation page. diff --git a/{{cookiecutter.project_slug}}/docs/installation.md b/{{cookiecutter.project_slug}}/docs/installation.md new file mode 100644 index 0000000..c328c6c --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/installation.md @@ -0,0 +1,37 @@ +# Installation guide + +We strongly recommend installing a few prerequisites to ensure a smooth experience. These prerequisites are: + +1. *Python 3* (version >= 3.10) + - [Install Python 3](https://docs.python.org/3/using/index.html) +2. *Poetry* (Python packaging and dependency manager) + - [Install Poetry](https://python-poetry.org/docs/#installation) +3. *git* (version control manager) + - [Install git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +4. *Docker* (containerization technology) [optional] + - [Install Docker](https://docs.docker.com/engine/) + +!!! tip "Tip" + If you are missing any of those pre-requisites, **please follow the installation guide in each resource before you continue**. + + +## Checking prerequisites + +You can verify access to these components in your terminal: + +1. `Python` version 3.10 or higher. + ```bash + python --version + ``` +2. `Poetry` + ```bash + poetry --version + ``` +3. `git` + ```bash + git --version + ``` +4. `Docker` + ```bash + docker --version + ``` diff --git a/{{cookiecutter.project_slug}}/docs/learn/explanation/index.md b/{{cookiecutter.project_slug}}/docs/learn/explanation/index.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/docs/learn/guides/index.md b/{{cookiecutter.project_slug}}/docs/learn/guides/index.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/docs/learn/tutorials/quickstart.md b/{{cookiecutter.project_slug}}/docs/learn/tutorials/quickstart.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/docs/learn/tutorials/tutorial0001_basics.md b/{{cookiecutter.project_slug}}/docs/learn/tutorials/tutorial0001_basics.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/docs/make.bat b/{{cookiecutter.project_slug}}/docs/make.bat deleted file mode 100644 index 5b28825..0000000 --- a/{{cookiecutter.project_slug}}/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=src -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/{{cookiecutter.project_slug}}/docs/reference/source/{{ cookiecutter.package_name }}/_metadata-docs.md b/{{cookiecutter.project_slug}}/docs/reference/source/{{ cookiecutter.package_name }}/_metadata-docs.md new file mode 100644 index 0000000..4ad1f87 --- /dev/null +++ b/{{cookiecutter.project_slug}}/docs/reference/source/{{ cookiecutter.package_name }}/_metadata-docs.md @@ -0,0 +1,12 @@ +## Description + +The `_metadata.py` file defines the structures and logic for handling metadata associated with cached items in the `{{ cookiecutter.project_slug}}` package. It provides classes and helper functions to manage, store, and retrieve metadata fields, ensuring that each cache entry can be enriched with flexible, structured, and queryable information. This enables advanced search, filtering, and organization of cached data based on user-defined or system-generated metadata. + +### Main Components + +- **Metadata Function:** + Encapsulates the metadata for a cache item, providing methods to set, get, update, and validate metadata fields. + +--- + +::: {{ cookiecutter.package_name }}._metadata diff --git a/{{cookiecutter.project_slug}}/docs/src/conf.py b/{{cookiecutter.project_slug}}/docs/src/conf.py deleted file mode 100644 index e1493db..0000000 --- a/{{cookiecutter.project_slug}}/docs/src/conf.py +++ /dev/null @@ -1,125 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -from datetime import datetime -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -import sys -import pathlib - -here = pathlib.Path(__file__).parent -sys.path.insert(0, str(here.parent)) - -import {{ cookiecutter.package_name }} # noqa: E402 - -# -- Project information ----------------------------------------------------- - -project = '{{ cookiecutter.package_name }}' -version = {{ cookiecutter.package_name }}.__version__ -author = ', '.join({{ cookiecutter.package_name }}.__author__) -years = '-'.join(sorted({'2022', f'{datetime.now():%Y}'})) -copyright = f'{years}, Saez Lab' -repository_url = 'https://github.com/{{ cookiecutter.github_organization }}/{{ cookiecutter.project_slug }}' - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named "sphinx.ext.*") or your custom -# ones. -extensions = [ - 'myst_parser', - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.autosummary', - 'sphinx.ext.napoleon', - 'sphinx.ext.todo', # not for output but to remove warnings - 'sphinx.ext.githubpages', - 'sphinx.ext.viewcode', - 'sphinx.ext.ifconfig', - 'sphinxcontrib.bibtex', - 'sphinx_autodoc_typehints', - 'sphinx.ext.mathjax', - 'sphinx_copybutton', - 'sphinx_last_updated_by_git', - 'sphinxcontrib.fulltoc', - 'sphinx_remove_toctrees', - 'nbsphinx', - 'IPython.sphinxext.ipython_console_highlighting', -] - -autosummary_generate = True -autodoc_member_order = 'groupwise' -default_role = 'literal' -napoleon_google_docstring = False -napoleon_numpy_docstring = True -napoleon_include_init_with_doc = False -napoleon_use_rtype = True # having a separate entry generally helps readability -napoleon_use_param = True - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = 'en' - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -source_suffix = ['.rst', '.md'] - -# The master toctree document. -master_doc = 'contents' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints'] - - -# -- Autodoc configuration --------------------------------------------------- - -autodoc_mock_imports = [] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'manni' - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - -html_theme = 'pydata_sphinx_theme' -html_theme_options = { - 'navigation_depth': 2, - 'collapse_navigation': True, -} -html_context = { - 'display_github': True, # Integrate GitHub - 'github_user': '{{cookiecutter.github_organization}}', # Username - 'github_repo': project, # Repo name - 'github_version': 'main', # Version - 'conf_py_path': '/docs/', # Path in the checkout to the docs root -} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -nitpick_ignore = [ - # If building the documentation fails because - # of a missing link that is outside your control, - # you can add an exception to this list. - # ("py:class", "igraph.Graph"), -] diff --git a/{{cookiecutter.project_slug}}/docs/src/developer_docs.md b/{{cookiecutter.project_slug}}/docs/src/developer_docs.md deleted file mode 100644 index 3017a37..0000000 --- a/{{cookiecutter.project_slug}}/docs/src/developer_docs.md +++ /dev/null @@ -1,58 +0,0 @@ -## Pre-commit documentation - -[Pre-commit](https://pre-commit.com/) checks are fast programs that -check code for errors, inconsistencies and code styles, before the code -is committed. This is a brief documentation of pre-commits checks -pre-sets in the scverse-template. - -The following pre-commit checks for code style and format. - -- [black](https://black.readthedocs.io/en/stable/): standard code - formatter in Python. -- [autopep8](https://github.com/hhatto/autopep8): code formatter to - conform to [PEP8](https://peps.python.org/pep-0008/) style guide. -- [isort](https://pycqa.github.io/isort/): sort module imports into - sections and types. -- [prettier](https://prettier.io/docs/en/index.html): standard code - formatter for non-Python files (e.g. YAML). -- [blacken-docs](https://github.com/asottile/blacken-docs): black on - python code in docs. - -The following pre-commit checks for errors, inconsistencies and typing. - -- [flake8](https://flake8.pycqa.org/en/latest/): standard check for errors in Python files. - - [flake8-tidy-imports](https://github.com/adamchainz/flake8-tidy-imports): - tidy module imports. - - [flake8-docstrings](https://github.com/PyCQA/flake8-docstrings): - pydocstyle extension of flake8. - - [flake8-rst-docstrings](https://github.com/peterjc/e8-rst-docstrings): - extension of `flake8-docstrings` for `rst` docs. - - [flake8-comprehensions](https://github.com/adamchainz/e8-comprehensions): - write better list/set/dict comprehensions. - - [flake8-bugbear](https://github.com/PyCQA/flake8-bugbear): - find possible bugs and design issues in program. - - [flake8-blind-except](https://github.com/elijahandrews/flake8-blind-except): - checks for blind, catch-all `except` statements. -- [yesqa](https://github.com/asottile/yesqa): - remove unneccesary `# noqa` comments, follows additional dependencies listed above. - Not included in this template. -- [autoflake](https://github.com/PyCQA/autoflake): - remove unused imports and variables. Not included in this template. -- [pre-commit-hooks](https://github.com/pre-commit/pre-commit-hooks): generic pre-commit hooks. - - **detect-private-key**: checks for the existence of private keys. - - **check-ast**: check whether files parse as valid python. - - **end-of-file-fixer**:check files end in a newline and only a newline. - - **mixed-line-ending**: checks mixed line ending. - - **trailing-whitespace**: trims trailing whitespace. - - **check-case-conflict**: check files that would conflict with case-insensitive file systems. -- [pyupgrade](https://github.com/asottile/pyupgrade): - upgrade syntax for newer versions of the language. - -### Notes on pre-commit checks - -- **flake8**: to ignore errors, you can add a comment `# noqa` to the offending line. - You can also specify the error id to ignore with e.g. `# noqa: E731`. - Check [flake8 guide](https://flake8.pycqa.org/en/3.1.1/user/ignoring-errors.html) for reference. -- You can add or remove pre-commit checks by simply deleting relevant lines in the `.pre-commit-config.yaml` file. - Some pre-commit checks have additional options that can be specified either in the `pyproject.toml` or pre-commit - specific config files, such as `.prettierrc.yml` for **prettier** and `.flake8` for **flake8**. diff --git a/{{cookiecutter.project_slug}}/docs/src/index.rst b/{{cookiecutter.project_slug}}/docs/src/index.rst deleted file mode 100644 index a3d1c9a..0000000 --- a/{{cookiecutter.project_slug}}/docs/src/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -############ -Introduction -############ - -Created from a project template. Please write the docs of your project here, -and remove the parts below (or edit ``README.rst`` in the project root). - -.. include:: ../../README.rst - -######### -Reference -######### - -{{ cookiecutter.project_name }} -=============================== - -.. automodule:: {{ cookiecutter.package_name }} - :members: - -########################### -Indices, Tables, and Search -########################### - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - - -.. toctree:: - :maxdepth: 4 diff --git a/{{cookiecutter.project_slug}}/docs/{{ cookiecutter.project_slug}}-project/design-philosophy.md b/{{cookiecutter.project_slug}}/docs/{{ cookiecutter.project_slug}}-project/design-philosophy.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/docs/{{ cookiecutter.project_slug}}-project/project.md b/{{cookiecutter.project_slug}}/docs/{{ cookiecutter.project_slug}}-project/project.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/docs/{{ cookiecutter.project_slug}}-project/use-cases.md b/{{cookiecutter.project_slug}}/docs/{{ cookiecutter.project_slug}}-project/use-cases.md new file mode 100644 index 0000000..e69de29 diff --git a/{{cookiecutter.project_slug}}/mkdocs.yml b/{{cookiecutter.project_slug}}/mkdocs.yml new file mode 100644 index 0000000..e8f77be --- /dev/null +++ b/{{cookiecutter.project_slug}}/mkdocs.yml @@ -0,0 +1,107 @@ +site_name: '{{ cookiecutter.package_name }}' +site_url: https://saezlab.github.io/{{ cookiecutter.package_name }} +theme: + name: material + font: + text: Lato + code: Roboto Mono + features: + - content.code.copy + - content.action.edit + - navigation.tabs + - navigation.instant + - navigation.footer + palette: + - scheme: default + primary: teal + accent: light blue + toggle: + icon: material/toggle-switch + name: Switch to dark mode + - scheme: slate + primary: teal + accent: light blue + toggle: + icon: material/toggle-switch-off + name: Switch to light mode + +#======= Website Navigation settings ===== +nav: +- Home: index.md + +- About: + - Project: '{{ cookiecutter.project_slug }}-project/project.md' + - Design philosophy: '{{ cookiecutter.project_slug }}-project/design-philosophy.md' + - Use Cases: '{{ cookiecutter.project_slug }}-project/use-cases.md' + - About: about.md + +- Get Started: + - Installation: installation.md + - Quickstart: learn/tutorials/quickstart.md + +- Learn: + - Tutorials: + - Basics: learn/tutorials/tutorial0001_basics.md + - HowTo / FAQ: + - learn/guides/index.md + +- Explanations: + - learn/explanation/index.md + +- Reference: + - API Documentation: + - '{{ cookiecutter.package_name }}': + - _metadata: reference/source/{{ cookiecutter.package_name}}/_metadata-docs.md +- Community: + - Join Us: community/index.md + - Where to Start: community/contribute.md + - Contribute to the Documentation: community/contribute-docs.md + - Contribute to the Code Base: community/contribute-codebase.md + + + +#======= Extension settings (sorted alphabetically) ===== +markdown_extensions: +- admonition +- attr_list +- md_in_html + #----- Python Markdown Extensions +- pymdownx.details +- pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg +- pymdownx.highlight: # Note: list of Pygments: https://pygments.org/docs/lexers/ + anchor_linenums: true + line_spans: __span + pygments_lang_class: true +- pymdownx.inlinehilite +- pymdownx.snippets +- pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format +- pymdownx.tabbed: + alternate_style: true + +#======= Plugins ===== +plugins: +- search +- mkdocstrings: + default_handler: python + handlers: + python: + options: + annotations_path: brief + docstring_style: google + heading_level: 3 + modernize_annotations: true + show_category_heading: true + show_object_full_path: false + show_root_toc_entry: true + show_signature: true + show_signature_annotations: false + signature_crossrefs: true + + +copyright: © Copyright 2021-2025, Saez-lab development team. diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index ed57852..917b298 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -1,52 +1,65 @@ +# =================================== +# ======= BUILD ======== +# =================================== [build-system] -requires = ["hatchling"] build-backend = "hatchling.build" +requires = ["hatchling"] -{% if cookiecutter.project_name.lower().replace("-", "_") != cookiecutter.package_name -%} -[tool.hatch.build.targets.wheel] -packages = [ "{{ cookiecutter.package_name }}" ] - -{% endif -%} +#=================================== +#======= PROJECT ======== +#=================================== [project] -name = "{{ cookiecutter.project_slug }}" -version = "0.0.1" -description = "{{ cookiecutter.short_description }}" -license = "{{ cookiecutter._license_spdx }}" -maintainers = [ - "{{ cookiecutter.author_full_name }} <{{ cookiecutter.author_email }}>" -] authors = [ - { name = "{{ cookiecutter.author_full_name }}" }, -] -packages = [ - { include = "{{ cookiecutter.package_name }}" } + {name = "{{ cookiecutter.author_full_name }}", email="{{ cookiecutter.author_email }}"}, ] classifiers = [ + # How mature is this project? Common values are + # 2 - Pre-Alpha + # 3 - Alpha + # 4 - Beta + # 5 - Production/Stable "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Intended Audience :: Science/Research", - "License :: OSI Approved :: {{ cookiecutter._license_classifier }}", + "License :: OSI Approved :: {{ cookiecutter._license[cookiecutter.license].license_classifiers }}", "Operating System :: OS Independent", "Programming Language :: Python", "Topic :: Scientific/Engineering :: Bio-Informatics" ] -readme = "README.md" -requires-python = ">={{ cookiecutter.python_version }}" dependencies = [ "toml", ] +description = "{{ cookiecutter.short_description }}" +license = "{{ cookiecutter._license[cookiecutter.license].license_spdx }}" +maintainers = [ + {name="{{ cookiecutter.author_full_name }}", email="{{ cookiecutter.author_email }}"}, +] +name = "{{ cookiecutter.project_slug }}" +# packages = [ +# { include = "{{ cookiecutter.package_name }}" } +# ] +readme = "README.md" +requires-python = ">={{ cookiecutter.python_version }}" +version = "0.0.1" [project.optional-dependencies] - -[dependency-groups] dev = [ "distlib", - "pre-commit>=2.17.0", + "pre-commit", "bump2version", "twine", ] +docs = [ + "mkdocs-material>=9.6.14", + "pymdown-extensions>=10.15", + "mkdocstrings[python]>=0.29.1,<0.30" +] +security = [ + "bandit" +] tests = [ "pytest>=6.0", + "pytest-cov", "tox>=3.20.1", "tox-gh>=1.5.0", "coverage>=6.0", @@ -54,139 +67,52 @@ tests = [ "diff_cover", "ruff", ] -docs = [ - "sphinx>=5.0.0", - "sphinx-last-updated-by-git>=0.3", - "sphinx-autodoc-typehints>=1.18.0", - "sphinxcontrib-fulltoc>=1.2.0", - "sphinxcontrib-bibtex", - "sphinx-copybutton", - "myst-parser", - "myst-nb", - "jupyterlab", - "pydata-sphinx-theme", - "sphinx-remove-toctrees", - "jupyter-contrib-nbextensions", - "nbsphinx", -] - -[tool.uv.sources] -nbsphinx = { git = "https://github.com/deeenes/nbsphinx", branch = "timings" } -jupyter-contrib-nbextensions = { git = "https://github.com/deeenes/jupyter_contrib_nbextensions.git", branch = "master" } [project.urls] +Documentation = "https://{{ cookiecutter.github_organization }}.github.io/{{ cookiecutter.project_slug }}" Homepage = "{{ cookiecutter.project_repo }}" -Repository = "{{ cookiecutter.project_repo }}" Issues = "{{ cookiecutter.project_repo }}/issues" -Documentation = "https://{{ cookiecutter.project_slug }}.readthedocs.io/" - -[tool.ruff] -line-length = 80 -target-version = "py312" -unfixable = [ "UP" ] -extend-include = [ "*.ipynb" ] -format.docstring-code-format = true -format.trailing-comma = "all" -format.quote-style = "single" -lint.select = [ - "B", # flake8-bugbear - "BLE", # flake8-blind-except - "C4", # flake8-comprehensions - "D", # pydocstyle - "E", # Error detected by Pycodestyle - "F", # Errors detected by Pyflakes - "I", # isort - "RUF100", # Report unused noqa directives - "TID", # flake8-tidy-imports - "UP", # pyupgrade - "W", # Warning detected by Pycodestyle - "Q", # Consistent quotes - "S307", # eval() detection - "ANN", # flake8-annotations -] -lint.ignore = [ - "B008", # Errors from function calls in argument defaults. These are fine when the result is immutable. - "B024", - "D100", # Missing docstring in public module - "D104", # Missing docstring in public package - "D105", # __magic__ methods are often self-explanatory, allow missing docstrings - "D107", # Missing docstring in __init__ - # Disable one in each pair of mutually incompatible rules - "D200", - "D202", - "D203", # We don’t want a blank line before a class docstring - "D213", # <> We want docstrings to start immediately after the opening triple quote - "D400", # first line should end with a period [Bug: doesn’t work with single-line docstrings] - "D401", # First line should be in imperative mood; try rephrasing - "E131", - "E251", - "E303", - "E501", # line too long -> we accept long comment lines; formatter gets rid of long code lines - "E521", - "E731", # Do not assign a lambda expression, use a def -> lambda expression assignments are convenient - "E741", # allow I, O, l as variable names -> I is the identity matrix - "W503", - "W504", -] -lint.pydocstyle.convention = "numpy" -lint.quotes.inline-quotes = "single" +Repository = "{{ cookiecutter.project_repo }}" -[tool.ruff.lint.per-file-ignores] -"docs/*" = [ "I" ] -"docs/src/conf.py" = [ "D100" ] -"tests/*" = [ "D" ] -"tests/conftest.py" = [ "D101", "D102", "D103", "E402" ] -"*/__init__.py" = [ "D104", "F401" ] -[tool.ruff.lint.isort] -known-first-party = [ "{{ cookiecutter.package_name }}" ] -known-third-party = [ "numpy", "pandas" ] -sections = [ - "FUTURE", - "STDLIB", - "THIRDPARTY", - "NUM", - "FIRSTPARTY", - "LOCALFOLDER", -] -no-lines-before = [ "LOCALFOLDER" ] -lines-after-imports = 1 -combine-as-imports = true -force-sort-within-sections = true -case-sensitive = false -order-by-type = true -length-sort = true -force-wrap-aliases = true -use-parentheses = true -indent = " " -balanced-wrapping = true -include-trailing-comma = true -multi-line-output = 3 -exclude = [ - "docs/_build", +#=================================== +#======= TOOL ======== +#=================================== +#---- Bandit: tool for security analysis +[tool.bandit] +exclude_dirs = [".venv", "venv", ".tox", "build", "dist"] +skips = [ + "B101" # Name: assert_used ] -[tool.rstcheck] -report_level = "INFO" -ignore_directives = [ - "automodule", - "toctree", -] -ignore_roles = ["ref"] -ignore_messages = '(Unknown target name:.*|No (directive|role) entry for "(auto)?(class|method|property|function|func|mod|attr)" in module "docutils\.parsers\.rst\.languages\.en"\.)' - +#---- Coverage: a tool used to measure code coverage of Python programs [tool.coverage.run] -source = ["{{ cookiecutter.package_name }}"] omit = [ "**/test_*.py", ] +source = ["{{ cookiecutter.package_name }}"] -[tool.pytest.ini_options] -python_files = "test_*.py" -testpaths = [ +#---- Cruft: a tool to manage and update projects based on templates +[tool.cruft] +skip = [ "tests", + "{{ cookiecutter.package_name }}/__init__.py", + "{{ cookiecutter.package_name }}/_metadata.py", + "docs/api.md", + "docs/changelog.md", + "docs/references.bib", + "docs/references.md", + "docs/notebooks/example.ipynb", ] -xfail_strict = true + +{% if cookiecutter.project_name.lower().replace("-", "_") != cookiecutter.package_name -%} +#---- Hatch: a modern Python project manager for builds, environments, and publishing +[tool.hatch.build.targets.wheel] +packages = [ "{{ cookiecutter.package_name }}" ] +{% endif -%} + +#--- Pytest: a lightweight and scalable testing tool using the pytest framework +[tool.pytest.ini_options] addopts = [ # "-Werror", # if 3rd party libs raise DeprecationWarnings, just use filterwarnings below "--import-mode=importlib", # allow using test files with same name @@ -194,121 +120,214 @@ addopts = [ filterwarnings = [ # "ignore:.*U.*mode is deprecated:DeprecationWarning", ] +python_files = "test_*.py" +testpaths = [ + "tests", +] +xfail_strict = true +#---- RSTcheck: a tool to lint reStructuredText (.rst) files. +[tool.rstcheck] +ignore_directives = [ + "automodule", + "toctree", +] +ignore_messages = '(Unknown target name:.*|No (directive|role) entry for "(auto)?(class|method|property|function|func|mod|attr)" in module "docutils\.parsers\.rst\.languages\.en"\.)' +ignore_roles = ["ref"] +report_level = "INFO" + +#---- Ruff: a fast Python linter and formatter. +[tool.ruff] +extend-include = [ "*.ipynb" ] +line-length = 80 +target-version = "py312" + +[tool.ruff.format] +quote-style = "single" + +[tool.ruff.lint] +exclude = [ + "docs/_build" +] +ignore = [ + # flake8-bugbear (B) + "B008", # Name: function-call-in-default-argument + "B024", # Name: abstract-base-class-without-abstract-method + # pydocstyle (D) + "D100", # Name: undocumented-public-module + "D104", # Name: undocumented-public-package + "D105", # Name: undocumented-magic-method + "D107", # Name: undocumented-public-init + "D200", # Name: unnecessary-multiline-docstring + "D202", # Name: blank-line-after-function + "D203", # Name: incorrect-blank-line-before-class + "D213", # Name: multi-line-summary-second-line + "D400", # Name: missing-trailing-period + "D401", # Name: non-imperative-mood + # Error (E) + "E251", # Name: unexpected-spaces-around-keyword-parameter-equals + "E303", # Name: too-many-blank-lines + "E501", # Name: line too long + "E731", # Name: lambda-assignment + "E741" # allow I, O, l as variable names -> I is the identity matrix +] +select = [ + "B", # flake8-bugbear + "BLE", # flake8-blind-except + "C4", # flake8-comprehensions + "D", # pydocstyle + "E", # Error detected by Pycodestyle + "F", # Errors detected by Pyflakes + "I", # isort + "RUF100", # Report unused noqa directives + "TID", # flake8-tidy-imports + "UP", # pyupgrade + "W", # Warning detected by Pycodestyle + "Q", # Consistent quotes + "S307", # eval() detection + "ANN" # flake8-annotations +] +unfixable = ["UP"] + +[tool.ruff.lint.flake8-quotes] +inline-quotes = "single" + +[tool.ruff.lint.isort] +case-sensitive = false +combine-as-imports = true +force-sort-within-sections = true +force-wrap-aliases = true +known-first-party = [ "{{ cookiecutter.package_name }}" ] +known-third-party = ["numpy", "pandas"] +length-sort = true +lines-after-imports = 1 +no-lines-before = ["local-folder"] +order-by-type = true +section-order = [ + "future", + "standard-library", + "third-party", + "first-party", + "local-folder" +] + +[tool.ruff.lint.per-file-ignores] +"*/__init__.py" = ["D104", "F401"] +"docs/*" = ["I"] +"docs/src/conf.py" = ["D100"] +"tests/*" = ["D"] +"tests/conftest.py" = ["D101", "D102", "D103", "E402"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +#---- tox: a tool to automate testing in multiple environments. [tool.tox] -min_version = "3.20.0" -isolated_build = true -skip_missing_interpreters = true envlist = [ "covclean", "lint", "py{39,310,311,312,313}", "coverage", "readme", - "docs", -] - -[tool.tox-gh-actions] -python = [ - "3.9: py39", - "3.10: py310", - "3.11: py311", - "3.12: py312", - "3.13: py313", -] -env = [ - "py313: covclean, lint, coverage, readme, docs", + "docs" ] +isolated_build = true +min_version = "3.20.0" +skip_missing_interpreters = true [tool.tox.envs] -platform = [ - "linux: linux", - "macos: (macos|osx|darwin)", -] base_python = [ "py39: python3.9", "py310: python3.10", "py311: python3.11", "py312: python3.12", - "py313: python3.13", + "py313: python3.13" +] +commands = [ + "pytest --cov --cov-append --cov-config={toxinidir}/.coveragerc --ignore docs/ {posargs:-vv {env:_PYTEST_TOX_POSARGS:}}" ] deps = [ - ".[tests]", + ".[tests]" ] passenv = "TOXENV,CI,CODECOV_*,GITHUB_ACTIONS" +platform = [ + "linux: linux", + "macos: (macos|osx|darwin)" +] usedevelop = true + +[tool.tox.envs.clean-docs] +allowlist_externals = ["make"] +changedir = "{toxinidir}/docs" commands = [ - "pytest --cov --cov-append --cov-config={toxinidir}/.coveragerc --ignore docs/ {posargs:-vv {env:_PYTEST_TOX_POSARGS:}}", + "make clean" ] - -[tool.tox.envs.py313] -setenv = "_PYTEST_TOX_POSARGS=--log-cli-level=ERROR" +description = "Clean the documentation artifacts." +skip_install = true [tool.tox.envs.covclean] -description = "Clean coverage files." -deps = [ ".[tests]" ] -skip_install = true commands = [ - "coverage erase", + "coverage erase" ] - -[tool.tox.envs.lint] -description = "Perform linting." -deps = [ ".[dev]" ] +deps = [".[tests]"] +description = "Clean coverage files." skip_install = true -commands = [ - "pre-commit run --all-files --show-diff-on-failure {posargs:}", -] [tool.tox.envs.coverage] -description = "Report the coverage difference." -deps = [ ".[tests]" ] -skip_install = true -depends = "py{39,310,311,312,313}" -parallel_show_output = true commands = [ "coverage report --omit=\"tox/*\"", "coverage xml --omit=\"tox/*\" -o {toxinidir}/coverage.xml", - "diff-cover --compare-branch origin/main {toxinidir}/coverage.xml", + "diff-cover --compare-branch origin/main {toxinidir}/coverage.xml" ] +depends = "py{39,310,311,312,313}" +deps = [".[tests]"] +description = "Report the coverage difference." +parallel_show_output = true +skip_install = true [tool.tox.envs.docs] -description = "Build the documentation." -skip_install = true -allowlist_externals = [ "uv" ] +allowlist_externals = ["uv"] commands = [ "uv sync --extra docs", "uv run sphinx-build --color -b html {toxinidir}/docs/source {toxinidir}/docs/build/html", - "python -c 'import pathlib; print(f\"Documentation is available under:\", pathlib.Path(f\"{toxinidir}\") / \"docs\" / \"build\" / \"html\" / \"index.html\")'", + "python -c 'import pathlib; print(f\"Documentation is available under:\", pathlib.Path(f\"{toxinidir}\") / \"docs\" / \"build\" / \"html\" / \"index.html\")'" ] - -[tool.tox.envs.clean-docs] -description = "Clean the documentation artifacts." +description = "Build the documentation." skip_install = true -changedir = "{toxinidir}/docs" -allowlist_externals = [ "make" ] + +[tool.tox.envs.lint] commands = [ - "make clean", + "pre-commit run --all-files --show-diff-on-failure {posargs:}" ] +deps = [".[dev]"] +description = "Perform linting." +skip_install = true + +[tool.tox.envs.py313] +setenv = "_PYTEST_TOX_POSARGS=--log-cli-level=ERROR" [tool.tox.envs.readme] -description = "Check if README renders on PyPI." -deps = [ ".[dev]" ] -skip_install = true -allowlist_externals = [ "uv" ] +allowlist_externals = ["uv"] commands = [ "uv build --wheel --out-dir {envtmpdir}/build", - "twine check {envtmpdir}/build/*", + "twine check {envtmpdir}/build/*" ] +deps = [".[dev]"] +description = "Check if README renders on PyPI." +skip_install = true -[tool.cruft] -skip = [ - "tests", - "{{ cookiecutter.package_name }}/__init__.py", - "{{ cookiecutter.package_name }}/_metadata.py", - "docs/api.md", - "docs/changelog.md", - "docs/references.bib", - "docs/references.md", - "docs/notebooks/example.ipynb", +[tool.tox-gh-actions] +env = [ + "py313: covclean, lint, coverage, readme, docs" +] +python = [ + "3.9: py39", + "3.10: py310", + "3.11: py311", + "3.12: py312", + "3.13: py313" ] + +[tool.uv.sources] +jupyter-contrib-nbextensions = {git = "https://github.com/deeenes/jupyter_contrib_nbextensions.git", branch = "master"} +nbsphinx = {git = "https://github.com/deeenes/nbsphinx", branch = "timings"} diff --git a/{{cookiecutter.project_slug}}/tests/test_placeholder.py b/{{cookiecutter.project_slug}}/tests/test_placeholder.py new file mode 100644 index 0000000..0210723 --- /dev/null +++ b/{{cookiecutter.project_slug}}/tests/test_placeholder.py @@ -0,0 +1,7 @@ +__all__ = [ + 'test_sum_numbers', +] + + +def test_sum_numbers() -> None: + assert 1 + 1 == 2 diff --git a/{{cookiecutter.project_slug}}/tests/test_twentythree.py b/{{cookiecutter.project_slug}}/tests/test_twentythree.py deleted file mode 100644 index d9c7d19..0000000 --- a/{{cookiecutter.project_slug}}/tests/test_twentythree.py +++ /dev/null @@ -1,10 +0,0 @@ -import {{ cookiecutter.package_name }} - -__all__ = ['Test23'] - - -class Test23: - - def test_twentythree(self): - - assert {{ cookiecutter.package_name }}.twentythree() == 23 diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/__init__.py b/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/__init__.py index 545997e..fb3e796 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/__init__.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/__init__.py @@ -8,7 +8,7 @@ # # File author(s): {{ cookiecutter.author_full_name }} ({{ cookiecutter.author_email }}) # -# Distributed under the {{ cookiecutter._licenses_short[cookiecutter.license] }} license +# Distributed under the {{ cookiecutter._license[cookiecutter.license].license_short }} license # See the file `LICENSE` or read a copy at {{ '' }}{%- if cookiecutter.license == 'GNU General Public License Version 3' -%} @@ -41,9 +41,7 @@ {%- endif -%}{{ '' }} # -""" -{{ cookiecutter.short_description }} -""" +"""{{ cookiecutter.short_description }}""" __all__ = [ '__version__', diff --git a/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/_metadata.py b/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/_metadata.py index 676fa27..d935aef 100644 --- a/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/_metadata.py +++ b/{{cookiecutter.project_slug}}/{{cookiecutter.package_name}}/_metadata.py @@ -8,7 +8,7 @@ # # File author(s): {{ cookiecutter.author_full_name }} ({{ cookiecutter.author_email }}) # -# Distributed under the {{ cookiecutter._licenses_short[cookiecutter.license] }} license +# Distributed under the {{ cookiecutter._license[cookiecutter.license].license_short }} license # See the file `LICENSE` or read a copy at {{ '' }}{%- if cookiecutter.license == 'GNU General Public License Version 3' -%} @@ -41,9 +41,7 @@ {%- endif -%}{{ '' }} # -""" -Package metadata (version, authors, etc). -""" +"""Package metadata (version, authors, etc).""" __all__ = ['get_metadata'] @@ -56,9 +54,8 @@ _VERSION = '0.0.1' -def get_metadata(): - """ - Basic package metadata. +def get_metadata() -> dict: + """Basic package metadata. Retrieves package metadata from the current project directory or from the installed package. @@ -91,7 +88,8 @@ def get_metadata(): try: meta = { - k.lower(): v for k, v in + k.lower(): + v for k, v in importlib.metadata.metadata(here.name).items() } @@ -107,4 +105,4 @@ def get_metadata(): metadata = get_metadata() __version__ = metadata.get('version', None) __author__ = metadata.get('author', None) -__license__ = metadata.get('license', None) +__license__ = '{{ cookiecutter._license[cookiecutter.license].license_short }}'