Skip to content

Commit

Permalink
method membership safety check
Browse files Browse the repository at this point in the history
This commit was sponsored by Derek Veit, Matt Campbell, Sergio Bost,
and my other patrons.  If you want to join them, you can support my
work at https://glyph.im/patrons/.
  • Loading branch information
glyph committed Aug 11, 2024
1 parent 6acab9e commit ef53493
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
22 changes: 22 additions & 0 deletions automat/_test/test_type_based.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,25 @@ def test_buildLock(self) -> None:
builder.state("hello")
with self.assertRaises(AlreadyBuiltError):
builder.build()

def test_methodMembership(self) -> None:
"""
Input methods must be members of their protocol.
"""
builder = TypeMachineBuilder(TestProtocol, NoOpCore)
state = builder.state("test-state")
def stateful(proto: TestProtocol, core: NoOpCore) -> int:
return 4
state2 = builder.state("state2", stateful)
def change(self: TestProtocol) -> None:
"fake copy"
def rogue(self: TestProtocol) -> int:
"not present"
return 3
with self.assertRaises(ValueError):
state.upon(change)
with self.assertRaises(ValueError) as ve:
state2.upon(change)
print(ve.exception)
with self.assertRaises(ValueError):
state.upon(rogue)
12 changes: 12 additions & 0 deletions automat/_typified.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ class TypifiedState(Generic[InputProtocol, Core]):
def upon(
self, input: Callable[Concatenate[InputProtocol, P], R]
) -> UponFromNo[InputProtocol, Core, P, R]:
self.builder._checkMembership(input)
return UponFromNo(self, input)

def _produce_outputs(
Expand Down Expand Up @@ -292,6 +293,7 @@ def upon(
UponFromData[InputProtocol, Core, P, R, Data]
| UponFromNo[InputProtocol, Core, P, R]
):
self.builder._checkMembership(input)
if nodata:
return UponFromNo(self, input)
else:
Expand Down Expand Up @@ -590,3 +592,13 @@ def build(self) -> TypifiedMachine[InputProtocol, Core]:
)

return TypifiedMachine(runtime_type, self._automaton)

def _checkMembership(self, input: Callable[..., object]) -> None:
"""
Ensure that ``input`` is a valid member function of the input protocol,
not just a function that happens to take the right first argument.
"""
if (checked := getattr(self.protocol, input.__name__, None)) is not input:
raise ValueError(
f"{input.__qualname__} is not a member of {self.protocol.__module__}.{self.protocol.__name__}"
)

0 comments on commit ef53493

Please sign in to comment.