Skip to content

Various ruff fixes #12821

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

Merged
merged 1 commit into from
Jul 5, 2025
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@
* [Present Value](financial/present_value.py)
* [Price Plus Tax](financial/price_plus_tax.py)
* [Simple Moving Average](financial/simple_moving_average.py)
* [Straight Line Depreciation](financial/straight_line_depreciation.py)
* [Time And Half Pay](financial/time_and_half_pay.py)

## Fractals
Expand Down Expand Up @@ -790,6 +791,7 @@
* [Sumset](maths/sumset.py)
* [Sylvester Sequence](maths/sylvester_sequence.py)
* [Tanh](maths/tanh.py)
* [Test Factorial](maths/test_factorial.py)
* [Test Prime Check](maths/test_prime_check.py)
* [Three Sum](maths/three_sum.py)
* [Trapezoidal Rule](maths/trapezoidal_rule.py)
Expand Down
2 changes: 1 addition & 1 deletion ciphers/hill_cipher.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def replace_digits(self, num: int) -> str:
>>> hill_cipher.replace_digits(26)
'0'
"""
return self.key_string[round(num)]
return self.key_string[(num)]
Copy link
Member Author

@cclauss cclauss Jul 5, 2025

Choose a reason for hiding this comment

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

@dhruvmanila I do not understand this change requested by ruff. Type hints are suggestions, not mandatory in Python. Just because the type hint says num: int, that does not guarantee that the caller will not pass in 17.23. This call to round() prevents breakage.

Copy link
Member

Choose a reason for hiding this comment

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

I think this is correct and is https://docs.astral.sh/ruff/rules/unnecessary-round/. The num is an int so it doesn't require a round call. If you expect num to be a float as well then the type hint should be updated to int | float which is basically float.

Copy link
Member Author

@cclauss cclauss Jul 7, 2025

Choose a reason for hiding this comment

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

This misses the point. The type hint is a suggestion in Python, not an obligation. The caller may choose to send a float even though I have suggested they send an int. Without the round() call, the function will crash. With the round() call, the function will succeed. This rule is asking me not to drive defensively.

The workaround for ruff rule RUF057 is to use int(num) instead of round(num):

Copy link
Member

Choose a reason for hiding this comment

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

I don't think that's correct. From a static analysis perspective, type hints are part of the function contract that says that the num parameter here should be an int and if the caller passes a float then that's an error. So, if the function is expecting to receive a float or an int, then the contract should be updated instead.

Copy link
Member Author

Choose a reason for hiding this comment

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

The num parameter here should be an int. Python will not prevent the caller from breaking that rule.

Using int(num) instead of round(num) ensures the function remains well-behaved in the face of garbage input.

Copy link
Member

Choose a reason for hiding this comment

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

I see. I think that depends on whether this specific function is part of the public API for this module or not. If it is, then one way to perform error handling would be to use something like:

if not isinstance(num, int):
    raise TypeError("expected an integer, but got ...")

And, if it's not part of the public API, then we shouldn't do this and instead use a type checker.


def check_determinant(self) -> None:
"""
Expand Down
4 changes: 2 additions & 2 deletions data_structures/binary_tree/non_recursive_segment_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@
from __future__ import annotations

from collections.abc import Callable
from typing import Any, Generic, TypeVar
from typing import Any, TypeVar

T = TypeVar("T")


class SegmentTree(Generic[T]):
class SegmentTree[T]:
def __init__(self, arr: list[T], fnc: Callable[[T, T], T]) -> None:
"""
Segment Tree constructor, it works just with commutative combiner.
Expand Down
4 changes: 2 additions & 2 deletions data_structures/hashing/hash_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

from collections.abc import Iterator, MutableMapping
from dataclasses import dataclass
from typing import Generic, TypeVar
from typing import TypeVar

KEY = TypeVar("KEY")
VAL = TypeVar("VAL")


@dataclass(slots=True)
class _Item(Generic[KEY, VAL]):
class _Item[KEY, VAL]:
key: KEY
val: VAL

Expand Down
4 changes: 2 additions & 2 deletions data_structures/heap/heap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from abc import abstractmethod
from collections.abc import Iterable
from typing import Generic, Protocol, TypeVar
from typing import Protocol, TypeVar


class Comparable(Protocol):
Expand All @@ -22,7 +22,7 @@ def __eq__(self: T, other: object) -> bool:
T = TypeVar("T", bound=Comparable)


class Heap(Generic[T]):
class Heap[T: Comparable]:
"""A Max Heap Implementation

>>> unsorted = [103, 9, 1, 7, 11, 15, 25, 201, 209, 107, 5]
Expand Down
6 changes: 3 additions & 3 deletions data_structures/heap/randomized_heap.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

