diff --git a/CHANGELOG.md b/CHANGELOG.md index 19e06afa..d260b95b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- Typing support. [#698](https://github.com/NLeSC/python-template/pull/698) + ### Changed ### Removed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 849988cd..0065accf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ in line with what is recommended there. If not, please [contribute to the guide] 1. (**important**) wait until some kind of consensus is reached about your idea being a good idea; 1. if needed, fork the repository to your own Github profile and create your own feature branch off of the latest main commit. While working on your feature branch, make sure to stay up to date with the main branch by pulling in changes, possibly from the 'upstream' repository (follow the instructions [here](https://help.github.com/articles/configuring-a-remote-for-a-fork/) and [here](https://help.github.com/articles/syncing-a-fork/)); 1. install dependencies (see the [development documentation](README.dev.md#create-a-virtual-environment)); -1. make sure the existing tests still work by running ``pytest``. If project tests fail use ``pytest --keep-baked-projects`` to keep generated project files in `/tmp/pytest-*` and investigate; +1. make sure the existing tests still work by running ``pytest``. If project tests fail use ``pytest --keep-copied-projects`` to keep generated project files in `/tmp/pytest-*` and investigate; 1. add your own tests (if necessary); 1. update or expand the documentation; 1. update the `CHANGELOG.md` file with your change; diff --git a/copier/questions/features_code_quality.yml b/copier/questions/features_code_quality.yml index 82a0afc7..b6bc377d 100644 --- a/copier/questions/features_code_quality.yml +++ b/copier/questions/features_code_quality.yml @@ -7,7 +7,7 @@ SelectCodeQualityFeatures: type: yaml default: |- {% if template_profile == 'recommended' %} - [AddLocalTests_flag, SelectGitHubActions_flag, AddLinting_flag, AddSonarCloud_flag, AddEditorConfig_flag] + [AddLocalTests_flag, SelectGitHubActions_flag, AddLinting_flag, AddSonarCloud_flag, AddEditorConfig_flag, AddTyping_flag] {%- else -%} [] {%- endif %} @@ -32,7 +32,8 @@ SelectCodeQualityFeatures: Editorconfig: value: AddEditorConfig_flag # validator: "{% if something != 'AnotherThing' %}BlaBla{% endif %}" - + Typing (select type checker later): + value: AddTyping_flag # Sub-menus SelectGitHubActions: @@ -54,6 +55,27 @@ SelectGitHubActions: value: AddLinkCheck_flag # validator: "{% if something != 'AnotherThing' %}BlaBla{% endif %}" +AddTyping: + type: bool + default: "{{ 'AddTyping_flag' in SelectCodeQualityFeatures }}" + when: false + +SelectTypeChecker: + when: "{{ template_profile != 'minimum' and AddTyping }}" + type: str + default: pyright + help: Select a type checker + choices: + Mypy: + value: mypy + Pyright: + value: pyright + # TODO add pyrefly https://pyrefly.org/ + # TODO add ty https://github.com/astral-sh/ty + +# TODO add runtime type checking (using pydantic or typeguard) + +# TODO ask how strict to typecheck # computed features AddLocalTests: diff --git a/copier/questions/features_documentation.yml b/copier/questions/features_documentation.yml index cc4098d3..bee43b1a 100644 --- a/copier/questions/features_documentation.yml +++ b/copier/questions/features_documentation.yml @@ -44,3 +44,7 @@ AddGitHubActionDocumentation: type: bool default: "{{ 'AddGitHubActionDocumentation_flag' in SelectDocumentationFeatures }}" when: false +AddTypingInDocs: + type: bool + default: "{{ AddTyping and AddLocalDocumentation }}" + when: false diff --git a/template/README.md.jinja b/template/README.md.jinja index 8523375a..4d32610e 100644 --- a/template/README.md.jinja +++ b/template/README.md.jinja @@ -29,6 +29,14 @@ {% if AddLinkCheck -%} | Link checker | [![link-check]({{repository_url}}/actions/workflows/link-check.yml/badge.svg)]({{repository_url}}/actions/workflows/link-check.yml) | {%- endif -%} +{% if AddTyping -%} +{% if SelectTypeChecker == 'pyright' -%} +| Type checker | [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/) | +{%- endif -%} +{% if SelectTypeChecker == 'mypy' -%} +| Type checker | [![Checked with mypy](https://mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) | +{%- endif -%} +{%- endif %} ## How to use {{ package_name }} diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja index 821cfc14..7c8e23ab 100644 --- a/template/pyproject.toml.jinja +++ b/template/pyproject.toml.jinja @@ -58,6 +58,12 @@ dev = [ {%- endif %} "tox", "myst_parser", + {% if AddTyping and SelectTypeChecker -%} + "{{ SelectTypeChecker }}", + {%- endif %} + {%- if AddTypingInDocs %} + "sphinx-autodoc-typehints", + {%- endif %} ] {%- if AddLocalDocumentation %} docs = [ @@ -65,6 +71,9 @@ docs = [ "sphinx_rtd_theme", "sphinx-autoapi", "myst_parser", + {%- if AddTypingInDocs %} + "sphinx-autodoc-typehints", + {%- endif %} ] {%- endif %} publishing = [ @@ -147,6 +156,17 @@ known-first-party = ["{{ package_name }}"] force-single-line = true no-lines-before = ["future","standard-library","third-party","first-party","local-folder"] +{% if AddTyping -%} +{% if SelectTypeChecker == 'pyright' -%} +[tool.pyright] +include = ["src"] +{%- endif %} +{% if SelectTypeChecker == 'mypy' -%} +[tool.mypy] +files = ["src"] +{%- endif %} +{%- endif %} + [tool.bumpversion] current_version = "{{ version }}" diff --git a/template/src/{{package_name}}/my_module.py.jinja b/template/src/{{package_name}}/my_module.py.jinja index 7248f258..4a9143eb 100644 --- a/template/src/{{package_name}}/my_module.py.jinja +++ b/template/src/{{package_name}}/my_module.py.jinja @@ -8,10 +8,10 @@ def hello(name: str) -> str: Function docstring using Google docstring style. Args: - name (str): Name to say hello to + name{% if not AddTypingInDocs %} (str){% endif %}: Name to say hello to Returns: - str: Hello message + {% if not AddTypingInDocs %}str: {% endif %}Hello message Raises: ValueError: If `name` is equal to `nobody` diff --git a/template/src/{{package_name}}/{% if AddTyping %}py.typed{% endif %} b/template/src/{{package_name}}/{% if AddTyping %}py.typed{% endif %} new file mode 100644 index 00000000..e69de29b diff --git a/template/{% if AddDevDoc %}README.dev.md{% endif %}.jinja b/template/{% if AddDevDoc %}README.dev.md{% endif %}.jinja index a12eafcb..11cb34a5 100644 --- a/template/{% if AddDevDoc %}README.dev.md{% endif %}.jinja +++ b/template/{% if AddDevDoc %}README.dev.md{% endif %}.jinja @@ -87,6 +87,26 @@ git config --local core.hooksPath .githooks ``` {%- endif -%} +{% if AddTyping %} +## Type checking + +{% if SelectTypeChecker == 'pyright' -%} +We use [pyright](https://microsoft.github.io/pyright/) for type checking. + +```shell +pyright +``` + +{% endif -%} +{% if SelectTypeChecker == 'mypy' -%} +We use [mypy](http://mypy-lang.org/) for type checking. + +```shell +mypy +``` +{% endif -%} +{% endif -%} + ## Generating the API docs ```shell diff --git a/template/{% if AddLocalDocumentation %}docs{% endif %}/conf.py.jinja b/template/{% if AddLocalDocumentation %}docs{% endif %}/conf.py.jinja index ea6ff9a8..102df1e7 100644 --- a/template/{% if AddLocalDocumentation %}docs{% endif %}/conf.py.jinja +++ b/template/{% if AddLocalDocumentation %}docs{% endif %}/conf.py.jinja @@ -46,6 +46,9 @@ extensions = [ "sphinx.ext.viewcode", "autoapi.extension", "myst_parser", + {%- if AddTypingInDocs %} + "sphinx_autodoc_typehints", + {%- endif %} ] # Add any paths that contain templates here, relative to this directory. diff --git a/template/{% if HasWorkflows %}.github{% endif %}/workflows/{% if AddGitHubActionBuild %}build.yml{% endif %}.jinja b/template/{% if HasWorkflows %}.github{% endif %}/workflows/{% if AddGitHubActionBuild %}build.yml{% endif %}.jinja index 264f69bf..c0e451fc 100644 --- a/template/{% if HasWorkflows %}.github{% endif %}/workflows/{% if AddGitHubActionBuild %}build.yml{% endif %}.jinja +++ b/template/{% if HasWorkflows %}.github{% endif %}/workflows/{% if AddGitHubActionBuild %}build.yml{% endif %}.jinja @@ -64,3 +64,22 @@ jobs: ruff check ruff format --check {%- endif %} + +{%- if AddTyping %} + typecheck: + name: Type checking + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: 3.12 + - name: Upgrade pip and install dependencies + run: | + python -m pip install --upgrade pip setuptools + python -m pip install .[dev] + - name: Run {{ SelectTypeChecker }} + run: | + {{ SelectTypeChecker }} +{%- endif %} \ No newline at end of file