diff --git a/cookiecutter-django/{{cookiecutter.project_slug}}/pyproject.toml b/cookiecutter-django/{{cookiecutter.project_slug}}/pyproject.toml
index f82ed00..6ecfd60 100644
--- a/cookiecutter-django/{{cookiecutter.project_slug}}/pyproject.toml
+++ b/cookiecutter-django/{{cookiecutter.project_slug}}/pyproject.toml
@@ -9,29 +9,26 @@ target-version = "py311"
[tool.ruff.lint]
select = ["ALL"]
ignore = [
- "ANN", "COM", "EM",
- # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
- "W191", "D206", "Q000", "Q001", "Q002", "Q003", "ISC001",
- "D203", "D212", # ignore incompatible rules
- "D200", "D205", # documentation preferences
- "C901", "PLR091", # complexity preferences
- "ARG001", "ARG002", "DJ008", "RUF012", "SLF001", # Django
+ "ANN", "C901", "COM812", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000",
+ "D1",
+ "PTH",
]
[tool.ruff.lint.flake8-builtins]
builtins-ignorelist = ["copyright"]
+[tool.ruff.lint.flake8-unused-arguments]
+ignore-variadic-names = true
+
[tool.ruff.lint.per-file-ignores]
-"docs/conf.py" = ["INP001"] # no __init__.py file
+"docs/*" = ["D100", "INP001"]
+"{*/signals,*/views,*/migrations/*}.py" = ["ARG001"]
+"{*/admin,*/routers,*/views,*/commands/*}.py" = ["ARG002"]
+"{*/admin,*/forms,*/models,*/routers,*/migrations/*,tests/*}.py" = ["RUF012"]
"*/migrations/*" = ["E501"]
"tests/*" = [
- "D", # docstring
- "ARG001", # pytest fixtures
- "PLR0913", # Too many arguments
- "PLR2004", # Magic value used
- "PT", # Django
- "S101", # assert
+ "D", "FBT003", "INP001", "PLR2004", "PT", "S", "TRY003",
]
[tool.coverage.run]
-omit = ['*/migrations/*']
+omit = ["*/migrations/*"]
diff --git a/cookiecutter-pypackage/{{cookiecutter.repository_name}}/pyproject.toml b/cookiecutter-pypackage/{{cookiecutter.repository_name}}/pyproject.toml
index b90f677..fafa391 100644
--- a/cookiecutter-pypackage/{{cookiecutter.repository_name}}/pyproject.toml
+++ b/cookiecutter-pypackage/{{cookiecutter.repository_name}}/pyproject.toml
@@ -47,23 +47,17 @@ target-version = "py39"
[tool.ruff.lint]
select = ["ALL"]
ignore = [
- "ANN", "COM", "EM",
- # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
- "W191", "D206", "Q000", "Q001", "Q002", "Q003", "ISC001",
- "D203", "D212", # ignore incompatible rules
- "D200", "D205", # documentation preferences
- "C901", "PLR091", # complexity preferences
+ "ANN", "C901", "COM812", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000",
]
[tool.ruff.lint.flake8-builtins]
builtins-ignorelist = ["copyright"]
+[tool.ruff.lint.flake8-unused-arguments]
+ignore-variadic-names = true
+
[tool.ruff.lint.per-file-ignores]
-"docs/conf.py" = ["INP001"] # no __init__.py file
+"docs/conf.py" = ["D100", "INP001"]
"tests/*" = [
- "D", # docstring
- "ARG001", # pytest fixtures
- "PLR0913", # Too many arguments
- "PLR2004", # Magic value used
- "S101", # assert
+ "ARG001", "D", "FBT003", "INP001", "PLR2004", "S", "TRY003",
]
diff --git a/docs/conf.py b/docs/conf.py
index c669dbc..84f854b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -9,10 +9,7 @@
# 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 os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
+
# -- Project information -----------------------------------------------------
diff --git a/docs/python/documentation.rst b/docs/python/documentation.rst
index f30dded..a433cfa 100644
--- a/docs/python/documentation.rst
+++ b/docs/python/documentation.rst
@@ -30,7 +30,7 @@ Type hints are preferred to ``type``, ``rtype`` and ``vartype`` fields. Use `aut
.. seealso::
- :ref:`check-docstring-style`
+ :doc:`linting`
Build documentation locally
---------------------------
@@ -53,37 +53,6 @@ Open http://127.0.0.1:8000/ in your web browser.
Documentation is built in ``docs/_build/html``, like when building with ``make html`` from the ``docs/`` directory.
-.. _check-docstring-style:
-
-Check docstring style
----------------------
-
-Use `Ruff `__ in new :doc:`packages`.
-
-.. literalinclude:: samples/pyproject-ruff.toml
- :language: toml
-
-These error codes are ignored:
-
-D100 Missing docstring in public module
- Avoid generic docstrings for utility modules like ``util.py``.
-D104 Missing docstring in public package
- Document the package in Sphinx, not in ``mypackage/__init__.py``.
-D200 One-line docstring should fit on one line with quotes
- Allow one style for all docstrings. (Make diffs smaller if docstrings change.)
-D203 1 blank line required before class docstring
- Incompatible with D211 (No blank lines allowed before class docstring).
-D205 1 blank line required between summary line and description
- Allow summary line to be multiple lines, especially if it contains links or roles.
-D212 Multi-line docstring summary should start at the first line
- Incompatible with D213 (Multi-line docstring summary should start at the second line).
-D400 First line should end with a period
- See D205.
-D415 First line should end with a period, question mark, or exclamation point
- Duplicative with D400 (First line should end with a period).
-
-Reference: `PEP 257 `__: `One-line Docstrings `__, `Multi-line Docstrings `__
-
Check broken links
------------------
diff --git a/docs/python/linting.rst b/docs/python/linting.rst
index 6ae191f..dedc3b0 100644
--- a/docs/python/linting.rst
+++ b/docs/python/linting.rst
@@ -3,15 +3,57 @@ Linting
Before writing any code, set up formatters and linters.
+In general, add all project configuration to ``pyproject.toml``. Do not use ``setup.cfg``, ``setup.py``, ``.editorconfig`` or tool-specific files like ``.coveragerc`` or ``pytest.ini``.
+
Configuration
-------------
-New projects should use the `Ruff `__ formatter and linter, with line lengths of 119 (the Django coding style until 4.0), configured as follows:
+New projects should use the `Ruff `__ formatter and linter, with line lengths of 119 (the Django coding style until 4.0). A starting point, based on `script.sh in standard-maintenance-scripts `__:
.. literalinclude:: samples/pyproject-ruff.toml
:language: toml
-In general, add all configuration to ``pyproject.toml``. Do not use ``setup.cfg``, ``setup.py``, ``.editorconfig`` or tool-specific files like ``.coveragerc`` or ``pytest.ini``.
+With this starting point, check which rules fail:
+
+.. code-block:: bash
+
+ ruff check . --statistics
+
+And check individual failures, for example:
+
+.. code-block:: bash
+
+ ruff check . --select D400
+
+As general guidance:
+
+- Fix failures, if possible.
+- Use a ``# noqa: RULE`` comment if the failure is rare (for example, an ``S`` rule), or if it should be fixed, given more time. Add a short comment to explain the failure. For example: ``# noqa: S104 # Docker``
+- Use ``per-file-ignores`` if the failures occur in a single (or a set of) files. For example: ``"*/__main__.py" = ["T201"] # print``
+- Use ``ignore`` if the failures occur in disparate files and are expected to occur in new code. For example: ``"TRY003", # errors``
+- Use `settings `__ where possible, instead of ignoring rules entirely. Notably, use:
+
+ - `builtins-ignorelist `__, instead of A002
+ - `extend-immutable-calls `__, instead of B008
+ - `allowed-confusables `__, instead of RUF001
+ - `extend-ignore-names `__, instead of SLF001
+
+``isort:skip`` and ``type: ignore`` comments should be avoided, and should reference the specific error if used, to avoid shadowing another error: for example, ``# type: ignore[attr-defined]``.
+
+.. admonition:: Complexity rules
+
+ We ignore the ``C901`` and all ``PLR091`` rules.
+
+ Complexity is best measured by the effort required to read and modify code. This cannot be measured using techniques like `cyclomatic complexity `__. Reducing cyclomatic complexity typically means extracting single-caller methods and/or using object-oriented programming, which frequently *increases* cognitive complexity.
+
+ See the note under :ref:`create-products-sustainably`.
+
+..
+ Maintainers can check if docs/ and tests/ rules are included in projects without those directories
+
+ diff -u <(find . -type d -maxdepth 1 ! -name '*handbook' -exec test -f '{}'/pyproject.toml -a -d '{}'/docs \; -print | cut -d/ -f2 | sort) <(rg -c copyright */pyproject.toml | cut -d/ -f1 | sort)
+ diff -u <(find . -type d -maxdepth 1 ! -name '*handbook' -exec test -f '{}'/pyproject.toml -a -d '{}'/docs \; -print | cut -d/ -f2 | sort) <(rg -c docs/ */pyproject.toml | cut -d/ -f1 | sort)
+ diff -u <(find . -type d -maxdepth 1 -exec test -f '{}'/pyproject.toml -a \( -d '{}'/tests -o -d '{}'/backend/tests \) \; -print | cut -d/ -f2 | sort) <(rg -c tests/ */pyproject.toml | cut -d/ -f1 | sort)
.. _linting-pre-commit:
@@ -42,25 +84,6 @@ To avoid pushing commits that fail formatting or linting checks, new projects sh
`pre-commit/pre-commit-hooks `__ is not used in the templates, as the errors it covers are rarely encountered.
-Skipping linting
-----------------
-
-``noqa``, ``isort:skip`` and ``type: ignore`` comments should be avoided, and should reference the specific error if used, to avoid shadowing another error: for example, ``# noqa: E501`` or ``# type: ignore[attr-defined]``.
-
-The errors that are allowed to be ignored per line are:
-
-- ``E501 line too long`` for long strings
-- ``F401 module imported but unused`` in a library's top-level ``__init__.py`` file
-- ``E402 module level import not at top of file`` in a Django project's ``asgi.py`` file
-- ``W291 Trailing whitespace`` in tests relating to trailing whitespace
-- ``isort:skip`` if ``sys.path`` needs to be changed before an import
-
-Maintainers can find unwanted comments with this regular expression:
-
-.. code-block:: none
-
- # noqa(?!(: (E501|F401|E402|W291)| isort:skip)\n)
-
.. _linting-ci:
Continuous integration
@@ -73,10 +96,10 @@ Create a ``.github/workflows/lint.yml`` file. The :doc:`django` and :doc:`Pypack
- Workflow files for linting :ref:`shell scripts` and :ref:`Javascript files`
- `standard-maintenance-scripts `__ to learn about the Bash scripts
-.. _python-optional-linting:
+.. _python-additional-linting:
-Optional linting
-----------------
+Additional linting
+------------------
`codespell `__ finds typographical errors. It is especially useful in repositories with lengthy documentation. Otherwise, all repositories can be periodically checked with:
@@ -96,9 +119,3 @@ Optional linting
- specific third-party code (docson, htmlcov, schemaspy)
- non-code and non-documentation files
- codespell-covered repositories (european-union-support)
-
-.. admonition:: Complexity rules
-
- Complexity is best measured by the effort required to read and modify code. This cannot be measured using techniques like `cyclomatic complexity `__. Reducing cyclomatic complexity typically means extracting single-caller methods and/or using object-oriented programming, which frequently *increases* cognitive complexity.
-
- See the note under :ref:`create-products-sustainably`.
diff --git a/docs/python/samples/pyproject-ruff.toml b/docs/python/samples/pyproject-ruff.toml
index bbd1a49..3d2bcd4 100644
--- a/docs/python/samples/pyproject-ruff.toml
+++ b/docs/python/samples/pyproject-ruff.toml
@@ -3,4 +3,20 @@ line-length = 119
target-version = "py311"
[tool.ruff.lint]
-select = ["E", "C4", "F", "I", "W"]
+select = ["ALL"]
+ignore = [
+ "ANN", "C901", "COM812", "D203", "D212", "D415", "EM", "ISC001", "PERF203", "PLR091", "Q000",
+ "D1",
+]
+
+[tool.ruff.lint.flake8-builtins]
+builtins-ignorelist = ["copyright"]
+
+[tool.ruff.lint.flake8-unused-arguments]
+ignore-variadic-names = true
+
+[tool.ruff.lint.per-file-ignores]
+"docs/conf.py" = ["D100", "INP001"]
+"tests/*" = [
+ "ARG001", "D", "FBT003", "INP001", "PLR2004", "S", "TRY003",
+]
diff --git a/docs/python/style_guide.rst b/docs/python/style_guide.rst
index ff4d4d8..79a9b99 100644
--- a/docs/python/style_guide.rst
+++ b/docs/python/style_guide.rst
@@ -7,7 +7,7 @@ You may also refer to common guidance like the `Google Python Style Guide `
+ :ref:`Organization-wide spell-checking`
Built-in functions
------------------