import random
from collections.abc import Iterable
from typing import Any, Generic, TypeVar
from typing import Any, TypeVar

T = TypeVar("T", bound=bool)


class RandomizedHeapNode(Generic[T]):
class RandomizedHeapNode[T: bool]:
"""
One node of the randomized heap. Contains the value and references to
two children.
Expand Down Expand Up @@ -73,7 +73,7 @@ def merge(
return root1


class RandomizedHeap(Generic[T]):
class RandomizedHeap[T: bool]:
"""
A data structure that allows inserting a new value and to pop the smallest
values. Both operations take O(logN) time where N is the size of the
Expand Down
6 changes: 3 additions & 3 deletions data_structures/heap/skew_heap.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from __future__ import annotations

from collections.abc import Iterable, Iterator
from typing import Any, Generic, TypeVar
from typing import Any, TypeVar

T = TypeVar("T", bound=bool)


class SkewNode(Generic[T]):
class SkewNode[T: bool]:
"""
One node of the skew heap. Contains the value and references to
two children.
Expand Down Expand Up @@ -87,7 +87,7 @@ def merge(
return result


class SkewHeap(Generic[T]):
class SkewHeap[T: bool]:
"""
A data structure that allows inserting a new value and to pop the smallest
values. Both operations take O(logN) time where N is the size of the
Expand Down
6 changes: 3 additions & 3 deletions data_structures/linked_list/skip_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

from itertools import pairwise
from random import random
from typing import Generic, TypeVar
from typing import TypeVar

KT = TypeVar("KT")
VT = TypeVar("VT")


class Node(Generic[KT, VT]):
class Node[KT, VT]:
def __init__(self, key: KT | str = "root", value: VT | None = None):
self.key = key
self.value = value
Expand Down Expand Up @@ -49,7 +49,7 @@ def level(self) -> int:
return len(self.forward)


class SkipList(Generic[KT, VT]):
class SkipList[KT, VT]:
def __init__(self, p: float = 0.5, max_level: int = 16):
self.head: Node[KT, VT] = Node[KT, VT]()
self.level = 0
Expand Down
15 changes: 6 additions & 9 deletions data_structures/queues/queue_by_list.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
"""Queue represented by a Python list"""

from collections.abc import Iterable
from typing import Generic, TypeVar

_T = TypeVar("_T")


class QueueByList(Generic[_T]):
def __init__(self, iterable: Iterable[_T] | None = None) -> None:
class QueueByList[T]:
def __init__(self, iterable: Iterable[T] | None = None) -> None:
"""
>>> QueueByList()
Queue(())
Expand All @@ -16,7 +13,7 @@ def __init__(self, iterable: Iterable[_T] | None = None) -> None:
>>> QueueByList((i**2 for i in range(1, 4)))
Queue((1, 4, 9))
"""
self.entries: list[_T] = list(iterable or [])
self.entries: list[T] = list(iterable or [])

def __len__(self) -> int:
"""
Expand Down Expand Up @@ -58,7 +55,7 @@ def __repr__(self) -> str:

return f"Queue({tuple(self.entries)})"

def put(self, item: _T) -> None:
def put(self, item: T) -> None:
"""Put `item` to the Queue

>>> queue = QueueByList()
Expand All @@ -72,7 +69,7 @@ def put(self, item: _T) -> None:

self.entries.append(item)

def get(self) -> _T:
def get(self) -> T:
"""
Get `item` from the Queue

Expand Down Expand Up @@ -118,7 +115,7 @@ def rotate(self, rotation: int) -> None:
for _ in range(rotation):
put(get(0))

def get_front(self) -> _T:
def get_front(self) -> T:
"""Get the front item from the Queue

>>> queue = QueueByList((10, 20, 30))
Expand Down
15 changes: 6 additions & 9 deletions data_structures/queues/queue_by_two_stacks.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
"""Queue implementation using two stacks"""

from collections.abc import Iterable
from typing import Generic, TypeVar

_T = TypeVar("_T")


class QueueByTwoStacks(Generic[_T]):
def __init__(self, iterable: Iterable[_T] | None = None) -> None:
class QueueByTwoStacks[T]:
def __init__(self, iterable: Iterable[T] | None = None) -> None:
"""
>>> QueueByTwoStacks()
Queue(())
Expand All @@ -16,8 +13,8 @@ def __init__(self, iterable: Iterable[_T] | None = None) -> None:
>>> QueueByTwoStacks((i**2 for i in range(1, 4)))
Queue((1, 4, 9))
"""
self._stack1: list[_T] = list(iterable or [])
self._stack2: list[_T] = []
self._stack1: list[T] = list(iterable or [])
self._stack2: list[T] = []

