Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

Use ruff format correctly. #22

Merged
merged 1 commit into from
Feb 29, 2024
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
8 changes: 4 additions & 4 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
branches: [main]
pull_request:
paths:
- 'python/**'
- "python/**"
defaults:
run:
working-directory: python/selfie-lib
Expand All @@ -24,9 +24,9 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: 'python/selfie-lib/pyproject.toml'
cache: 'poetry'
python-version-file: "python/selfie-lib/pyproject.toml"
cache: "poetry"
- run: poetry install
- run: poetry run pytest -vv
- run: poetry run pyright
- run: poetry run ruff check
- run: poetry run ruff format --check
42 changes: 23 additions & 19 deletions python/selfie-lib/selfie_lib/ArrayMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,46 @@
from typing import List, TypeVar, Union
from abc import abstractmethod, ABC

T = TypeVar('T')
V = TypeVar('V')
K = TypeVar('K')
T = TypeVar("T")
V = TypeVar("V")
K = TypeVar("K")


class ListBackedSet(Set[T], ABC):
@abstractmethod
def __len__(self) -> int: ...
def __len__(self) -> int:
...

@abstractmethod
def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]: ...
def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]:
...

def __contains__(self, item: object) -> bool:
for i in range(len(self)):
if self[i] == item:
return True
return False


class ArraySet(ListBackedSet[K]):
__data: List[K]

def __init__(self, data: List[K]):
raise NotImplementedError("Use ArraySet.empty() instead")

@classmethod
def __create(cls, data: List[K]) -> 'ArraySet[K]':
def __create(cls, data: List[K]) -> "ArraySet[K]":
# Create a new instance without calling __init__
instance = super().__new__(cls)
instance.__data = data
return instance

def __iter__(self) -> Iterator[K]:
return iter(self.__data)

@classmethod
def empty(cls) -> 'ArraySet[K]':
if not hasattr(cls, '__EMPTY'):
def empty(cls) -> "ArraySet[K]":
if not hasattr(cls, "__EMPTY"):
cls.__EMPTY = cls([])
return cls.__EMPTY

Expand All @@ -52,14 +56,14 @@ def __getitem__(self, index: Union[int, slice]) -> Union[K, List[K]]:
else:
raise TypeError("Invalid argument type.")

def plusOrThis(self, element: K) -> 'ArraySet[K]':
def plusOrThis(self, element: K) -> "ArraySet[K]":
# TODO: use binary search, and also special sort order for strings
if element in self.__data:
return self
else:
new_data = self.__data[:]
new_data.append(element)
new_data.sort() # type: ignore[reportOperatorIssue]
new_data.sort() # type: ignore[reportOperatorIssue]
return ArraySet.__create(new_data)


Expand All @@ -69,8 +73,8 @@ def __init__(self, data: list):
self.__data = data

@classmethod
def empty(cls) -> 'ArrayMap[K, V]':
if not hasattr(cls, '__EMPTY'):
def empty(cls) -> "ArrayMap[K, V]":
if not hasattr(cls, "__EMPTY"):
cls.__EMPTY = cls([])
return cls.__EMPTY

Expand All @@ -86,7 +90,7 @@ def __iter__(self) -> Iterator[K]:
def __len__(self) -> int:
return len(self.__data) // 2

