Skip to content
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: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
flake8 . --count --max-complexity=10 --max-line-length=180 --show-source --statistics
- name: Type check with mypy
run: |
mypy src
mypy src --strict
- name: Test with pytest
run: |
pytest --cov --cov-fail-under=95
Expand Down
15 changes: 15 additions & 0 deletions experimentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import override


class Base:
def some_method(self) -> str:
return "This is a method from the Base class."


class Sub(Base):
def some_method(self) -> str:
return "This is a method from the Sub class."

@override
def some_methods(self) -> str:
return "This is a method from the Sub class."
88 changes: 47 additions & 41 deletions src/spellbind/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import itertools
from abc import ABC, abstractmethod
from typing import Generic, SupportsIndex, Iterable, TypeVar, Callable
from typing import Generic, SupportsIndex, Iterable, TypeVar, Callable, Any

from typing_extensions import override

Expand All @@ -20,7 +20,7 @@ def is_permutation_only(self) -> bool: ...
def map(self, transformer: Callable[[_S_co], _T]) -> CollectionAction[_T]: ...

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}()"


Expand Down Expand Up @@ -98,12 +98,12 @@ def map(self, transformer: Callable[[_S_co], _T]) -> AddOneAction[_T]:
return SimpleAddOneAction(transformer(self.value))

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(value={self.value})"


class SimpleAddOneAction(AddOneAction[_S_co], Generic[_S_co]):
def __init__(self, item: _S_co):
def __init__(self, item: _S_co) -> None:
self._item = item

@property
Expand All @@ -123,12 +123,12 @@ def map(self, transformer: Callable[[_S_co], _T]) -> RemoveOneAction[_T]:
return SimpleRemoveOneAction(transformer(self.value))

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(value={self.value})"


class SimpleRemoveOneAction(RemoveOneAction[_S_co], Generic[_S_co]):
def __init__(self, item: _S_co):
def __init__(self, item: _S_co) -> None:
self._item = item

@property
Expand Down Expand Up @@ -167,13 +167,13 @@ def changes(self) -> Iterable[OneElementChangedAction[_S_co]]:
return self._changes

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ElementsChangedAction):
return NotImplemented
return self.changes == other.changes

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(changes={self.changes})"


Expand Down Expand Up @@ -212,22 +212,22 @@ def old_item(self) -> _S_co:
return self._old_item

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, OneElementChangedAction):
return NotImplemented
return (self.new_item == other.new_item and
self.old_item == other.old_item)
# mypy --strict complains that equality between two "Any" does return Any, not bool
return bool(self.new_item == other.new_item and self.old_item == other.old_item)

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(new_item={self.new_item}, old_item={self.old_item})"


CLEAR_ACTION: ClearAction = ClearAction()
_CLEAR_ACTION: ClearAction[Any] = ClearAction()


def clear_action() -> ClearAction[_S_co]:
return CLEAR_ACTION # type: ignore[return-value]
return _CLEAR_ACTION


class SequenceAction(CollectionAction[_S_co], Generic[_S_co], ABC):
Expand Down Expand Up @@ -272,7 +272,7 @@ def delta_actions(self) -> tuple[AtIndexDeltaAction[_S_co], ...]:
def map(self, transformer: Callable[[_S_co], _T]) -> AtIndexDeltaAction[_T]: ...

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(index={self.index}, value={self.value})"


Expand All @@ -283,7 +283,7 @@ def map(self, transformer: Callable[[_S_co], _T]) -> InsertAction[_T]:


class SimpleInsertAction(InsertAction[_S_co], Generic[_S_co]):
def __init__(self, index: SupportsIndex, item: _S_co):
def __init__(self, index: SupportsIndex, item: _S_co) -> None:
self._index = index
self._item = item

Expand All @@ -298,10 +298,11 @@ def value(self) -> _S_co:
return self._item

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, InsertAction):
return NotImplemented
return self.index == other.index and self.value == other.value
# mypy --strict complains that equality between two "Any" does return Any, not bool
return self.index == other.index and bool(self.value == other.value)


class InsertAllAction(AtIndicesDeltasAction[_S_co], Generic[_S_co], ABC):
Expand All @@ -319,10 +320,11 @@ def map(self, transformer: Callable[[_S_co], _T]) -> InsertAllAction[_T]:
return SimpleInsertAllAction(tuple((index, transformer(item)) for index, item in self.index_with_items))

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, InsertAllAction):
return NotImplemented
return self.index_with_items == other.index_with_items
# mypy --strict complains that equality between two "Any" does return Any, not bool
return bool(self.index_with_items == other.index_with_items)


