python-chess
python-chess copied to clipboard
AssertionError is thrown if async analysis is cancelled too soon
I'm writing a website that listens to a pgn feed and runs an analysis of the current position in an asyncio task. When a new position comes, the analysis task is cancelled, and new task started.
If analysis is cancelled too soon (two updates come one after another), python-chess throws an AssertionError exception.
Traceback (most recent call last):
File "/home/crem/dev/lczero-live/backend/analyzer.py", line 157, in _uci_worker_think
with await self._engine.analysis(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/crem/dev/lczero-live/.venv/lib/python3.12/site-packages/chess/engine.py", line 1774, in analysis
return await self.communicate(UciAnalysisCommand)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/crem/dev/lczero-live/.venv/lib/python3.12/site-packages/chess/engine.py", line 997, in communicate
self.next_command.set_finished()
File "/home/crem/dev/lczero-live/.venv/lib/python3.12/site-packages/chess/engine.py", line 1269, in set_finished
assert self.state in [CommandState.ACTIVE, CommandState.CANCELLING], self.state
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: CommandState.NEW
Having this awkward lock did help to work around it.
It's ugly because I cannot have overlapping async with, so I acquire/release manually and then release in finally: for the case engine.analysis throws an exception.
await self._uci_cancelation_lock.acquire()
with await self._engine.analysis(board=board) as analysis:
self._uci_cancelation_lock.release()
# do the stuff
finally:
try:
self._uci_cancelation_lock.release()
except RuntimeError:
pass
### somewhere else:
async with self._uci_cancelation_lock:
task.cancel()