aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Automat/py3/automat/_core.py
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-09-02 00:01:09 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-09-02 00:09:17 +0300
commitb5c4ec42ac2cc59dc3b104277ce2e85f5f77c88e (patch)
tree9b37605b78a3d2398da4addca3ee37899621c38e /contrib/python/Automat/py3/automat/_core.py
parent88fb7b5c334c3afdffacd104ee20efda4400d1bc (diff)
downloadydb-b5c4ec42ac2cc59dc3b104277ce2e85f5f77c88e.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python/Automat/py3/automat/_core.py')
-rw-r--r--contrib/python/Automat/py3/automat/_core.py146
1 files changed, 92 insertions, 54 deletions
diff --git a/contrib/python/Automat/py3/automat/_core.py b/contrib/python/Automat/py3/automat/_core.py
index 4118a4b070a..fc637b3a0f4 100644
--- a/contrib/python/Automat/py3/automat/_core.py
+++ b/contrib/python/Automat/py3/automat/_core.py
@@ -5,23 +5,41 @@ A core state-machine abstraction.
Perhaps something that could be replaced with or integrated into machinist.
"""
+from __future__ import annotations
+import sys
from itertools import chain
+from typing import Callable, Generic, Optional, Sequence, TypeVar, Hashable
+
+if sys.version_info >= (3, 10):
+ from typing import TypeAlias
+else:
+ from typing_extensions import TypeAlias
_NO_STATE = "<no state>"
+State = TypeVar("State", bound=Hashable)
+Input = TypeVar("Input", bound=Hashable)
+Output = TypeVar("Output", bound=Hashable)
-class NoTransition(Exception):
+class NoTransition(Exception, Generic[State, Input]):
"""
A finite state machine in C{state} has no transition for C{symbol}.
- @param state: the finite state machine's state at the time of the
- illegal transition.
+ @ivar state: See C{state} init parameter.
- @param symbol: the input symbol for which no transition exists.
+ @ivar symbol: See C{symbol} init parameter.
"""
- def __init__(self, state, symbol):
+ def __init__(self, state: State, symbol: Input):
+ """
+ Construct a L{NoTransition}.
+
+ @param state: the finite state machine's state at the time of the
+ illegal transition.
+
+ @param symbol: the input symbol for which no transition exists.
+ """
self.state = state
self.symbol = symbol
super(Exception, self).__init__(
@@ -29,31 +47,33 @@ class NoTransition(Exception):
)
-class Automaton(object):
+class Automaton(Generic[State, Input, Output]):
"""
A declaration of a finite state machine.
Note that this is not the machine itself; it is immutable.
"""
- def __init__(self):
+ def __init__(self, initial: State | None = None) -> None:
"""
Initialize the set of transitions and the initial state.
"""
- self._initialState = _NO_STATE
- self._transitions = set()
-
+ if initial is None:
+ initial = _NO_STATE # type:ignore[assignment]
+ assert initial is not None
+ self._initialState: State = initial
+ self._transitions: set[tuple[State, Input, State, Sequence[Output]]] = set()
+ self._unhandledTransition: Optional[tuple[State, Sequence[Output]]] = None
@property
- def initialState(self):
+ def initialState(self) -> State:
"""
Return this automaton's initial state.
"""
return self._initialState
-
@initialState.setter
- def initialState(self, state):
+ def initialState(self, state: State) -> None:
"""
Set this automaton's initial state. Raises a ValueError if
this automaton already has an initial state.
@@ -61,12 +81,18 @@ class Automaton(object):
if self._initialState is not _NO_STATE:
raise ValueError(
- "initial state already set to {}".format(self._initialState))
+ "initial state already set to {}".format(self._initialState)
+ )
self._initialState = state
-
- def addTransition(self, inState, inputSymbol, outState, outputSymbols):
+ def addTransition(
+ self,
+ inState: State,
+ inputSymbol: Input,
+ outState: State,
+ outputSymbols: tuple[Output, ...],
+ ):
"""
Add the given transition to the outputSymbol. Raise ValueError if
there is already a transition with the same inState and inputSymbol.
@@ -74,44 +100,51 @@ class Automaton(object):
# keeping self._transitions in a flat list makes addTransition
# O(n^2), but state machines don't tend to have hundreds of
# transitions.
- for (anInState, anInputSymbol, anOutState, _) in self._transitions:
- if (anInState == inState and anInputSymbol == inputSymbol):
+ for anInState, anInputSymbol, anOutState, _ in self._transitions:
+ if anInState == inState and anInputSymbol == inputSymbol:
raise ValueError(
- "already have transition from {} via {}".format(inState, inputSymbol))
- self._transitions.add(
- (inState, inputSymbol, outState, tuple(outputSymbols))
- )
+ "already have transition from {} to {} via {}".format(
+ inState, anOutState, inputSymbol
+ )
+ )
+ self._transitions.add((inState, inputSymbol, outState, tuple(outputSymbols)))
+ def unhandledTransition(
+ self, outState: State, outputSymbols: Sequence[Output]
+ ) -> None:
+ """
+ All unhandled transitions will be handled by transitioning to the given
+ error state and error-handling output symbols.
+ """
+ self._unhandledTransition = (outState, tuple(outputSymbols))
- def allTransitions(self):
+ def allTransitions(self) -> frozenset[tuple[State, Input, State, Sequence[Output]]]:
"""
All transitions.
"""
return frozenset(self._transitions)
-
- def inputAlphabet(self):
+ def inputAlphabet(self) -> set[Input]:
"""
The full set of symbols acceptable to this automaton.
"""
- return {inputSymbol for (inState, inputSymbol, outState,
- outputSymbol) in self._transitions}
+ return {
+ inputSymbol
+ for (inState, inputSymbol, outState, outputSymbol) in self._transitions
+ }
-
- def outputAlphabet(self):
+ def outputAlphabet(self) -> set[Output]:
"""
The full set of symbols which can be produced by this automaton.
"""
return set(
chain.from_iterable(
- outputSymbols for
- (inState, inputSymbol, outState, outputSymbols)
- in self._transitions
+ outputSymbols
+ for (inState, inputSymbol, outState, outputSymbols) in self._transitions
)
)
-
- def states(self):
+ def states(self) -> frozenset[State]:
"""
All valid states; "Q" in the mathematical description of a state
machine.
@@ -119,47 +152,52 @@ class Automaton(object):
return frozenset(
chain.from_iterable(
(inState, outState)
- for
- (inState, inputSymbol, outState, outputSymbol)
- in self._transitions
+ for (inState, inputSymbol, outState, outputSymbol) in self._transitions
)
)
-
- def outputForInput(self, inState, inputSymbol):
+ def outputForInput(
+ self, inState: State, inputSymbol: Input
+ ) -> tuple[State, Sequence[Output]]:
"""
A 2-tuple of (outState, outputSymbols) for inputSymbol.
"""
- for (anInState, anInputSymbol,
- outState, outputSymbols) in self._transitions:
+ for anInState, anInputSymbol, outState, outputSymbols in self._transitions:
if (inState, inputSymbol) == (anInState, anInputSymbol):
return (outState, list(outputSymbols))
- raise NoTransition(state=inState, symbol=inputSymbol)
+ if self._unhandledTransition is None:
+ raise NoTransition(state=inState, symbol=inputSymbol)
+ return self._unhandledTransition
+
+OutputTracer = Callable[[Output], None]
+Tracer: TypeAlias = "Callable[[State, Input, State], OutputTracer[Output] | None]"
-class Transitioner(object):
+
+class Transitioner(Generic[State, Input, Output]):
"""
The combination of a current state and an L{Automaton}.
"""
- def __init__(self, automaton, initialState):
- self._automaton = automaton
- self._state = initialState
- self._tracer = None
+ def __init__(self, automaton: Automaton[State, Input, Output], initialState: State):
+ self._automaton: Automaton[State, Input, Output] = automaton
+ self._state: State = initialState
+ self._tracer: Tracer[State, Input, Output] | None = None
- def setTrace(self, tracer):
+ def setTrace(self, tracer: Tracer[State, Input, Output] | None) -> None:
self._tracer = tracer
- def transition(self, inputSymbol):
+ def transition(
+ self, inputSymbol: Input
+ ) -> tuple[Sequence[Output], OutputTracer[Output] | None]:
"""
Transition between states, returning any outputs.
"""
- outState, outputSymbols = self._automaton.outputForInput(self._state,
- inputSymbol)
+ outState, outputSymbols = self._automaton.outputForInput(
+ self._state, inputSymbol
+ )
outTracer = None
if self._tracer:
- outTracer = self._tracer(self._state._name(),
- inputSymbol._name(),
- outState._name())
+ outTracer = self._tracer(self._state, inputSymbol, outState)
self._state = outState
return (outputSymbols, outTracer)