class SimpleInsertAllAction(InsertAllAction[_S_co], Generic[_S_co]):
Expand All @@ -341,14 +343,15 @@ def map(self, transformer: Callable[[_S_co], _T]) -> RemoveAtIndexAction[_T]:
return SimpleRemoveAtIndexAction(self.index, transformer(self.value))

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, RemoveAtIndexAction):
return NotImplemented
return self.index == other.index and self.value == other.value
# mypy --strict complains that equality between two "Any" does return Any, not bool
return self.index == other.index and bool(self.value == other.value)


class SimpleRemoveAtIndexAction(RemoveAtIndexAction[_S_co], RemoveOneAction[_S_co], Generic[_S_co]):
def __init__(self, index: SupportsIndex, item: _S_co):
def __init__(self, index: SupportsIndex, item: _S_co) -> None:
self._index = index.__index__()
self._item = item

Expand Down Expand Up @@ -377,13 +380,13 @@ def map(self, transformer: Callable[[_S_co], _T]) -> RemoveAtIndicesAction[_T]:
))

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, RemoveAtIndicesAction):
return NotImplemented
return self.removed_elements_with_index == other.removed_elements_with_index
return bool(self.removed_elements_with_index == other.removed_elements_with_index)

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.removed_elements_with_index})"


Expand Down Expand Up @@ -439,16 +442,17 @@ def map(self, transformer: Callable[[_S_co], _T]) -> SetAtIndexAction[_T]:
return SimpleSetAtIndexAction(self.index, old_item=transformer(self.old_item), new_item=transformer(self.new_item))

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(index={self.index}, old_item={self.old_item}, new_item={self.new_item})"

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, SetAtIndexAction):
return NotImplemented
# mypy --strict complains that equality between two "Any" does return Any, not bool
return (self.index == other.index and
self.old_item == other.old_item and
self.new_item == other.new_item)
bool(self.old_item == other.old_item and
self.new_item == other.new_item))


class SimpleSetAtIndexAction(SetAtIndexAction[_S_co], Generic[_S_co]):
Expand Down Expand Up @@ -498,15 +502,16 @@ def map(self, transformer: Callable[[_S_co], _T]) -> SliceSetAction[_T]:
old_items=tuple(transformer(item) for item in self.old_items))

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, SliceSetAction):
return NotImplemented
# mypy --strict complains that equality between two "Any" does return Any, not bool
return (self.indices == other.indices and
self.new_items == other.new_items and
self.old_items == other.old_items)
bool(self.new_items == other.new_items and
self.old_items == other.old_items))

@override
def __repr__(self):
def __repr__(self) -> str:
return (f"{self.__class__.__name__}(indices={self.indices}, "
f"new_items={self.new_items}, old_items={self.old_items})")

Expand Down Expand Up @@ -571,13 +576,14 @@ def map(self, transformer: Callable[[_S_co], _T]) -> SetAtIndicesAction[_T]:
in self.indices_with_new_and_old_items))

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, SetAtIndicesAction):
return NotImplemented
return self.indices_with_new_and_old_items == other.indices_with_new_and_old_items
# mypy --strict complains that equality between two "Any" does return Any, not bool
return bool(self.indices_with_new_and_old_items == other.indices_with_new_and_old_items)

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(indices_with_new_and_old_items={self.indices_with_new_and_old_items})"


Expand Down Expand Up @@ -633,14 +639,14 @@ def map(self, transformer: Callable[[_S_co], _T]) -> ExtendAction[_T]:
return SimpleExtendAction(self.old_sequence_length, tuple(transformer(item) for item in self.items))

@override
def __eq__(self, other):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ExtendAction):
return NotImplemented
return (self.old_sequence_length == other.old_sequence_length and
tuple(self.items) == tuple(other.items))

@override
def __repr__(self):
def __repr__(self) -> str:
return f"{self.__class__.__name__}(old_sequence_length={self.old_sequence_length}, items={self.items})"


Expand All @@ -660,8 +666,8 @@ def old_sequence_length(self) -> int:
return self._old_sequence_length