def __len__(self) -> int:
"""
Expand Down Expand Up @@ -59,7 +56,7 @@ def __repr__(self) -> str:
"""
return f"Queue({tuple(self._stack2[::-1] + self._stack1)})"

def put(self, item: _T) -> None:
def put(self, item: T) -> None:
"""
Put `item` into the Queue

Expand All @@ -74,7 +71,7 @@ def put(self, item: _T) -> None:

self._stack1.append(item)

def get(self) -> _T:
def get(self) -> T:
"""
Get `item` from the Queue

Expand Down
4 changes: 2 additions & 2 deletions data_structures/stacks/stack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import Generic, TypeVar
from typing import TypeVar

T = TypeVar("T")

Expand All @@ -13,7 +13,7 @@ class StackUnderflowError(BaseException):
pass


class Stack(Generic[T]):
class Stack[T]:
"""A stack is an abstract data type that serves as a collection of
elements with two principal operations: push() and pop(). push() adds an
element to the top of the stack, and pop() removes an element from the top
Expand Down
6 changes: 3 additions & 3 deletions data_structures/stacks/stack_with_doubly_linked_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@

from __future__ import annotations

from typing import Generic, TypeVar
from typing import TypeVar

T = TypeVar("T")


class Node(Generic[T]):
class Node[T]:
def __init__(self, data: T):
self.data = data # Assign data
self.next: Node[T] | None = None # Initialize next as null
self.prev: Node[T] | None = None # Initialize prev as null


class Stack(Generic[T]):
class Stack[T]:
"""
>>> stack = Stack()
>>> stack.is_empty()
Expand Down
6 changes: 3 additions & 3 deletions data_structures/stacks/stack_with_singly_linked_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from __future__ import annotations

from collections.abc import Iterator
from typing import Generic, TypeVar
from typing import TypeVar

T = TypeVar("T")


class Node(Generic[T]):
class Node[T]:
def __init__(self, data: T):
self.data = data
self.next: Node[T] | None = None
Expand All @@ -17,7 +17,7 @@ def __str__(self) -> str:
return f"{self.data}"


class LinkedStack(Generic[T]):
class LinkedStack[T]:
"""
Linked List Stack implementing push (to top),
pop (from top) and is_empty
Expand Down
4 changes: 2 additions & 2 deletions graphs/graph_adjacency_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
import random
import unittest
from pprint import pformat
from typing import Generic, TypeVar
from typing import TypeVar

import pytest

T = TypeVar("T")


class GraphAdjacencyList(Generic[T]):
class GraphAdjacencyList[T]:
def __init__(
self, vertices: list[T], edges: list[list[T]], directed: bool = True
) -> None:
Expand Down
4 changes: 2 additions & 2 deletions graphs/graph_adjacency_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
import random
import unittest
from pprint import pformat
from typing import Generic, TypeVar
from typing import TypeVar

import pytest

T = TypeVar("T")


class GraphAdjacencyMatrix(Generic[T]):
class GraphAdjacencyMatrix[T]:
def __init__(
self, vertices: list[T], edges: list[list[T]], directed: bool = True
) -> None:
Expand Down
4 changes: 2 additions & 2 deletions graphs/graph_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
from __future__ import annotations

from pprint import pformat
from typing import Generic, TypeVar
from typing import TypeVar

T = TypeVar("T")


class GraphAdjacencyList(Generic[T]):
class GraphAdjacencyList[T]:
"""
Adjacency List type Graph Data Structure that accounts for directed and undirected
Graphs. Initialize graph object indicating whether it's directed or undirected.
Expand Down
8 changes: 4 additions & 4 deletions graphs/minimum_spanning_tree_kruskal2.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
from __future__ import annotations

from typing import Generic, TypeVar
from typing import TypeVar

T = TypeVar("T")


class DisjointSetTreeNode(Generic[T]):
class DisjointSetTreeNode[T]:
# Disjoint Set Node to store the parent and rank
def __init__(self, data: T) -> None:
self.data = data
self.parent = self
self.rank = 0


class DisjointSetTree(Generic[T]):
class DisjointSetTree[T]:
# Disjoint Set DataStructure
def __init__(self) -> None:
# map from node name to the node object
Expand Down Expand Up @@ -46,7 +46,7 @@ def union(self, data1: T, data2: T) -> None:
self.link(self.find_set(data1), self.find_set(data2))


class GraphUndirectedWeighted(Generic[T]):
class GraphUndirectedWeighted[T]:
def __init__(self) -> None:
# connections: map from the node to the neighbouring nodes (with weights)
self.connections: dict[T, dict[T, int]] = {}
Expand Down
Loading