Skip to content

Commit 7289aa8

Browse files
committed
ruff: Setup more aggressive linting
Already caught some mistakes. As is usual for my "modern" python projects, I intend to setup the config here then propagate it elsewhere.
1 parent bffbeb7 commit 7289aa8

File tree

4 files changed

+97
-8
lines changed

4 files changed

+97
-8
lines changed

pyproject.toml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,89 @@ write_to = "src/techcable/orderedset/_version.py"
5454

5555
[tool.ruff]
5656
line-length = 120
57+
exclude = [
58+
# this is in .gitignore - but for some reason its still triggering `UP` lints
59+
"src/techcable/orderedset/_version.py",
60+
]
61+
62+
[tool.ruff.lint]
63+
preview = true
64+
extend-select = [
65+
"RUF", # ruff
66+
"B", # flake8-bugbear
67+
# probable bugs
68+
"DTZ", # flake8-datetimez - timezone issues
69+
"EXE", # issues with shebang
70+
"RET", # return statements
71+
"SLOT", # require __slots__ for subclasses of immutable types
72+
"T10", # use of breakpoint()
73+
"FBT", # boolean flags should not be positional args
74+
# possible bugs
75+
"BLE", # prevent over-broad `catch` statements
76+
"PIE", # flake8-pie - misc lints
77+
"PYI", # lint pyi files
78+
"PLE", # pylint[error]
79+
"PLR", # pylint[refactor]
80+
"LOG", # flake8-logging
81+
"PT", # flake8-pytest-style
82+
"ASYNC", # flake8-async
83+
# lints for legacy code
84+
"UP", # pyupgrade
85+
"YTT", # flake8-2020
86+
"C4", # flake8-comprehensions
87+
"FA", # flake-future-annotations
88+
"PLR", # pylint[refactor]
89+
"FURB", # refurb
90+
# style
91+
"Q002", # avoid single-quote docstrings
92+
"Q003", # change outer quotes to avoid escaping inner quotes
93+
"Q004", # unecessary quote escape
94+
"TD", # checks todo statements are well-formed
95+
"PTH", # prefer use of pathlib.Path
96+
"FLY", # prefer fstring to str.json
97+
"W", # pycodestyle
98+
"G", # flake8-logging-format
99+
"N", # pep8-naming
100+
# pedantic
101+
"SIM", # flake8-simplify
102+
"A", # falke8-builtins - prevent shadowing builtins
103+
"T20", # flake8-print - prevent use of `print` statement
104+
"TC", # flake8-type-checking - guard imports needed only for type-checking
105+
"TID252", # forbid relative imports
106+
# "D", # pydocstyle - require docstrings to be well-written
107+
"PLW", # pylint[warning]
108+
"PLC", # pylint[convention]
109+
"F", # pyflakes
110+
"TRY", # tryceratops
111+
"ERA", # eradicate - forbid commented out code
112+
# "FIX", # prevent use of todo comments
113+
# buggy lints
114+
# "SLF", # flake8-self - doesn't consider isinstance
115+
# "CPY", # flake8-copyright - require copyright header - doesn't support SPDX tags
116+
]
117+
ignore = [
118+
# moderately pedantic
119+
# "E741", # ambiguous variable names
120+
"PLW3201", # allow dunder methods with 'no special meaning'
121+
# extremely pedantic
122+
"PLR2004", # allow use of 'magic values' - `if x == 3` is fine
123+
"PYI025", # collections.abc.Set is not confusing with `set`
124+
"UP015", # allow passing redundant modes to `open()` - explicit is better than implicit
125+
"TRY003", # allow long error messages 'outside of exception class'
126+
"SIM105", # allow try/except/pass instead of contextlib.suppress
127+
"RET505", # use of return statement in `if` does not make `else` unecessary
128+
"PLR0904", # too many public methods
129+
]
130+
131+
[tool.ruff.lint.pydocstyle]
132+
convention = "pep257"
133+
134+
[tool.ruff.lint.per-file-ignores]
135+
"tests/**" = [
136+
# excessively pedantic
137+
"D", # pydocstyle
138+
"E741", # ambigous variable names
139+
"TC", # flake8-type-checking - import guards are useless here
140+
# moderately pedantic
141+
"CPY", # test files don't need copyright
142+
]

src/techcable/orderedset/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
from ._version import __version__
33

44
__all__ = (
5-
"__version__",
65
"OrderedSet",
6+
"__version__",
77
)

src/techcable/orderedset/_orderedset.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
Sequence,
1313
Set,
1414
)
15-
from typing import TYPE_CHECKING, Any, Optional, TypeVar, overload
15+
from typing import TYPE_CHECKING, Any, TypeVar, overload
1616

1717
if TYPE_CHECKING:
1818
from pydantic import GetCoreSchemaHandler
1919
from pydantic_core import core_schema
2020
from typing_extensions import Protocol, get_args, override
2121

22-
class Comparable(Protocol):
22+
class Comparable(Protocol): # noqa: PLW1641 - do not require __hash__ method
2323
def __lt__(self, other: Comparable) -> bool:
2424
pass
2525

@@ -59,7 +59,7 @@ class OrderedSet(MutableSet[T], Sequence[T]):
5959
This type cannot implement `MutableSequence` because OrderedSet.append
6060
ignores duplicate elements and returns a `bool` instead of `None`."""
6161

62-
__slots__ = ("_unique", "_elements")
62+
__slots__ = ("_elements", "_unique")
6363

6464
_unique: set[T]
6565
_elements: list[T]
@@ -181,6 +181,9 @@ def __eq__(self, other: object) -> bool:
181181
else:
182182
return NotImplemented
183183

184+
__hash__ = None # type: ignore
185+
"""Since an OrderedSet is mutable, it cannot be hashed"""
186+
184187
def _impl_cmp_op(self, other: object, op: Callable[[Any, Any], bool]) -> bool:
185188
if isinstance(other, OrderedSet):
186189
return op(self._unique, other._unique)
@@ -263,7 +266,7 @@ def __setstate__(self, state: Any) -> None:
263266
"""Restores the elements of an OrderedSet from the pickled representation."""
264267
# init variables
265268
self._unique = set()
266-
self._elements = list()
269+
self._elements = []
267270
# deserialize `state` - a poor man's `match`
268271
elements: list[T]
269272
if isinstance(state, list):
@@ -283,7 +286,7 @@ def __setstate__(self, state: Any) -> None:
283286
self.update(elements)
284287

285288
@classmethod
286-
def dedup(self, source: Iterable[T], /) -> Generator[T]:
289+
def dedup(cls, source: Iterable[T], /) -> Generator[T]:
287290
"""A utility method to deduplicate the specified iterable,
288291
while preserving the original order.
289292
@@ -299,7 +302,7 @@ def dedup(self, source: Iterable[T], /) -> Generator[T]:
299302
yield item
300303

301304
@classmethod
302-
async def dedup_async(self, source: AsyncIterable[T], /) -> AsyncGenerator[T]:
305+
async def dedup_async(cls, source: AsyncIterable[T], /) -> AsyncGenerator[T]:
303306
"""A utility method to deduplicate the specified iterable,
304307
while preserving the original order.
305308

tests/test_simple.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ async def test_async_dedup():
6161
for example in EXAMPLE_DATA:
6262
async_counter = 0
6363

64-
async def increment_counter() -> None:
64+
async def increment_counter() -> None: # noqa: RUF029 - absence of `await` is intentional
6565
nonlocal async_counter
6666
async_counter += 1
6767

0 commit comments

Comments
 (0)