Skip to content

Commit 6efc60c

Browse files
233 strict typing (#238)
Fixes #233
2 parents 1357917 + 6372ae3 commit 6efc60c

File tree

5 files changed

+47
-21
lines changed

5 files changed

+47
-21
lines changed

copier.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ type_checker:
122122
- pyright
123123
- mypy
124124

125+
strict_typing:
126+
type: bool
127+
when: >-
128+
{{ type_checker == 'pyright' }}
129+
default: true
130+
help: |
131+
Would you like to run pyright in strict mode?
132+
The recommended approach is to start with strict mode and disable it if it
133+
becomes too costly to maintain.
134+
See https://diamondlightsource.github.io/python-copier-template/main/how-to/strict-mode.html
135+
for more information.
136+
125137
pypi:
126138
type: bool
127139
help: Would you like the wheel and source distribution to be automatically uploaded to PyPI when a release is made?

docs/how-to/strict-mode.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
# Enable Pyright's Strict Mode
1+
# Use Pyright's Strict Mode
22

3-
For projects using pyright you can enable strict mode for stricter than normal type checking. See [the docs](https://github.com/microsoft/pyright/blob/main/docs/configuration.md) for a full breakdown. The primary benefits are increased confidence in code that has been more thoroughly analyzed and a shorter development time thanks to fast feedback from the type checker.
3+
For projects using pyright you can enable strict mode for stricter than normal type checking. See [the docs](https://github.com/microsoft/pyright/blob/main/docs/configuration.md) for a full breakdown.
44

5-
## Configuration
5+
## How to Enable
66

7-
Change the `typeCheckingMode` line to `"strict"` in `pyproject.toml` as follows:
7+
When creating a template, select `pyright` as the type checker and type `y` when prompted to enable strict mode.
88

9-
```toml
10-
[tool.pyright]
11-
typeCheckingMode = "strict"
12-
reportMissingImports = false # Ignore missing stubs in imported modules
13-
```
9+
## Who Should Use Strict Mode?
1410

15-
## Third Party Libraries
11+
Strict mode enforces good practices such as type hints on function signatures, providing increased confidence in code that has been more thoroughly analyzed and a shorter development time thanks to fast feedback from the type checker. Starting a new project and continually keeping it passing provides a long-term benefit when it comes to maintanability and robustness. However, adopting strict mode on top of legacy projects is likely to lead to lots of errors to work through - probably thousands. Additionally it does not usually work well with libraries that do not have [type stubs](https://github.com/microsoft/pyright/blob/main/docs/type-stubs.md), you will likely need a `# type: ignore` on any line that directly uses the library code. This may limit the usefulness of pyright but it can still be worth doing to ensure your own code is internally consistent.
1612

17-
Strict mode does not usually work well with libraries that do not have [type stubs](https://github.com/microsoft/pyright/blob/main/docs/type-stubs.md), you will likely need a `# type: ignore` on any line that directly uses the library code. This may limit the usefulness of pyright but it can still be worth doing to ensure your own code is internally consistent.
13+
The recommended approach for brand new projects is to enable strict mode and stick with it for as long as is practical, moving away if it starts to cause more hindrance than help (e.g. because too many major dependencies do not support it).

example-answers.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ github_org: DiamondLightSource
1212
package_name: python_copier_template_example
1313
repo_name: python-copier-template-example
1414
type_checker: pyright
15+
strict_typing: true
1516
pypi: true

template/pyproject.toml.jinja

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ name = "{{ author_name }}"
5151
[tool.setuptools_scm]
5252
version_file = "src/{{ package_name }}/_version.py"
5353
{% if type_checker=="pyright" %}
54-
[tool.pyright]
54+
[tool.pyright]{% if strict_typing %}
55+
typeCheckingMode = "strict"
56+
{% else %}
5557
typeCheckingMode = "standard"
56-
reportMissingImports = false # Ignore missing stubs in imported modules
58+
{% endif %}reportMissingImports = false # Ignore missing stubs in imported modules
5759
{% endif %}{% if type_checker=="mypy" %}
5860
[tool.mypy]
5961
ignore_missing_imports = true # Ignore missing stubs in imported modules

tests/test_example.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ def test_template_defaults(tmp_path: Path):
4848
copy_project(tmp_path)
4949
run = make_venv(tmp_path)
5050
container_doc = tmp_path / "docs" / "how-to" / "run-container.md"
51+
pyproject_toml = tmp_path / "pyproject.toml"
5152
assert container_doc.exists()
5253
catalog_info = tmp_path / "catalog-info.yaml"
5354
assert catalog_info.exists()
55+
assert 'typeCheckingMode = "strict"' in pyproject_toml.read_text()
5456
run("./venv/bin/tox -p")
5557
if not run_pipe("git tag --points-at HEAD"):
5658
# Only run linkcheck if not on a tag, as the CI might not have pushed
@@ -68,8 +70,16 @@ def test_template_with_extra_code_and_api_docs(tmp_path: Path):
6870
init = tmp_path / "src" / "python_copier_template_example" / "__init__.py"
6971
init.write_text(
7072
init.read_text().replace(
71-
"__all__ = [",
73+
"""
74+
from ._version import __version__
75+
76+
__all__ = [""",
7277
'''
78+
from python_copier_template_example import extra_pkg
79+
80+
from ._version import __version__
81+
82+
7383
class TopCls:
7484
"""A top level class."""
7585
@@ -213,21 +223,26 @@ def __init__(self):
213223
run("ruff check")
214224

215225

216-
def test_works_in_pyright_strict_mode(tmp_path: Path):
217-
copy_project(tmp_path)
226+
def test_pyright_works_in_standard_typing_mode(tmp_path: Path):
227+
copy_project(tmp_path, type_checker="pyright", strict_typing=False)
218228
pyproject_toml = tmp_path / "pyproject.toml"
219229

220-
# Enable strict mode
221-
run_pipe(
222-
'sed -i \'s|typeCheckingMode = "standard"|typeCheckingMode = "strict"|\''
223-
f" {pyproject_toml}"
224-
)
230+
# Check standard mode is configured
231+
assert 'typeCheckingMode = "standard"' in pyproject_toml.read_text()
225232

226233
# Ensure pyright is still happy
227234
run = make_venv(tmp_path)
228235
run(f"./venv/bin/pyright {tmp_path}")
229236

230237

238+
def test_ignores_mypy_strict_mode(tmp_path: Path):
239+
copy_project(tmp_path, type_checker="mypy", strict_typing=True)
240+
pyproject_toml = tmp_path / "pyproject.toml"
241+
242+
# Check strict mode is not configured
243+
assert "typeCheckingMode =" not in pyproject_toml.read_text()
244+
245+
231246
def test_works_with_pydocstyle(tmp_path: Path):
232247
copy_project(tmp_path)
233248
pyproject_toml = tmp_path / "pyproject.toml"

0 commit comments

Comments
 (0)