REVERSE_SEQUENCE_ACTION: ReverseAction = ReverseAction()
REVERSE_SEQUENCE_ACTION: ReverseAction[Any] = ReverseAction()


def reverse_action() -> ReverseAction[_S_co]:
return REVERSE_SEQUENCE_ACTION # type: ignore[return-value]
return REVERSE_SEQUENCE_ACTION
32 changes: 23 additions & 9 deletions src/spellbind/bool_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,20 @@ def __xor__(self, other: BoolLike) -> BoolValue:
def __rxor__(self, other: bool) -> BoolValue:
return BoolValue.derive_from_two(operator.xor, other, self)

@overload
def select(self, if_true: IntValueLike, if_false: IntValueLike) -> IntValue: ...
def select_int(self, if_true: IntLike, if_false: IntLike) -> IntValue:
from spellbind.int_values import IntValue
return IntValue.derive_from_three(_select_function, self, if_true, if_false)

def select_float(self, if_true: FloatLike, if_false: FloatLike) -> FloatValue:
from spellbind.float_values import FloatValue
return FloatValue.derive_from_three(_select_function, self, if_true, if_false)

def select_bool(self, if_true: BoolLike, if_false: BoolLike) -> BoolValue:
return BoolValue.derive_from_three(_select_function, self, if_true, if_false)

def select_str(self, if_true: StrLike, if_false: StrLike) -> StrValue:
from spellbind.str_values import StrValue
return StrValue.derive_from_three(_select_function, self, if_true, if_false)

@overload
def select(self, if_true: FloatValueLike, if_false: FloatValueLike) -> FloatValue: ...
Expand All @@ -75,19 +87,21 @@ def select(self, if_true: BoolValue, if_false: BoolValue) -> BoolValue: ...
@overload
def select(self, if_true: Value[_S] | _S, if_false: Value[_S] | _S) -> Value[_S]: ...

def select(self, if_true, if_false):
def select(self, if_true: Value[_S] | _S, if_false: Value[_S] | _S) -> Value[_S]:
from spellbind.str_values import StrValue
from spellbind.float_values import FloatValue
from spellbind.int_values import IntValue
from spellbind.str_values import StrValue

# suppressing errors, because it seems mypy does not understand the connection between
# parameter type and return type as it could be inferred from the overloads
if isinstance(if_true, (FloatValue, float)) and isinstance(if_false, (FloatValue, float)):
return FloatValue.derive_from_three(_select_function, self, if_true, if_false)
return self.select_float(if_true, if_false) # type: ignore[return-value]
elif isinstance(if_true, (StrValue, str)) and isinstance(if_false, (StrValue, str)):
return StrValue.derive_from_three(_select_function, self, if_true, if_false)
return self.select_str(if_true, if_false) # type: ignore[return-value]
elif isinstance(if_true, (BoolValue, bool)) and isinstance(if_false, (BoolValue, bool)):
return BoolValue.derive_from_three(_select_function, self, if_true, if_false)
return self.select_bool(if_true, if_false) # type: ignore[return-value]
elif isinstance(if_true, (IntValue, int)) and isinstance(if_false, (IntValue, int)):
return IntValue.derive_from_three(_select_function, self, if_true, if_false)
return self.select_int(if_true, if_false) # type: ignore[return-value]
else:
return Value.derive_three_value(_select_function, self, if_true, if_false)

Expand Down Expand Up @@ -127,7 +141,7 @@ class OneToBoolValue(OneToOneValue[_S, bool], BoolValue, Generic[_S]):


class NotBoolValue(OneToOneValue[bool, bool], BoolValue):
def __init__(self, value: Value[bool]):
def __init__(self, value: Value[bool]) -> None:
super().__init__(operator.not_, value)


Expand Down
4 changes: 2 additions & 2 deletions src/spellbind/event.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable, TypeVar, Generic, Iterable, Sequence
from typing import Callable, TypeVar, Generic, Iterable, Sequence, Any

from typing_extensions import override

Expand All @@ -10,7 +10,7 @@
_S = TypeVar("_S")
_T = TypeVar("_T")
_U = TypeVar("_U")
_O = TypeVar('_O', bound=Callable)
_O = TypeVar('_O', bound=Callable[..., Any])


class Event(_BaseObservable[Observer], Observable, Emitter):
Expand Down
Loading