Skip to content

Saner linters #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -35,11 +35,8 @@ jobs:
pixi-version: v0.39.0
cache: true
environments: lint
- name: Run Pylint, Mypy & Pyright
run: |
pixi run -e lint pylint
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant and incompatible with ruff

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pylint is also included in ruff, so why would it be incompatible?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are many pylint rules that ruff hasn't implemented: astral-sh/ruff#970

Copy link
Contributor Author

@crusaderky crusaderky Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple example was

class at:

pylint complains that classes need to be capitalized;
but if you add a # noqa to silence pylint, then ruff fails with an "unnecessary noqa" error

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the way to silence pylint is # pylint: disable=invalid-name

pixi run -e lint mypy
pixi run -e lint pyright
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't have pyright AND mypy in the same project. They will eventually result conflicting with each other, and in fact they did so heavily in #53.
Chose mypy as I found pyright extremely obnoxious and factually wrong in too many cases.

Copy link

@jorenham jorenham Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't have pyright AND mypy in the same project. They will eventually result conflicting with each other, and in fact they did so heavily in #53.

This isn't true at all, and all of my typed projects are proof of this.
In my experience, there are indeed situations where mypy and pyright behave differently. In at least 99% of these cases, this is caused by one of the >1200 confirmed bugs in mypy that aren't present in pyright: https://github.com/erictraut/mypy_issues

Chose mypy as I found pyright extremely obnoxious and factually wrong in too many cases.

Do you have an example of this?

- name: Run Mypy
run: pixi run -e lint mypy

checks:
name: Check ${{ matrix.environment }}
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ repos:
additional_dependencies: [black==24.*]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v4.6.0"
rev: "v5.0.0"
hooks:
- id: check-added-large-files
- id: check-case-conflict
@@ -35,14 +35,14 @@ repos:
- id: rst-inline-touching-normal

- repo: https://github.com/rbubley/mirrors-prettier
rev: "v3.3.3"
rev: "v3.4.2"
hooks:
- id: prettier
types_or: [yaml, markdown, html, css, scss, javascript, json]
args: [--prose-wrap=always]

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.6.1"
rev: "v0.8.2"
hooks:
- id: ruff
args: ["--fix", "--show-fixes"]
@@ -68,13 +68,13 @@ repos:
exclude: .pre-commit-config.yaml

- repo: https://github.com/abravalheri/validate-pyproject
rev: "v0.19"
rev: "v0.23"
hooks:
- id: validate-pyproject
additional_dependencies: ["validate-pyproject-schema-store[all]"]

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: "0.29.1"
rev: "0.30.0"
hooks:
- id: check-dependabot
- id: check-github-workflows
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>
</svg>
""",
""", # noqa: E501
"class": "",
},
],
2 changes: 0 additions & 2 deletions docs/contributing.md
Original file line number Diff line number Diff line change
@@ -77,9 +77,7 @@ pixi run ipython

```
pixi run pre-commit
pixi run pylint
pixi run mypy
pixi run pyright
```

Alternative environments are available with a subset of the dependencies and
503 changes: 134 additions & 369 deletions pixi.lock

Large diffs are not rendered by default.

71 changes: 25 additions & 46 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -70,9 +70,7 @@ array-api-extra = { path = ".", editable = true }

[tool.pixi.feature.lint.dependencies]
pre-commit = "*"
pylint = "*"
basedmypy = "*"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced basedmypy with mypy. I think it is an extremely bad idea to diverge from PEPs. Namely, IDE plugins will not support this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is untrue. All mypy IDE plugins and other CI tools also work with basedmypy.

In fact, mypy diverges more from the typing specification that basedmypy does. It also has significantly more bugs.

basedpyright = "*"
mypy = "*"
typing_extensions = ">=4.12.2,<4.13"
# import dependencies for mypy:
array-api-strict = "*"
@@ -81,11 +79,9 @@ pytest = "*"

[tool.pixi.feature.lint.tasks]
pre-commit-install = { cmd = "pre-commit install" }
pre-commit = { cmd = "pre-commit run -v --all-files --show-diff-on-failure" }
pre-commit = { cmd = "pre-commit run --all-files" }
mypy = { cmd = "mypy", cwd = "." }
pylint = { cmd = ["pylint", "array_api_extra"], cwd = "src" }
pyright = { cmd = "basedpyright", cwd = "." }
lint = { depends-on = ["pre-commit", "pylint", "mypy", "pyright"] }
lint = { depends-on = ["pre-commit", "mypy"] }

[tool.pixi.feature.tests.dependencies]
pytest = ">=6"
@@ -154,42 +150,34 @@ run.source = ["array_api_extra"]
report.exclude_also = [
'\.\.\.',
'if typing.TYPE_CHECKING:',
'if TYPE_CHECKING:',
]


# mypy

[tool.mypy]
files = ["src", "tests"]
files = ["src", "tests", "vendor_tests"]
python_version = "3.10"
warn_unused_configs = true
strict = true
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true
disallow_untyped_defs = false
disallow_incomplete_defs = false
# data-apis/array-api#589
disallow_any_expr = false
disallow_incomplete_defs = false
disallow_untyped_defs = false
disallow_untyped_decorators = true
ignore_missing_imports = true
no_implicit_optional = true
show_error_codes = true
warn_redundant_casts = true
warn_unused_configs = true
warn_unused_ignores = true
warn_unreachable = true


[[tool.mypy.overrides]]
module = "array_api_extra.*"
disallow_untyped_defs = true
disallow_incomplete_defs = true


# pyright

[tool.basedpyright]
include = ["src", "tests"]
pythonVersion = "3.10"
pythonPlatform = "All"
typeCheckingMode = "all"