def __binary_search_key(self, key: K) -> int:
def __binary_search_key(self, key: K) -> int:
# TODO: special sort order for strings
low, high = 0, (len(self.__data) // 2) - 1
while low <= high:
Expand All @@ -100,20 +104,20 @@ def __binary_search_key(self, key: K) -> int:
return mid
return -(low + 1)

def plus(self, key: K, value: V) -> 'ArrayMap[K, V]':
index = self.__binary_search_key(key)
def plus(self, key: K, value: V) -> "ArrayMap[K, V]":
index = self.__binary_search_key(key)
if index >= 0:
raise ValueError("Key already exists")
insert_at = -(index + 1)
new_data = self.__data[:]
new_data[insert_at * 2:insert_at * 2] = [key, value]
new_data[insert_at * 2 : insert_at * 2] = [key, value]
return ArrayMap(new_data)

def minus_sorted_indices(self, indicesToRemove: List[int]) -> 'ArrayMap[K, V]':
def minus_sorted_indices(self, indicesToRemove: List[int]) -> "ArrayMap[K, V]":
if not indicesToRemove:
return self
newData = []
for i in range(0, len(self.__data), 2):
if i // 2 not in indicesToRemove:
newData.extend(self.__data[i:i + 2])
newData.extend(self.__data[i : i + 2])
return ArrayMap(newData)
49 changes: 30 additions & 19 deletions python/selfie-lib/selfie_lib/Slice.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
from typing import Optional
from typing import Union
from typing import Optional
from typing import Union
from collections import Counter


class Slice:
"""Represents a slice of a base string from startIndex to endIndex."""

def __init__(self, base: str, startIndex: int = 0, endIndex: Optional[int] = None) -> None:
def __init__(
self, base: str, startIndex: int = 0, endIndex: Optional[int] = None
) -> None:
self.base = base
self.base = base
self.startIndex = startIndex
self.endIndex = endIndex if endIndex is not None else len(base)

assert 0 <= self.startIndex <= self.endIndex <= len(base), "Invalid start or end index"

assert (
0 <= self.startIndex <= self.endIndex <= len(base)
), "Invalid start or end index"

def __len__(self) -> int:
return self.endIndex - self.startIndex

Expand All @@ -21,10 +26,10 @@ def __getitem__(self, index: int) -> str:
raise IndexError("Index out of range")
return self.base[self.startIndex + index]

def subSequence(self, start: int, end: int) -> 'Slice':
def subSequence(self, start: int, end: int) -> "Slice":
return Slice(self.base, self.startIndex + start, self.startIndex + end)

def trim(self) -> 'Slice':
def trim(self) -> "Slice":
start, end = 0, len(self)
while start < end and self[start].isspace():
start += 1
Expand All @@ -33,9 +38,9 @@ def trim(self) -> 'Slice':
return self.subSequence(start, end) if start > 0 or end < len(self) else self

def __str__(self) -> str:
return self.base[self.startIndex:self.endIndex]
return self.base[self.startIndex : self.endIndex]

def sameAs(self, other: Union['Slice', str]) -> bool:
def sameAs(self, other: Union["Slice", str]) -> bool:
if isinstance(other, Slice):
return str(self) == str(other)
elif isinstance(other, str):
Expand All @@ -48,18 +53,24 @@ def sameAs(self, other: Union['Slice', str]) -> bool:
return False

def indexOf(self, lookingFor: str, startOffset: int = 0) -> int:
result = self.base.find(lookingFor, self.startIndex + startOffset, self.endIndex)
result = self.base.find(
lookingFor, self.startIndex + startOffset, self.endIndex
)
return -1 if result == -1 else result - self.startIndex

def unixLine(self, count: int) -> 'Slice':
def unixLine(self, count: int) -> "Slice":
assert count > 0, "Count must be positive"
lineStart = 0
for i in range(1, count):
lineStart = self.indexOf('\n', lineStart)
lineStart = self.indexOf("\n", lineStart)
assert lineStart >= 0, f"This string has only {i - 1} lines, not {count}"
lineStart += 1
lineEnd = self.indexOf('\n', lineStart)
return Slice(self.base, self.startIndex + lineStart, self.endIndex if lineEnd == -1 else self.startIndex + lineEnd)
lineEnd = self.indexOf("\n", lineStart)
return Slice(
self.base,
self.startIndex + lineStart,
self.endIndex if lineEnd == -1 else self.startIndex + lineEnd,
)

def __eq__(self, other: object) -> bool:
if self is other:
Expand All @@ -75,10 +86,10 @@ def __hash__(self) -> int:
return h

def replaceSelfWith(self, s: str) -> str:
return self.base[:self.startIndex] + s + self.base[self.endIndex:]
return self.base[: self.startIndex] + s + self.base[self.endIndex :]

def count(self, char: str) -> int:
return Counter(self.base[self.startIndex:self.endIndex])[char]
return Counter(self.base[self.startIndex : self.endIndex])[char]

def baseLineAtOffset(self, index: int) -> int:
return 1 + Slice(self.base, 0, index).count('\n')
return 1 + Slice(self.base, 0, index).count("\n")
1 change: 0 additions & 1 deletion python/selfie-lib/selfie_lib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from .LineReader import LineReader as LineReader
from .Slice import Slice as Slice

32 changes: 27 additions & 5 deletions python/selfie-lib/tests/ArrayMap_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from selfie_lib.ArrayMap import ArrayMap


def assertEmpty(map):
assert len(map) == 0
assert list(map.keys()) == []
Expand All @@ -11,6 +12,7 @@ def assertEmpty(map):
assert map == {}
assert map == ArrayMap.empty()


def assertSingle(map, key, value):
assert len(map) == 1
assert set(map.keys()) == {key}
Expand All @@ -22,6 +24,7 @@ def assertSingle(map, key, value):
assert map == {key: value}
assert map == ArrayMap.empty().plus(key, value)


def assertDouble(map, key1, value1, key2, value2):
assert len(map) == 2
assert set(map.keys()) == {key1, key2}
Expand All @@ -36,6 +39,7 @@ def assertDouble(map, key1, value1, key2, value2):
assert map == ArrayMap.empty().plus(key1, value1).plus(key2, value2)
assert map == ArrayMap.empty().plus(key2, value2).plus(key1, value1)


def assertTriple(map, key1, value1, key2, value2, key3, value3):
assert len(map) == 3
assert set(map.keys()) == {key1, key2, key3}
Expand All @@ -47,17 +51,22 @@ def assertTriple(map, key1, value1, key2, value2, key3, value3):
with pytest.raises(KeyError):
_ = map[key1 + "blah"]
assert map == {key1: value1, key2: value2, key3: value3}
assert map == ArrayMap.empty().plus(key1, value1).plus(key2, value2).plus(key3, value3)
assert map == ArrayMap.empty().plus(key1, value1).plus(key2, value2).plus(
key3, value3
)


def test_empty():
assertEmpty(ArrayMap.empty())


def test_single():
empty = ArrayMap.empty()
single = empty.plus("one", "1")
assertEmpty(empty)
assertSingle(single, "one", "1")


def test_double():
empty = ArrayMap.empty()
single = empty.plus("one", "1")
Expand All @@ -71,19 +80,28 @@ def test_double():
single.plus("one", "2")
assert str(context.value) == "Key already exists"


def test_triple():
triple = ArrayMap.empty().plus("1", "one").plus("2", "two").plus("3", "three")
assertTriple(triple, "1", "one", "2", "two", "3", "three")


def test_multi():
test_triple() # Calling another test function directly is unusual but works
triple = ArrayMap.empty().plus("2", "two").plus("3", "three").plus("1", "one")
assertTriple(triple, "1", "one", "2", "two", "3", "three")
triple = ArrayMap.empty().plus("3", "three").plus("1", "one").plus("2", "two")
assertTriple(triple, "1", "one", "2", "two", "3", "three")


def test_minus_sorted_indices():
initial_map = ArrayMap.empty().plus("1", "one").plus("2", "two").plus("3", "three").plus("4", "four")
initial_map = (
ArrayMap.empty()
.plus("1", "one")
.plus("2", "two")
.plus("3", "three")
.plus("4", "four")
)
modified_map = initial_map.minus_sorted_indices([1, 3])
assert len(modified_map) == 2
assert list(modified_map.keys()) == ["1", "3"]
Expand All @@ -94,6 +112,7 @@ def test_minus_sorted_indices():
_ = modified_map["4"]
assert modified_map == {"1": "one", "3": "three"}


def test_plus_with_existing_keys():
map_with_duplicates = ArrayMap.empty().plus("a", "alpha").plus("b", "beta")
with pytest.raises(ValueError):
Expand All @@ -103,11 +122,14 @@ def test_plus_with_existing_keys():
assert updated_map["a"] == "alpha"
assert updated_map["b"] == "beta"
assert updated_map["c"] == "gamma"
modified_map = map_with_duplicates.minus_sorted_indices([0]).plus("a", "updated alpha")
modified_map = map_with_duplicates.minus_sorted_indices([0]).plus(
"a", "updated alpha"
)
assert len(modified_map) == 2
assert modified_map["a"] == "updated alpha"
assert modified_map["b"] == "beta"


def test_map_length():
map = ArrayMap.empty()
assert len(map) == 0, "Length should be 0 for an empty map"
Expand All @@ -119,7 +141,7 @@ def test_map_length():
assert len(map) == 3, "Length should be 3 after adding a third item"
map = map.minus_sorted_indices([1])
assert len(map) == 2, "Length should be 2 after removing one item"
map = map.minus_sorted_indices([0])
map = map.minus_sorted_indices([0])
assert len(map) == 1, "Length should be 1 after removing another item"
map = map.minus_sorted_indices([0])
map = map.minus_sorted_indices([0])
assert len(map) == 0, "Length should be 0 after removing all items"
Loading