from transitions import Machine
class TradingSystem:
def __init__(self):
self.machine = Machine(model=self, states=['RUNNING'], initial='RUNNING')
def check_running(self) -> None:
if self.is_RUNNING():
print("System is running")
system = TradingSystem()
system.check_running()
mypy transitions_mypy.py
выдает ошибку:
transitions_mypy.py:9: error: "TradingSystem" has no attribute "is_RUNNING" [attr-defined]
Этого можно избежать, обойдя mypy, например добавив # type: ignore[attr-defined]
в конце строки 9.
Но каков правильный путь? Лучше ли избегать обхода mypy? Возможно, определив атрибут вручную?
Согласно github.com/pytransitions/transitions/issues/574 «переходы 0.9.0 были опубликованы в PyPI и включают файлы-заглушки ввода». Но я не знаком с термином «плагин mypy».
Mypy поддерживает систему плагинов, которая позволяет пакетам настраивать способ проверки mypy кода, который их использует: mypy.readthedocs.io/en/stable/… . Например, плагин mypy numpy. pytransition должен будет предоставить плагин, чтобы mypy знал о динамических атрибутах, которые он определяет.
Итак, во-первых, причина этой проблемы заключается в том, что эта библиотека «переходов» действительно плохо себя ведет — внешнее изменение себя таким образом не совсем хорошая практика, например. что, если что-то уже использовало is_RUNNING
, когда изменяло себя?
Тем не менее, есть способ решить вашу проблему без игнорирования типов, вам просто нужно смоделировать методы, которые генерируют переходы, через отдельный интерфейс. На данный момент я добавил сюда is_RUNNING
, но вы можете добавить и другие методы:
from transitions import Machine
class TransitionsInterface:
def is_RUNNING(self) -> bool:
...
class TradingSystem(TransitionsInterface):
def __init__(self):
self.machine = Machine(model=self, states=['RUNNING'], initial='RUNNING')
def check_running(self) -> None:
if self.is_RUNNING():
print("System is running")
system = TradingSystem()
system.check_running()
Надеюсь это поможет!
Насколько я понимаю, правильное решение от @brian61354270 — это то, что для переходов нужен плагин mypy. Но интерфейс кажется лучшим решением на данный момент.
Я никогда не тестировал этот обходной путь, так как в конечном итоге я использовал attrs, который, как мне казалось, был просто вариацией этого метода. Но, как отмечает @aleneum, приведенный выше код даже не работает с моей модификацией.
Ответ , предложенный @Mark, не будет работать, поскольку переходы не переопределяют уже существующие атрибуты модели, потому что это было бы неожиданным (неправильным) поведением (как указал @Mark). Если вы включите ведение журнала, переходы также сообщат вам, что:
import logging
logging.basicConfig(level=logging.DEBUG)
Вывод должен содержать:
WARNING:transitions.core:Model already contains an attribute 'is_RUNNING'. Skip binding.
Раньше я предлагал наследовать от Machine
и переопределять Machine._checked_assignment
, чтобы обойти эту защиту. Однако подход @james-hirschorn с использованием attrs , который был опубликован в трекере проблем с переходами, на мой взгляд, является лучшим решением:
from typing import Callable
from attrs import define, field
from transitions import Machine
@define(slots=False)
class TradingSystem:
is_RUNNING: Callable[[], bool] = field(init=False)
stop: Callable[[], None] = field(init=False)
def __attrs_post_init__(self):
self.machine = Machine(model=self, states=['RUNNING', 'STOPPED'], initial='RUNNING')
self.machine.add_transition(trigger='stop', source='RUNNING', dest='STOPPED')
def check_running(self) -> None:
if self.is_RUNNING():
print("System is running")
def stop_system(self) -> None:
self.stop()
print("System stopped")
# Example usage
system = TradingSystem()
system.check_running()
system.stop_system()
Это приводит к некоторым накладным расходам, поскольку вам придется дважды определять триггеры и удобные методы. Кроме того, информация о типе также является неполной, поскольку переходы также будут добавлять is_STOPPED
и автоматические переходы, такие как to_STOPPED/RUNNING
, или методы перехода «просмотр», такие как may_stop
к TradingSystem
во время выполнения.
Улучшение поддержки ввода переходов в настоящее время является открытым вопросом .
Предлагает ли pytransitions плагин mypy?