# data-apis/array-api#589
reportAny = false
reportExplicitAny = false
# data-apis/array-api-strict#6
reportUnknownMemberType = false
disallow_untyped_defs = true


# Ruff
@@ -200,11 +188,16 @@ target-version = "py310"
[tool.ruff.lint]
extend-select = [
"B", # flake8-bugbear
"F", # Pyflakes
"I", # isort
"E", # Pycodestyle
"W", # Pycodestyle
"N", # pep8-naming
"ARG", # flake8-unused-arguments
"C4", # flake8-comprehensions
"EM", # flake8-errmsg
"ICN", # flake8-import-conventions
"ISC", # flake8-implicit-str-concat
"G", # flake8-logging-format
"PGH", # pygrep-hooks
"PIE", # flake8-pie
@@ -220,29 +213,15 @@ extend-select = [
"EXE", # flake8-executable
"NPY", # NumPy specific rules
"PD", # pandas-vet
"UP", # Pyupgrade
]
ignore = [
"PLR09", # Too many <...>
"PLR2004", # Magic value used in comparison
"ISC001", # Conflicts with formatter
"N802", # Function name should be lowercase
"N806", # Variable in function should be lowercase
]

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["T20"]


# Pylint

[tool.pylint]
py-version = "3.10"
ignore-paths = [".*/_version.py"]
reports.output-format = "colorized"
similarities.ignore-imports = "yes"
messages_control.disable = [
"design",
"fixme",
"line-too-long",
"missing-module-docstring",
"missing-function-docstring",
"wrong-import-position",
]
3 changes: 1 addition & 2 deletions src/array_api_extra/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990
from __future__ import annotations

from ._funcs import atleast_nd, cov, create_diagonal, expand_dims, kron, setdiff1d, sinc

__version__ = "0.3.3.dev0"

# pylint: disable=duplicate-code
__all__ = [
"__version__",
"atleast_nd",
7 changes: 2 additions & 5 deletions src/array_api_extra/_funcs.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990
from __future__ import annotations

import typing
import warnings

if typing.TYPE_CHECKING:
from ._lib._typing import Array, ModuleType

from ._lib import _utils
from ._lib._compat import array_namespace
from ._lib._typing import Array, ModuleType

__all__ = [
"atleast_nd",
10 changes: 5 additions & 5 deletions src/array_api_extra/_lib/_compat.py
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@
from __future__ import annotations

try:
from ..._array_api_compat_vendor import ( # pyright: ignore[reportMissingImports]
array_namespace, # pyright: ignore[reportUnknownVariableType]
device, # pyright: ignore[reportUnknownVariableType]
from ..._array_api_compat_vendor import (
array_namespace,
device,
)
except ImportError:
from array_api_compat import ( # pyright: ignore[reportMissingTypeStubs]
array_namespace, # pyright: ignore[reportUnknownVariableType]
from array_api_compat import (
array_namespace,
device,
)

22 changes: 5 additions & 17 deletions src/array_api_extra/_lib/_typing.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990
from __future__ import annotations

import typing
from types import ModuleType
from typing import Any

if typing.TYPE_CHECKING:
from typing_extensions import override
# To be changed to a Protocol later (see data-apis/array-api#589)
Array = Any
Device = Any

# To be changed to a Protocol later (see data-apis/array-api#589)
Array = Any # type: ignore[no-any-explicit]
Device = Any # type: ignore[no-any-explicit]
else:

def no_op_decorator(f): # pyright: ignore[reportUnreachable]
return f

override = no_op_decorator

__all__ = ["ModuleType", "override"]
if typing.TYPE_CHECKING:
__all__ += ["Array", "Device"]
__all__ = ["Array", "Device", "ModuleType"]
8 changes: 2 additions & 6 deletions src/array_api_extra/_lib/_utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990

import typing

if typing.TYPE_CHECKING:
from ._typing import Array, ModuleType
from __future__ import annotations

from . import _compat
from ._typing import Array, ModuleType

__all__ = ["in1d", "mean"]

9 changes: 3 additions & 6 deletions tests/test_funcs.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990
from __future__ import annotations

import contextlib
import typing
import warnings

# data-apis/array-api-strict#6
import array_api_strict as xp # type: ignore[import-untyped] # pyright: ignore[reportMissingTypeStubs]
import array_api_strict as xp
import numpy as np
import pytest
from numpy.testing import assert_allclose, assert_array_equal, assert_equal
@@ -19,9 +18,7 @@
setdiff1d,
sinc,
)

if typing.TYPE_CHECKING:
from array_api_extra._lib._typing import Array
from array_api_extra._lib._typing import Array


class TestAtLeastND:
10 changes: 3 additions & 7 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990

import typing
from __future__ import annotations

# data-apis/array-api-strict#6
import array_api_strict as xp # type: ignore[import-untyped] # pyright: ignore[reportMissingTypeStubs]
import array_api_strict as xp
import pytest
from numpy.testing import assert_array_equal

from array_api_extra._lib._typing import Array
from array_api_extra._lib._utils import in1d

if typing.TYPE_CHECKING:
from array_api_extra._lib._typing import Array


# some test coverage already provided by TestSetDiff1D
class TestIn1D:
2 changes: 1 addition & 1 deletion tests/test_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import annotations # https://github.com/pylint-dev/pylint/pull/9990
from __future__ import annotations

import importlib.metadata

2 changes: 1 addition & 1 deletion vendor_tests/test_vendor.py
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ def test_vendor_compat():
from ._array_api_compat_vendor import array_namespace

x = xp.asarray([1, 2, 3])
assert array_namespace(x) is xp
assert array_namespace(x) is xp # type: ignore[no-untyped-call]


def test_vendor_extra():