Skip to content

Linting

Chris Bunney edited this page Nov 21, 2024 · 10 revisions

Lint (software) - Wikipedia

The following applies to all SciTools repositories except Cartopy, which has its own independent team of awesome developers.

Which linters

We mainly use the Scientific Python Library Development Guide to dictate which linting tools check our repositories. Our 'compliance' is maintained by using the automated sp-repo-review tool, as a pre-commit hook.

We sometimes alse choose linting tools that are not covered by Scientific Python. To ensure a consistent developer experience across repositories, we record our standard list of linting tools in a template .pre-commit-config.yaml, which all repos are expected to align with as closely as possible.

Which rules

The following linting tools categorise their rules, which allows use to easily choose which types of rules we want to opt-in or opt-out:

We try to apply as many of the rules as possible - both the default one and optional extra ones - then opt-out of any specific rules that cannot be applied. Opt-out reasons that are shared across all our repositories are recorded in the section below.

All of this - selecting all rules AND opting-out of specific ones - is achieved via each repository's pyproject.toml file, which includes sections to configure each of the above tools.

How to apply all rules

Repo-review (SP)

All rules are run by default.

Ruff

[tool.ruff.lint]
select = [
    "ALL",
    # Note: the above "ALL" disables conflicting rules; if you want to enable a
    # specific rule that is skipped then it needs to be explicitly listed below:
    "D212",  # Multi-line docstring summary should start at the first line
]

MyPy

[tool.mypy]
strict = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true

Numpydoc

[tool.numpydoc_validation]
checks = [
    "all",  # run all tests.
     # Any rule following "all" will be excluded from checks
]

Rules that we opt-out for all repositories

Please summarise any useful team discussions when listing each of these rules.

Repo-review (SP)

  • PC180 - not possible to run on the hardware used by the majority of our developers. Might change in future!

Ruff

MyPy

TBD

Numpydoc

  • GL01 - a chosen house-style
  • GL02 - a chosen house-style
  • GL03 - we benefit from more flexibility in formatting
  • SA01 - not all our docstrings need to be this detailed
  • ES01 - not all our docstrings need to be this detailed
  • EX01 - not all our docstrings need to be this detailed
  • YD01 - not all our operations are appropriate for mention of yield

Temporary opt-out rules

Should be separated from the permanent opt-outs with a # TODO: comment. E.g.

[tool.ruff.lint]
ignore = [
    "AAA111",
    "BBB222",

    # TODO: exceptions that still need investigating are below. Might be fixable, or might become permanent (above):
    "YYY999",
    "ZZZ000",
    ]

Targeted opt-outs

If you consider a rule to not apply for a specific file, or a specific line in a file, you can usually ignore that rule on a more granular basis rather than opting out completely; this will ensure that any new code added will still have the linting rule applied to it.

This can be done on a per file basis, for example in Ruff:

[tool.ruff.lint.per-file-ignores]
"cf_units/tests/*.py" = [
     "N999",  # Invalid module name

With Ruff, this can also be done for specific lines in a file, e.g.:

@property
def file_element(self):
    # noqa D102   # <-- allow undocumented public method
    return self._file_element

Comments

Please use comments to give as much info as possible to your fellow developers. E.g.

[tool.ruff.lint]
ignore = [
    # flake8-commas (COM)
    # https://docs.astral.sh/ruff/rules/#flake8-commas-com
    "COM812",  # Trailing comma missing.
    "COM819",  # Trailing comma prohibited.

    # flake8-implicit-str-concat (ISC)
    # https://docs.astral.sh/ruff/rules/single-line-implicit-string-concatenation/
    # NOTE: This rule may cause conflicts when used with "ruff format".
    "ISC001",  # Implicitly concatenate string literals on one line.
    ]

Other standard linter config

By linter

Ruff

[tool.ruff]
line-length = 88

[tool.ruff.format]
preview = false

[tool.ruff.lint]
preview = false

[tool.ruff.lint.isort]
force-sort-within-sections = true
# Change to match specific package name:
known-first-party = ["iris"]

[tool.ruff.lint.per-file-ignores]
# All test scripts

# Change to match specific package path:
"lib/iris/tests/*.py" = [
    # https://docs.astral.sh/ruff/rules/undocumented-public-module/
    "D100",  # Missing docstring in public module
    "D205",  # 1 blank line required between summary line and description
    "D401",  # 1 First line of docstring should be in imperative mood
]

[tool.ruff.lint.pydocstyle]
convention = "numpy"

MyPy

TBD

Numpydoc

[tool.numpydoc_validation]
exclude = [
    '\.__eq__$',
    '\.__ne__$',
    '\.__repr__$',
]