From 3e671c6caa711167a7c12b5b66932ac5a0311fd4 Mon Sep 17 00:00:00 2001 From: saattrupdan Date: Tue, 16 Jul 2024 13:58:34 +0200 Subject: [PATCH] feat: Update jinja templates, add versions to cookiecutter.json as hidden variables --- README.md | 70 +-------- cookiecutter.json | 22 +++ .../.github/workflows/ci.yaml | 45 ++++-- ... cookiecutter.open_source == 'y' else ''}} | 28 ++-- .../.pre-commit-config.yaml | 10 +- {{cookiecutter.project_name}}/Dockerfile | 22 ++- {{cookiecutter.project_name}}/README.md | 61 ++++++-- .../config/__init__.py | 2 +- {{cookiecutter.project_name}}/makefile | 140 ++++++++++-------- {{cookiecutter.project_name}}/pyproject.toml | 114 ++++++++++---- .../src/scripts/{your_script.py => main.py} | 11 +- .../{{cookiecutter.project_name}}/__init__.py | 2 +- .../{your_module.py => module.py} | 3 +- .../tests/{test_dummy.py => test_module.py} | 2 +- 14 files changed, 328 insertions(+), 204 deletions(-) rename {{cookiecutter.project_name}}/src/scripts/{your_script.py => main.py} (50%) rename {{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/{your_module.py => module.py} (83%) rename {{cookiecutter.project_name}}/tests/{test_dummy.py => test_module.py} (66%) diff --git a/README.md b/README.md index af9e765..1fa3964 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ - + + + # Alexandra Institute Machine Learning Repository Template This repository is a template for a Python-based data science project within the @@ -56,64 +63,3 @@ browser. The cookiecutter allows the user to choose between `poetry` and `pip` for managing dependencies. In both cases, `pyproject.toml` will be used for all dependencies. - - -## Tools used in this project -* [Poetry](https://towardsdatascience.com/how-to-effortlessly-publish-your-python-package-to-pypi-using-poetry-44b305362f9f): Dependency management -* [hydra](https://hydra.cc/): Manage configuration files -* [pre-commit plugins](https://pre-commit.com/): Automate code reviewing formatting -* [pdoc](https://github.com/pdoc3/pdoc): Automatically create an API documentation for your project - - -## Project Structure -``` -. -├── .devcontainer -│   └── devcontainer.json -├── .github -│   └── workflows -│   ├── ci.yaml -│   └── docs.yaml -├── .gitignore -├── .pre-commit-config.yaml -├── CODE_OF_CONDUCT.md -├── CONTRIBUTING.md -├── Dockerfile -├── LICENSE -├── README.md -├── config -│   ├── __init__.py -│   ├── config.yaml -│   └── hydra -│   └── job_logging -│   └── custom.yaml -├── data -│   ├── final -│   │   └── .gitkeep -│   ├── processed -│   │   └── .gitkeep -│   └── raw -│   └── .gitkeep -├── docs -│   └── .gitkeep -├── gfx -│   ├── .gitkeep -│   └── alexandra_logo.png -├── makefile -├── models -│   └── .gitkeep -├── notebooks -│   └── .gitkeep -├── poetry.toml -├── pyproject.toml -├── src -│   ├── scripts -│   │   ├── fix_dot_env_file.py -│   │   └── your_script.py -│   └── {{cookiecutter.project_name}} -│   ├── __init__.py -│   └── your_module.py -└── tests - ├── __init__.py - └── test_dummy.py -``` diff --git a/cookiecutter.json b/cookiecutter.json index b4ac4c4..94a9dbb 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -6,5 +6,27 @@ "open_source": "Is this a public open source project? (y/n, default: n)", "python_version": "3.11", "dependency_manager": "Which dependency manager do you use? (pip/poetry, default: poetry)", + "pre_commit_hooks": "Do you want to use pre-commit hooks? (y/n, default: y)", + "_poetry_version": "1.8.2", + "_poetry_core_version": "1.0.0", + "_setuptools_version": "68.0.0", + "_hydra_version": "1.3.2", + "_pytest_version": "8.1.1", + "_pytest_cov_version": "4.1.0", + "_pre_commit_version": "3.6.2", + "_pdoc_version": "14.1.0", + "_readme_coverage_badger_version": "0.1.2", + "_click_version": "8.1.7", + "_ruff_version": "0.3.2", + "_mypy_version": "1.9.0", + "_nbstripout_version": "0.7.1", + "_coverage_version": "5.5", + "_toml_version": "0.10.2", + "_pygrep_hooks_version": "1.10.0", + "_pre_commit_hooks_version": "4.6.0", + "_checkout_action_version": "4", + "_setup_python_action_version": "5", + "_pre_commit_action_version": "3.0.1", + "_deploy_pages_action_version": "4", "_copy_without_render": [] } diff --git a/{{cookiecutter.project_name}}/.github/workflows/ci.yaml b/{{cookiecutter.project_name}}/.github/workflows/ci.yaml index 1f638f0..2d604bb 100644 --- a/{{cookiecutter.project_name}}/.github/workflows/ci.yaml +++ b/{{cookiecutter.project_name}}/.github/workflows/ci.yaml @@ -13,32 +13,51 @@ on: jobs: code-check: if: github.event.pull_request.draft == false - runs-on: {{'ubuntu-latest' if cookiecutter.open_source == 'y' else '[self-hosted, self-hosted-ubuntu-latest]'}} + {% if cookiecutter.open_source == 'y' -%} + runs-on: ubuntu-latest + {% else -%} + runs-on: [self-hosted, self-hosted-ubuntu-latest] + {% endif -%} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - - uses: pre-commit/action@v3.0.1 + - uses: actions/checkout@v{{ cookiecutter._checkout_action_version }} + - uses: actions/setup-python@v{{ cookiecutter._setup_python_action_version }} + - uses: pre-commit/action@v{{ cookiecutter._pre_commit_action_version }} pytest: if: github.event.pull_request.draft == false strategy: matrix: - os: [{{'windows-latest, macos-latest, ubuntu-latest' if cookiecutter.open_source == 'y' else 'self-hosted-ubuntu-latest'}}] + {% if cookiecutter.open_source == 'y' -%} + os: [windows-latest, macos-latest, ubuntu-latest] + {% else -%} + os: [self-hosted-ubuntu-latest] + {% endif -%} python-version: ["{{ cookiecutter.python_version }}"] runs-on: {% raw %}${{ matrix.os }}{% endraw %} steps: - - uses: actions/checkout@v4 -{{'\n - name: Install Poetry\n run: pipx install poetry==1.8.2\n\n' if cookiecutter.dependency_manager != 'pip'}} + - uses: actions/checkout@v{{ cookiecutter._checkout_action_version }} + {% if cookiecutter.dependency_manager != 'pip' -%} + - name: Install Poetry + run: pipx install poetry=={{ cookiecutter._poetry_version }} + {% endif -%} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v{{ cookiecutter._setup_python_action_version }} with: python-version: {% raw %}${{ matrix.python-version }}{% endraw %} - cache: {{'"poetry"' if cookiecutter.dependency_manager != 'pip' else 'pip'}} + cache: {{'poetry' if cookiecutter.dependency_manager != 'pip' else 'pip'}} - name: Install Dependencies run: | - {{'poetry env use ' if cookiecutter.dependency_manager != 'pip' else 'python'}}{% raw %}"${{ matrix.python-version }}"{% endraw %}{{'' if cookiecutter.dependency_manager != 'pip' else ' -m venv .venv'}} - {{'poetry install' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && pip install .[dev]'}} - + {% if cookiecutter.dependency_manager != 'pip' -%} + poetry env use {% raw %}"${{ matrix.python-version }}"{% endraw %} + poetry install + {% else -%} + python3 -m venv .venv + . .venv/bin/activate && pip install .[dev] + {% endif %} - name: Test with pytest - run: {{'poetry run' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && python -m'}} pytest + {% if cookiecutter.dependency_manager != 'pip' -%} + run: poetry run pytest + {% else -%} + run: . .venv/bin/activate && python -m pytest + {% endif -%} diff --git a/{{cookiecutter.project_name}}/.github/workflows/{{'docs.yaml' if cookiecutter.open_source == 'y' else ''}} b/{{cookiecutter.project_name}}/.github/workflows/{{'docs.yaml' if cookiecutter.open_source == 'y' else ''}} index c6a811e..080c2ce 100644 --- a/{{cookiecutter.project_name}}/.github/workflows/{{'docs.yaml' if cookiecutter.open_source == 'y' else ''}} +++ b/{{cookiecutter.project_name}}/.github/workflows/{{'docs.yaml' if cookiecutter.open_source == 'y' else ''}} @@ -15,22 +15,32 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 -{{'\n - name: Install Poetry\n run: pip3 install poetry==1.8.2\n' if cookiecutter.dependency_manager != 'pip' else ''}} + - uses: actions/checkout@v{{ cookiecutter._checkout_action_version }} + {% if cookiecutter.dependency_manager != 'pip' -%} + - name: Install Poetry + run: pip3 install poetry=={{ cookiecutter._poetry_version }} + {% endif -%} - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v{{ cookiecutter._setup_python_action_version }} with: python-version: "{{ cookiecutter.python_version }}" cache: {{'"poetry"' if cookiecutter.dependency_manager != 'pip' else 'pip'}} - name: Install Dependencies run: | - {{'poetry env use ' if cookiecutter.dependency_manager != 'pip' else 'python'}}"{{ cookiecutter.python_version }}"{{'' if cookiecutter.dependency_manager != 'pip' else ' -m venv .venv'}} - {{'poetry install --no-interaction --no-cache' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && python -m pip install .[dev]'}} - + {% if cookiecutter.dependency_manager != 'pip' -%} + poetry env use "{{ cookiecutter.python_version }}" + poetry install --no-interaction --no-cache + {% else -%} + python3 -m venv .venv + . .venv/bin/activate && python -m pip install .[dev] + {% endif -%} - name: Build documentation - run: {{'poetry run' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && python -m'}} pdoc --docformat google src/{{ cookiecutter.project_name }} -o docs - + {% if cookiecutter.dependency_manager != 'pip' -%} + run: poetry run pdoc --docformat google src/{{ cookiecutter.project_name }} -o docs + {% else -%} + run: . .venv/bin/activate && python -m pdoc --docformat google src/{{ cookiecutter.project_name }} -o docs + {% endif -%} - name: Compress documentation run: tar --directory docs/ -hcf artifact.tar . @@ -53,4 +63,4 @@ jobs: url: {% raw %}${{ steps.deployment.outputs.page_url }}{% endraw %} steps: - id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v{{ cookiecutter._deploy_pages_action_version }} diff --git a/{{cookiecutter.project_name}}/.pre-commit-config.yaml b/{{cookiecutter.project_name}}/.pre-commit-config.yaml index 9c83284..6bc65b8 100644 --- a/{{cookiecutter.project_name}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_name}}/.pre-commit-config.yaml @@ -1,16 +1,16 @@ repos: - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.10.0 + rev: v{{ cookiecutter._pygrep_hooks_version }} hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v{{ cookiecutter._pre_commit_hooks_version }} hooks: - id: end-of-file-fixer - id: trailing-whitespace - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.2 + rev: v{{ cookiecutter._ruff_version }} hooks: - id: ruff args: @@ -26,11 +26,11 @@ repos: - pyi - jupyter - repo: https://github.com/kynan/nbstripout - rev: 0.7.1 + rev: {{ cookiecutter._nbstripout_version }} hooks: - id: nbstripout - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v{{ cookiecutter._mypy_version }} hooks: - id: mypy args: diff --git a/{{cookiecutter.project_name}}/Dockerfile b/{{cookiecutter.project_name}}/Dockerfile index 173ebf9..95623cf 100644 --- a/{{cookiecutter.project_name}}/Dockerfile +++ b/{{cookiecutter.project_name}}/Dockerfile @@ -1,11 +1,27 @@ FROM python:{{ cookiecutter.python_version }}-slim-bookworm -{{'\n# Install poetry\nRUN pip install "poetry==1.8.2"\n' if cookiecutter.dependency_manager != 'pip'}} + +{%- if cookiecutter.dependency_manager != 'pip' %} + +# Install Poetry +RUN pip install "poetry=={{ cookiecutter._poetry_version }}" +{%- endif %} + # Move the files into the container WORKDIR /project COPY . /project # Install dependencies -{{'RUN poetry env use python' if cookiecutter.dependency_manager != 'pip'}}{{cookiecutter.python_version if cookiecutter.dependency_manager != 'pip'}}{{'\nRUN poetry install --no-interaction --no-cache --without dev' if cookiecutter.dependency_manager != 'pip' else 'RUN pip install .'}} +{% if cookiecutter.dependency_manager != 'pip' -%} +RUN poetry env use python {{ cookiecutter.python_version }} +RUN poetry install --no-interaction --no-cache --without dev +{%- else -%} +RUN python3 -m venv .venv +RUN . .venv/bin/activate && pip install . +{%- endif %} # Run the script -CMD {{'poetry run ' if cookiecutter.dependency_manager != 'pip'}}python src/scripts/your_script.py +{% if cookiecutter.dependency_manager != 'pip' -%} +CMD poetry run python src/scripts/main.py +{%- else -%} +CMD . .venv/bin/activate && python src/scripts/main.py +{%- endif %} diff --git a/{{cookiecutter.project_name}}/README.md b/{{cookiecutter.project_name}}/README.md index 3a322b5..fd4eefd 100644 --- a/{{cookiecutter.project_name}}/README.md +++ b/{{cookiecutter.project_name}}/README.md @@ -1,13 +1,24 @@ - + + + # {{cookiecutter.project_name}} {{cookiecutter.project_description}} ______________________________________________________________________ -[![Code Coverage](https://img.shields.io/badge/Coverage-0%25-red.svg)](https://github.com/alexandrainst/{{cookiecutter.project_name}}/tree/main/tests){{'\n[![Documentation](https://img.shields.io/badge/docs-passing-green)](https://alexandrainst.github.io/' if cookiecutter.open_source == 'y'}}{{cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{'/' if cookiecutter.open_source == 'y'}}{{ cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{'.html)\n[![License](https://img.shields.io/github/license/alexandrainst/' if cookiecutter.open_source == 'y'}}{{cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{')](https://github.com/alexandrainst/' if cookiecutter.open_source == 'y'}}{{cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{'/blob/main/LICENSE)\n[![LastCommit](https://img.shields.io/github/last-commit/alexandrainst/' if cookiecutter.open_source == 'y'}}{{cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{')](https://github.com/alexandrainst/' if cookiecutter.open_source == 'y'}}{{cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{'/commits/main)\n[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/alexandrainst/' if cookiecutter.open_source == 'y'}}{{cookiecutter.project_name if cookiecutter.open_source == 'y'}}{{'/blob/main/CODE_OF_CONDUCT.md)' if cookiecutter.open_source == 'y'}} - - -Developer(s): +[![Code Coverage](https://img.shields.io/badge/Coverage-0%25-red.svg)](https://github.com/alexandrainst/{{cookiecutter.project_name}}/tree/main/tests) +{% if cookiecutter.open_source == 'y' -%} +[![Documentation](https://img.shields.io/badge/docs-passing-green)](https://alexandrainst.github.io/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}.html) +[![License](https://img.shields.io/github/license/alexandrainst/{{cookiecutter.project_name}})](https://github.com/alexandrainst/{{cookiecutter.project_name}}/blob/main/LICENSE) +[![LastCommit](https://img.shields.io/github/last-commit/alexandrainst/{{cookiecutter.project_name}})](https://github.com/alexandrainst/{{cookiecutter.project_name}}/commits/main) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/alexandrainst/{{cookiecutter.project_name}}/blob/main/CODE_OF_CONDUCT.md) +{% endif %} +Developer: - {{cookiecutter.author_name}} ({{cookiecutter.email}}) @@ -18,25 +29,45 @@ Developer(s): 1. Run `make install`, which sets up a virtual environment and all Python dependencies therein. 2. Run `source .venv/bin/activate` to activate the virtual environment. -3. (Optional) Run `make install-pre-commit`, which installs pre-commit hooks for linting, formatting and type checking.{{'\n4. (Optional) Run `make add-rag` to add RAG functionality from [ragger](https://github.com/alexandrainst/ragger).' if cookiecutter.open_source != 'y'}} - +3. (Optional) Run `make install-pre-commit`, which installs pre-commit hooks for linting, formatting and type checking. +{% if cookiecutter.open_source != 'y' -%} +4. (Optional) Run `make add-rag` to add RAG functionality from [ragger](https://github.com/alexandrainst/ragger). +{% endif %} ### Adding and Removing Packages To install new PyPI packages, run: ``` -{{'poetry add' if cookiecutter.dependency_manager != 'pip' else 'pip install'}} +{% if cookiecutter.dependency_manager != 'pip' -%} +poetry add +{% else -%} +pip install +{% endif -%} ``` To remove them again, run: ``` -{{'poetry remove' if cookiecutter.dependency_manager != 'pip' else 'pip uninstall'}} -```{{'\n\nTo freeze dependencies into pyproject.toml, run:\n```\nmake freeze\n```' if cookiecutter.dependency_manager == 'pip'}} - +{% if cookiecutter.dependency_manager != 'pip' -%} +poetry remove +{% else -%} +pip uninstall +{% endif -%} +``` +{% if cookiecutter.dependency_manager == 'pip' -%} +To freeze dependencies into `requirements.txt`, run: +``` +make freeze +``` +{% endif %} To show all installed packages, run: ``` -{{'poetry show' if cookiecutter.dependency_manager != 'pip' else 'pip list'}} +{% if cookiecutter.dependency_manager != 'pip' -%} +poetry show +{% else -%} +pip list +{% endif -%} ``` + ## All Built-in Commands The project includes the following convenience commands: @@ -47,8 +78,10 @@ The project includes the following convenience commands: - `make type-check`: Type check the code using `mypy`. - `make test`: Run tests using `pytest` and update the coverage badge in the readme. - `make docker`: Build a Docker image and run the Docker container. +{% if cookiecutter.open_source != 'y' -%} - `make add-rag`: Add RAG functionality from [ragger](https://github.com/alexandrainst/ragger). - `make update-rag`: Update RAG functionality from [ragger](https://github.com/alexandrainst/ragger). +{% endif -%} - `make docs`: Generate HTML documentation using `pdoc`. - `make view-docs`: View the generated HTML documentation in a browser. - `make tree`: Show the project structure as a tree. @@ -94,8 +127,8 @@ file is also a Python script, rather than a module. ### Docker Setup A Dockerfile is included in the new repositories, which by default runs -`src/scripts/your_script.py`. You can build the Docker image and run the Docker -container by running `make docker`. +`src/scripts/main.py`. You can build the Docker image and run the Docker container by +running `make docker`. ### Automatic Documentation diff --git a/{{cookiecutter.project_name}}/config/__init__.py b/{{cookiecutter.project_name}}/config/__init__.py index 206fafb..d1e4e78 100644 --- a/{{cookiecutter.project_name}}/config/__init__.py +++ b/{{cookiecutter.project_name}}/config/__init__.py @@ -1 +1 @@ -"""Config files to be used with hydra.""" +"""Config files to be used with Hydra.""" diff --git a/{{cookiecutter.project_name}}/makefile b/{{cookiecutter.project_name}}/makefile index 341ee96..cf214a2 100644 --- a/{{cookiecutter.project_name}}/makefile +++ b/{{cookiecutter.project_name}}/makefile @@ -9,15 +9,14 @@ ifeq (,$(wildcard .env)) $(shell touch .env) endif +{%- if cookiecutter.dependency_manager != 'pip' %} -{% if cookiecutter.dependency_manager != 'pip' -%} # Create poetry env file if it does not already exist ifeq (,$(wildcard ${HOME}/.poetry/env)) $(shell mkdir ${HOME}/.poetry) $(shell touch ${HOME}/.poetry/env) endif - -{% endif -%} +{%- endif %} # Includes environment variables from the .env file include .env @@ -29,13 +28,12 @@ export GRPC_PYTHON_BUILD_SYSTEM_ZLIB=1 # Ensure that `pipx`{{' and `poetry`' if cookiecutter.dependency_manager != 'pip'}} will be able to run, since `pip` and `brew` put these # in the following folders on Unix systems export PATH := ${HOME}/.local/bin:/opt/homebrew/bin:$(PATH) +{%- if cookiecutter.dependency_manager != 'pip' %} -{% if cookiecutter.dependency_manager != 'pip' -%} # Prevent DBusErrorResponse during `poetry install`. # See https://stackoverflow.com/a/75098703 for more information export PYTHON_KEYRING_BACKEND := keyring.backends.null.Keyring - -{% endif -%} +{%- endif %} help: @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' makefile | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @@ -43,24 +41,29 @@ help: install: ## Install dependencies @echo "Installing the '{{ cookiecutter.project_name }}' project..." @$(MAKE) --quiet install-brew - {% if cookiecutter.open_source != 'y' -%} + {%- if cookiecutter.open_source != 'y' %} @$(MAKE) --quiet install-gpg @$(MAKE) --quiet generate-gpg-key - {% endif -%} - {% if cookiecutter.dependency_manager != 'pip' -%} + {%- endif %} + {%- if cookiecutter.dependency_manager != 'pip' %} @$(MAKE) --quiet install-pipx @$(MAKE) --quiet install-poetry - {% endif -%} + {%- endif %} @$(MAKE) --quiet install-dependencies + {%- if cookiecutter.pre_commit_hooks == 'y' %} + @$(MAKE) --quiet install-pre-commit + {%- endif %} @$(MAKE) --quiet setup-environment-variables @$(MAKE) --quiet setup-git - {% if cookiecutter.dependency_manager == 'pip' -%} + {%- if cookiecutter.dependency_manager == 'pip' %} @$(MAKE) --quiet freeze - {% endif -%} + {%- endif %} @$(MAKE) --quiet add-repo-to-git @echo "Installed the '{{ cookiecutter.project_name }}' project!" @echo "You can now activate your virtual environment with 'source .venv/bin/activate'." - @echo "If you want to use pre-commit hooks, run 'make install-pre-commit'." + {%- if cookiecutter.pre_commit_hooks != 'y' %} + @echo "If you want to use pre-commit hooks in the future, run 'make install-pre-commit'." + {%- endif %} @echo "Note that this is a {{'Poetry' if cookiecutter.dependency_manager != 'pip' else 'pip'}} project. Use '{{'poetry add ' if cookiecutter.dependency_manager != 'pip' else 'pip install '}}' to install new dependencies and '{{'poetry remove ' if cookiecutter.dependency_manager != 'pip' else 'pip uninstall '}}' to remove them.{{' Freeze your dependencies into pyproject.toml with \'make freeze\'.' if cookiecutter.dependency_manager == 'pip'}}" install-brew: @@ -69,7 +72,8 @@ install-brew: echo "Installed Homebrew."; \ fi -{% if cookiecutter.open_source != 'y' -%} +{%- if cookiecutter.open_source != 'y' %} + install-gpg: @if [ "$(shell which gpg)" = "" ] || [ "$(shell which gpg-agent)" = "" ]; then \ uname=$$(uname); \ @@ -94,9 +98,43 @@ generate-gpg-key: echo "Generated a new GPG key. Remember to register it to Github at https://github.com/settings/gpg/new, where you add the key generated by running 'gpg --armor --export '"; \ fi -{% endif -%} +setup-ragger: + @if [ -d src/ragger ]; then \ + echo "RAG functionality already added to the project. Skipping."; \ + exit 1; \ + fi + @if [ ! -d .venv ]; then \ + echo "The project hasn't been installed yet. Please run 'make install' first."; \ + exit 1; \ + fi + @echo "Adding RAG functionality to the project..." + @cd src && git clone git@github.com:alexandrainst/ragger.git && rm -rf ragger/.git && cd - + @mv src/ragger/src/scripts/run_demo.py src/scripts/ + @mv src/ragger/src/scripts/run_cli.py src/scripts/ + @cp -R src/ragger/config/* config/ + @if [ ! -f data/processed/document_store.jsonl ]; then \ + mv src/ragger/data/processed/document_store.jsonl data/processed/; \ + fi + @{{'poetry add' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && pip install'}} -e src/ragger + @{{'poetry run ' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && '}}python src/scripts/fix_dot_env_file.py --include-openai + +add-rag: ## Add RAG functionality to the project + @$(MAKE) --quiet setup-ragger + @git add . + @echo "Added RAG functionality to the project, but haven't committed the changes yet. Please commit them manually." + +update-rag: ## Update the RAG submodule - this requires `make add-rag` to have been run + @if [ -d src/ragger ]; then \ + rm -rf src/ragger; \ + $(MAKE) --quiet setup-ragger; \ + git add .; \ + echo "Updated RAG functionality, but haven't committed the changes yet (if any). Please commit them manually."; \ + else \ + echo "RAG code not found. Please run 'make add-rag' first."; \ + fi +{%- endif %} +{%- if cookiecutter.dependency_manager != 'pip' %} -{% if cookiecutter.dependency_manager != 'pip' -%} install-pipx: @if [ "$(shell which pipx)" = "" ]; then \ uname=$$(uname); \ @@ -111,13 +149,12 @@ install-pipx: fi install-poetry: - @if [ ! "$(shell poetry --version)" = "Poetry (version 1.8.2)" ]; then \ + @if [ ! "$(shell poetry --version)" = "Poetry (version {{ cookiecutter._poetry_version }})" ]; then \ python3 -m pip uninstall -y poetry poetry-core poetry-plugin-export; \ - pipx install --force poetry==1.8.2; \ + pipx install --force poetry=={{ cookiecutter._poetry_version }}; \ echo "Installed Poetry."; \ fi - -{% endif -%} +{%- endif %} install-dependencies: {% if cookiecutter.dependency_manager != 'pip' -%} @@ -125,7 +162,7 @@ install-dependencies: {%- else -%} @python -m venv .venv @. .venv/bin/activate && pip install -qU pip && pip install -qe .[dev] - {% endif %} + {%- endif %} setup-environment-variables: @if [ -d src/ragger ]; then \ @@ -153,13 +190,10 @@ setup-git: git config --local user.signingkey ${GPG_KEY_ID}; \ echo "Signed with GPG key ID ${GPG_KEY_ID}."; \ fi - {% endif -%} + {%- endif %} @git config --local user.name ${GIT_NAME} @git config --local user.email ${GIT_EMAIL} -install-pre-commit: ## Install pre-commit hooks - @{{'poetry run ' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && '}}pre-commit install - add-repo-to-git: @export GPG_TTY=$(tty) @gpgconf --kill gpg-agent @@ -171,12 +205,15 @@ add-repo-to-git: git remote add origin git@github.com:alexandrainst/{{ cookiecutter.project_name }}.git; \ fi -{% if cookiecutter.dependency_manager == 'pip' -%} +install-pre-commit: ## Install pre-commit hooks + @{{'poetry run ' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && '}}pre-commit install + +{%- if cookiecutter.dependency_manager == 'pip' %} + freeze: ## Freeze dependencies @. .venv/bin/activate && python src/scripts/freeze_dependencies.py @echo "Updated dependencies in pyproject.toml." - -{% endif -%} +{%- endif %} docs: ## Generate documentation @{{'poetry run ' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && '}}pdoc --docformat google src/{{cookiecutter.project_name}} -o docs @@ -199,44 +236,23 @@ test: ## Run tests docker: ## Build Docker image and run container {% if cookiecutter.dependency_manager == 'pip' -%} @$(MAKE) --quiet freeze - {% endif -%} + {%- endif %} @docker build -t {{ cookiecutter.project_name }} . @docker run -it --rm {{ cookiecutter.project_name }} tree: ## Print directory tree @tree -a --gitignore -I .git . -setup-ragger: - @if [ -d src/ragger ]; then \ - echo "RAG functionality already added to the project. Skipping."; \ - exit 1; \ - fi - @if [ ! -d .venv ]; then \ - echo "The project hasn't been installed yet. Please run 'make install' first."; \ - exit 1; \ - fi - @echo "Adding RAG functionality to the project..." - @cd src && git clone git@github.com:alexandrainst/ragger.git && rm -rf ragger/.git && cd - - @mv src/ragger/src/scripts/run_demo.py src/scripts/ - @mv src/ragger/src/scripts/run_cli.py src/scripts/ - @cp -R src/ragger/config/* config/ - @if [ ! -f data/processed/document_store.jsonl ]; then \ - mv src/ragger/data/processed/document_store.jsonl data/processed/; \ - fi - @{{'poetry add' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && pip install'}} -e src/ragger - @{{'poetry run ' if cookiecutter.dependency_manager != 'pip' else '. .venv/bin/activate && '}}python src/scripts/fix_dot_env_file.py --include-openai - -add-rag: ## Add RAG functionality to the project - @$(MAKE) --quiet setup-ragger - @git add . - @echo "Added RAG functionality to the project, but haven't committed the changes yet. Please commit them manually." +lint: ## Lint the project + {% if cookiecutter.dependency_manager != 'pip' -%} + poetry run ruff check src --fix + {%- else -%} + . .venv/bin/activate && ruff check src --fix + {%- endif %} -update-rag: ## Update the RAG submodule - this requires `make add-rag` to have been run - @if [ -d src/ragger ]; then \ - rm -rf src/ragger; \ - $(MAKE) --quiet setup-ragger; \ - git add .; \ - echo "Updated RAG functionality, but haven't committed the changes yet (if any). Please commit them manually."; \ - else \ - echo "RAG code not found. Please run 'make add-rag' first."; \ - fi +type-check: ## Type-check the project + {% if cookiecutter.dependency_manager != 'pip' -%} + @poetry run mypy src --install-types --non-interactive --ignore-missing-imports --show-error-codes --check-untyped-defs + {%- else -%} + . .venv/bin/activate && mypy src --install-types --non-interactive --ignore-missing-imports --show-error-codes --check-untyped-defs + {%- endif %} diff --git a/{{cookiecutter.project_name}}/pyproject.toml b/{{cookiecutter.project_name}}/pyproject.toml index 54090ae..7394b0b 100644 --- a/{{cookiecutter.project_name}}/pyproject.toml +++ b/{{cookiecutter.project_name}}/pyproject.toml @@ -1,32 +1,81 @@ -{{'[tool.poetry]' if cookiecutter.dependency_manager != 'pip' else '[project]'}} +{% if cookiecutter.dependency_manager != 'pip' -%} +[tool.poetry] name = "{{cookiecutter.project_name}}" description = "{{cookiecutter.project_description}}" -version = "0.0.0"{{'\nauthors = [\n "' if cookiecutter.dependency_manager != 'pip'}}{{cookiecutter.author_name if cookiecutter.dependency_manager != 'pip'}}{{' <' if cookiecutter.dependency_manager != 'pip'}}{{cookiecutter.email if cookiecutter.dependency_manager != 'pip'}}{{'>",\n]' if cookiecutter.dependency_manager != 'pip'}} -readme = "README.md"{{'\nlicense = ' if cookiecutter.dependency_manager != 'pip'}}{{'"MIT"' if cookiecutter.open_source == "y" and cookiecutter.dependency_manager != 'pip'}}{{'"Proprietary"' if cookiecutter.open_source != "y" and cookiecutter.dependency_manager != 'pip'}}{{'\n\n[tool.poetry.dependencies]' if cookiecutter.dependency_manager != 'pip'}} -{{'python' if cookiecutter.dependency_manager != 'pip' else 'requires-python'}} = ">={{ cookiecutter.python_version }},<3.13"{{'' if cookiecutter.dependency_manager != 'pip' else '\ndependencies = ['}} -{{'hydra-core = "^1.3.2"' if cookiecutter.dependency_manager != 'pip' else ' "hydra-core==1.3.2",\n]'}}{{'' if cookiecutter.dependency_manager != 'pip' else '\n[[project.authors]]\nname = "'}}{{cookiecutter.author_name if cookiecutter.dependency_manager == 'pip'}}{{'"\nemail = "' if cookiecutter.dependency_manager == 'pip'}}{{cookiecutter.email if cookiecutter.dependency_manager == 'pip'}}{{'"' if cookiecutter.dependency_manager == 'pip'}} +version = "0.0.0" +authors = [ + "{{cookiecutter.author_name}} <{{cookiecutter.email}}>", +] +readme = "README.md" +license = {{'"MIT"' if cookiecutter.open_source == "y" else '"Proprietary"'}} -[build-system] -requires = [ - "{{'poetry-core>=1.0.0' if cookiecutter.dependency_manager != 'pip' else 'setuptools>=68.0.0'}}" +[tool.poetry.dependencies] +python = ">={{ cookiecutter.python_version }}" +hydra-core = "^{{ cookiecutter._hydra_version }}" + +[tool.poetry.group.dev.dependencies] +pytest = ">={{ cookiecutter._pytest_version }}" +pytest-cov = ">={{ cookiecutter._pytest_cov_version }}" +pre-commit = ">={{ cookiecutter._pre_commit_version }}" +pdoc = ">={{ cookiecutter._pdoc_version }}" +readme-coverage-badger = ">={{ cookiecutter._readme_coverage_badger_version }}" +click = ">={{ cookiecutter._click_version }}" +ruff = ">={{ cookiecutter._ruff_version }}" +mypy = ">={{ cookiecutter._mypy_version }}" +nbstripout = ">={{ cookiecutter._nbstripout_version }}" + +[[tool.poetry.source]] +name = "pypi" +{% else -%} +[project] +name = "{{cookiecutter.project_name}}" +description = "{{cookiecutter.project_description}}" +version = "0.0.0" +readme = "README.md" +authors = [ + {name = "{{cookiecutter.author_name}}", email = "{{cookiecutter.email}}"}, +] +requires-python = ">={{ cookiecutter.python_version }}" +dependencies = [ + "hydra-core=={{ cookiecutter._hydra_version }}", ] -build-backend = "{{'poetry.core.masonry.api' if cookiecutter.dependency_manager != 'pip' else 'setuptools.build_meta'}}" -{{'\n[project.license]\nfile = "LICENSE"\n' if cookiecutter.dependency_manager == 'pip'}} -{{'[tool.poetry.group.dev.dependencies]' if cookiecutter.dependency_manager != 'pip' else '[project.optional-dependencies]\ndev = ['}} -{{'pytest = ">=8.1.1"' if cookiecutter.dependency_manager != 'pip' else ' "pytest==8.1.1",'}} -{{'pytest-cov = ">=4.1.0"' if cookiecutter.dependency_manager != 'pip' else ' "pytest-cov==4.1.0",'}} -{{'pre-commit = ">=3.6.2"' if cookiecutter.dependency_manager != 'pip' else ' "pre-commit==3.6.2",'}} -{{'pdoc = ">=14.1.0"' if cookiecutter.dependency_manager != 'pip' else ' "pdoc==14.1.0",'}} -{{'readme-coverage-badger = ">=0.1.2"' if cookiecutter.dependency_manager != 'pip' else ' "readme-coverage-badger==0.1.2",'}} -{{'click = ">=8.1.7"' if cookiecutter.dependency_manager != 'pip' else ' "click==8.1.7",'}} -{{'ruff = ">=0.3.2"' if cookiecutter.dependency_manager != 'pip' else ' "ruff==0.3.2",'}} -{{'mypy = ">=1.9.0"' if cookiecutter.dependency_manager != 'pip' else ' "mypy==1.9.0",'}} -{{'nbstripout = ">=0.7.1"' if cookiecutter.dependency_manager != 'pip' else ' "nbstripout==0.7.1",'}} -{{'' if cookiecutter.dependency_manager != 'pip' else ' "coverage[toml]==5.5",'}} -{{'[[tool.poetry.source]]\nname = "pypi"' if cookiecutter.dependency_manager != 'pip' else ']'}} +[project.license] +file = "LICENSE" + +[project.optional-dependencies] +dev = [ + "pytest>={{ cookiecutter._pytest_version }}", + "pytest-cov>={{ cookiecutter._pytest_cov_version }}", + "pre-commit>={{ cookiecutter._pre_commit_version }}", + "pdoc>={{ cookiecutter._pdoc_version }}", + "readme-coverage-badger>={{ cookiecutter._readme_coverage_badger_version }}", + "click>={{ cookiecutter._click_version }}", + "ruff>={{ cookiecutter._ruff_version }}", + "mypy>={{ cookiecutter._mypy_version }}", + "nbstripout>={{ cookiecutter._nbstripout_version }}", + "coverage>={{ cookiecutter._coverage_version }}", + "toml>={{ cookiecutter._toml_version }}", +] +{% endif %} [tool.ruff] -target-version = "{{'py312' if cookiecutter.python_version == '3.12'}}{{'py311' if cookiecutter.python_version == '3.11'}}{{'py310' if cookiecutter.python_version == '3.10'}}{{'py39' if cookiecutter.python_version == '3.9'}}{{'py38' if cookiecutter.python_version == '3.8'}}{{'py37' if cookiecutter.python_version == '3.7'}}" +{% if cookiecutter.python_version == '3.13' -%} +target-version = "py313" +{% elif cookiecutter.python_version == '3.12' -%} +target-version = "py312" +{% elif cookiecutter.python_version == '3.11' -%} +target-version = "py311" +{% elif cookiecutter.python_version == '3.10' -%} +target-version = "py310" +{% elif cookiecutter.python_version == '3.9' -%} +target-version = "py39" +{% elif cookiecutter.python_version == '3.8' -%} +target-version = "py38" +{% elif cookiecutter.python_version == '3.7' -%} +target-version = "py37" +{% elif cookiecutter.python_version == '3.6' -%} +target-version = "py36" +{% endif %} line-length = 88 exclude = [ ".git", @@ -52,7 +101,7 @@ extend-select = [ "__init__.py" = [ "F401", ] -"src/scripts/your_script.py" = [ +"src/scripts/main.py" = [ "I", ] @@ -65,11 +114,8 @@ convention = "google" [tool.pytest.ini_options] minversion = "7.0" addopts = [ - '--verbose', '--durations=10', '--color=yes', - '-s', - '-vv', '--doctest-modules', '--cov=src/{{cookiecutter.project_name}}', ] @@ -80,9 +126,23 @@ filterwarnings = [ "ignore::DeprecationWarning", "ignore::PendingDeprecationWarning", "ignore::ImportWarning", + "ignore::FutureWarning", ] log_cli_level = "info" testpaths = [ "tests", "src/{{cookiecutter.project_name}}", ] + +[build-system] +{% if cookiecutter.dependency_manager != 'pip' -%} +requires = [ + "poetry-core>={{ cookiecutter._poetry_core_version }}", +] +build-backend = "poetry.core.masonry.api" +{% else -%} +requires = [ + "setuptools>={{ cookiecutter._setuptools_version }}", +] +build-backend = "setuptools.build_meta" +{% endif -%} diff --git a/{{cookiecutter.project_name}}/src/scripts/your_script.py b/{{cookiecutter.project_name}}/src/scripts/main.py similarity index 50% rename from {{cookiecutter.project_name}}/src/scripts/your_script.py rename to {{cookiecutter.project_name}}/src/scripts/main.py index f242204..8b96279 100644 --- a/{{cookiecutter.project_name}}/src/scripts/your_script.py +++ b/{{cookiecutter.project_name}}/src/scripts/main.py @@ -1,20 +1,21 @@ -"""Main script for your project. +"""Main script. Usage: - python src/scripts/your_script.py = ... + python src/scripts/main.py = ... """ import hydra from omegaconf import DictConfig -from {{ cookiecutter.project_name }}.your_module import example_function +from {{ cookiecutter.project_name }}.module import example_function @hydra.main(config_path="../../config", config_name="config", version_base=None) def main(config: DictConfig) -> None: - """Main function for your project. + """Main function. Args: - config: The Hydra config for your project. + config: + The Hydra config for your project. """ example_function(config=config) diff --git a/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/__init__.py b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/__init__.py index 680d300..37bdb16 100644 --- a/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/__init__.py +++ b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/__init__.py @@ -1 +1 @@ -"""{{cookiecutter.project_description}}""" +"""{{cookiecutter.project_description}}{{ '.' if not cookiecutter.project_description.endswith('.') }}""" diff --git a/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/your_module.py b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/module.py similarity index 83% rename from {{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/your_module.py rename to {{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/module.py index 6bbd36e..9b77127 100644 --- a/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/your_module.py +++ b/{{cookiecutter.project_name}}/src/{{cookiecutter.project_name}}/module.py @@ -11,7 +11,8 @@ def example_function(config: DictConfig) -> None: """An example function for your project. Args: - config: The Hydra config for your project. + config: + The Hydra config for your project. """ logger.info("Hello World!") logger.info(f"Your config is: {config}") diff --git a/{{cookiecutter.project_name}}/tests/test_dummy.py b/{{cookiecutter.project_name}}/tests/test_module.py similarity index 66% rename from {{cookiecutter.project_name}}/tests/test_dummy.py rename to {{cookiecutter.project_name}}/tests/test_module.py index 3d2b8ff..91ac29f 100644 --- a/{{cookiecutter.project_name}}/tests/test_dummy.py +++ b/{{cookiecutter.project_name}}/tests/test_module.py @@ -1,4 +1,4 @@ -"""Dummy test module.""" +"""Tests for the `module` module.""" def test_dummy() -> None: