diff --git a/pyproject.toml b/pyproject.toml index 3660460..d3bc1f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "superstate" -version = "1.6.2a2" +version = "1.6.2a3" description = "Robust statechart for configurable automation rules." readme = "README.md" license = {file = "LICENSE"} diff --git a/src/superstate/__init__.py b/src/superstate/__init__.py index a6b015c..d2361a3 100644 --- a/src/superstate/__init__.py +++ b/src/superstate/__init__.py @@ -64,7 +64,7 @@ __author_email__ = 'jpj6652@gmail.com' __title__ = 'superstate' __description__ = 'Compact statechart that can be vendored.' -__version__ = '1.6.2a2' +__version__ = '1.6.2a3' __license__ = 'MIT' __copyright__ = 'Copyright 2022 Jesse Johnson.' __all__ = ( diff --git a/src/superstate/context.py b/src/superstate/context.py index 3121dfe..3541b9e 100644 --- a/src/superstate/context.py +++ b/src/superstate/context.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING, List, Tuple +from typing import TYPE_CHECKING from uuid import UUID from superstate.state import ParallelState @@ -56,7 +56,7 @@ def parent(self) -> State: return self.current_state.parent or self.root @property - def children(self) -> Tuple[State, ...]: + def children(self) -> tuple[State, ...]: """Return list of states.""" return ( tuple(self.__current_state.states.values()) @@ -65,19 +65,19 @@ def children(self) -> Tuple[State, ...]: ) @property - def states(self) -> Tuple[State, ...]: + def states(self) -> tuple[State, ...]: """Return list of states.""" return tuple(self.parent.states.values()) @property - def siblings(self) -> Tuple[State, ...]: + def siblings(self) -> tuple[State, ...]: """Return list of states.""" return tuple(self.parent.states.values()) @property - def active(self) -> Tuple[State, ...]: + def active(self) -> tuple[State, ...]: """Return active states.""" - states: List[State] = [] + states: list[State] = [] parents = list(reversed(self.current_state)) for i, x in enumerate(parents): n = i + 1 diff --git a/src/superstate/machine.py b/src/superstate/machine.py index 18450db..456ae99 100644 --- a/src/superstate/machine.py +++ b/src/superstate/machine.py @@ -7,17 +7,7 @@ import os from copy import deepcopy from itertools import zip_longest -from typing import ( - TYPE_CHECKING, - Any, - Dict, - Iterator, - List, - Optional, - # Sequence, - Tuple, - cast, -) +from typing import TYPE_CHECKING, Any, Iterator, Optional, cast from uuid import UUID from superstate.config import DEFAULT_BINDING, DEFAULT_PROVIDER @@ -60,8 +50,8 @@ class MetaStateChart(type): def __new__( mcs, name: str, - bases: Tuple[type, ...], - attrs: Dict[str, Any], + bases: tuple[type, ...], + attrs: dict[str, Any], ) -> 'MetaStateChart': if '__name__' not in attrs: name = name.lower() @@ -218,7 +208,7 @@ def parent(self) -> SubstateMixin: return self.current_state.parent or self.root @property - def children(self) -> Tuple[State, ...]: + def children(self) -> tuple[State, ...]: """Return list of states.""" return ( tuple(self.__current_state.states.values()) @@ -227,19 +217,19 @@ def children(self) -> Tuple[State, ...]: ) @property - def states(self) -> Tuple[State, ...]: + def states(self) -> tuple[State, ...]: """Return list of states.""" return tuple(self.parent.states.values()) @property - def siblings(self) -> Tuple[State, ...]: + def siblings(self) -> tuple[State, ...]: """Return list of states.""" return tuple(self.parent.states.values()) @property - def active(self) -> Tuple[State, ...]: + def active(self) -> tuple[State, ...]: """Return active states.""" - states: List[State] = [] + states: list[State] = [] parents = list(reversed(self.current_state)) for i, x in enumerate(parents): n = i + 1 diff --git a/src/superstate/model/action.py b/src/superstate/model/action.py index 77ec5db..13de50b 100644 --- a/src/superstate/model/action.py +++ b/src/superstate/model/action.py @@ -4,16 +4,9 @@ import logging import logging.config +from collections.abc import Callable from dataclasses import InitVar, asdict, dataclass -from typing import ( - TYPE_CHECKING, - Any, - Callable, - List, - Optional, - Sequence, - Union, -) +from typing import TYPE_CHECKING, Any, Optional, Sequence, Union from superstate.config import LOGGING_CONFIG from superstate.model.base import Action, Conditional @@ -57,12 +50,12 @@ def callback(self, provider: Provider, *args: Any, **kwargs: Any) -> None: class ForEach(Action): """Data item providing state data.""" - content: InitVar[List[str]] + content: InitVar[list[str]] array: str item: str index: Optional[str] = None # expression - def __post_init__(self, content: List[str]) -> None: + def __post_init__(self, content: list[str]) -> None: self.__content = [Action.create(x) for x in content] # type: ignore def callback(self, provider: Provider, *args: Any, **kwargs: Any) -> None: diff --git a/src/superstate/model/data.py b/src/superstate/model/data.py index 37fdead..dbf1c34 100644 --- a/src/superstate/model/data.py +++ b/src/superstate/model/data.py @@ -10,8 +10,6 @@ TYPE_CHECKING, Any, ClassVar, - # Dict, - List, Optional, # Sequence, Type, @@ -87,7 +85,7 @@ def value(self) -> Optional[Any]: class DataModel(ChainMap): """Instantiate state types from class metadata.""" - data: List[Data] + data: list[Data] binding: ClassVar[str] = 'early' provider: ClassVar[Type[Provider]] = Default @@ -132,5 +130,5 @@ def populate(self) -> None: class DoneData: """Data model providing state data.""" - param: List[Data] + param: list[Data] content: Optional[Any] = None diff --git a/src/superstate/provider/__init__.py b/src/superstate/provider/__init__.py index 3e81320..5ad026b 100644 --- a/src/superstate/provider/__init__.py +++ b/src/superstate/provider/__init__.py @@ -1,6 +1,6 @@ """Provide common types for statechart components.""" -from typing import Dict, Type +from typing import Type from superstate.provider.base import Provider from superstate.provider.default import Default @@ -18,7 +18,7 @@ # 'XPath', ) -PROVIDERS: Dict[str, Type[Provider]] = { +PROVIDERS: dict[str, Type[Provider]] = { 'default': Default, # 'ecmasscript': ECMASript, # 'null': Null, diff --git a/src/superstate/provider/base.py b/src/superstate/provider/base.py index e17a9c7..97f7e80 100644 --- a/src/superstate/provider/base.py +++ b/src/superstate/provider/base.py @@ -2,12 +2,11 @@ import re from abc import ABC, abstractmethod # pylint: disable=no-name-in-module +from collections.abc import Callable from functools import singledispatchmethod from typing import ( TYPE_CHECKING, Any, - Callable, - Dict, Optional, Type, TypeVar, @@ -64,7 +63,7 @@ def get_provider(cls, name: str) -> Type['Provider']: # ) @property - def globals(self) -> Dict[str, Any]: + def globals(self) -> dict[str, Any]: """Get global attributes and methods available for eval and exec.""" # pylint: disable=import-outside-toplevel from datetime import datetime @@ -75,7 +74,7 @@ def globals(self) -> Dict[str, Any]: return glb @property - def locals(self) -> Dict[str, Any]: + def locals(self) -> dict[str, Any]: """Get local attributes and methods available for eval and exec.""" lcl = dict(self.ctx.current_state.datamodel) lcl['In'] = self.In diff --git a/src/superstate/state.py b/src/superstate/state.py index 8417259..fefe523 100644 --- a/src/superstate/state.py +++ b/src/superstate/state.py @@ -4,17 +4,7 @@ import logging from itertools import chain # , zip_longest -from typing import ( - TYPE_CHECKING, - Any, - Dict, - Generator, - List, - Optional, - Tuple, - Union, - cast, -) +from typing import TYPE_CHECKING, Any, Generator, Optional, Union, cast from superstate.exception import ( InvalidConfig, @@ -39,16 +29,16 @@ # # initial: Optional[Initial] # kind: Optional[str] -# states: List[State] -# transitions: List[State] +# states: list[State] +# transitions: list[State] # on_entry: Optional[ActionTypes] # on_exit: Optional[ActionTypes] # # def __new__( # cls, # name: str, -# bases: Tuple[type, ...], -# attrs: Dict[str, Any], +# bases: tuple[type, ...], +# attrs: dict[str, Any], # ) -> 'MetaState': # initial = attrs.pop('initial', None) # kind = attrs.pop('type', None) @@ -70,15 +60,15 @@ class TransitionMixin: """Provide an atomic state for a statechart.""" - __transitions: List[Transition] + __transitions: list[Transition] @property - def transitions(self) -> Tuple[Transition, ...]: + def transitions(self) -> tuple[Transition, ...]: """Return transitions of this state.""" return tuple(self.__transitions) @transitions.setter - def transitions(self, transitions: List[Transition]) -> None: + def transitions(self, transitions: list[Transition]) -> None: """Initialize atomic state.""" self.__transitions = transitions @@ -86,7 +76,7 @@ def add_transition(self, transition: Transition) -> None: """Add transition to this state.""" self.__transitions.append(transition) - def get_transition(self, event: str) -> Tuple[Transition, ...]: + def get_transition(self, event: str) -> tuple[Transition, ...]: """Get each transition maching event.""" return tuple( filter( @@ -161,15 +151,15 @@ class State: # '__type', # ] - __stack: List[State] + __stack: list[State] datamodel: DataModel name: str = cast(str, Identifier()) # history: Optional['HistoryState'] # final: Optional[FinalState] - # states: Dict[str, State] - # transitions: Tuple[Transition, ...] - # onentry: Tuple[ActionTypes, ...] - # onexit: Tuple[ActionTypes, ...] + states: dict[str, State] + # transitions: tuple[Transition, ...] + # onentry: tuple[ActionTypes, ...] + # onexit: tuple[ActionTypes, ...] # pylint: disable-next=unused-argument def __new__(cls, *args: Any, **kwargs: Any) -> State: @@ -193,7 +183,7 @@ def __new__(cls, *args: Any, **kwargs: Any) -> State: def __init__( self, # pylint: disable=unused-argument name: str, - # settings: Optional[Dict[str, Any]] = None, + # settings: Optional[dict[str, Any]] = None, # /, **kwargs: Any, ) -> None: @@ -488,7 +478,7 @@ def run_on_entry(self, ctx: StateChart) -> Optional[Any]: class SubstateMixin(State): """Provide composite abstract to define nested state types.""" - __states: Dict[str, State] = {} + __states: dict[str, State] = {} def __getattr__(self, name: str) -> Any: if name.startswith('__'): @@ -499,12 +489,12 @@ def __getattr__(self, name: str) -> Any: raise AttributeError @property - def states(self) -> Dict[str, State]: + def states(self) -> dict[str, State]: """Return states.""" return self.__states @states.setter - def states(self, states: List[State]) -> None: + def states(self, states: list[State]) -> None: """Define states.""" if not self.__states: self.__states = {} @@ -529,7 +519,7 @@ def get_state(self, name: str) -> Optional[State]: # class SubstateMixin(State): # """Provide composite abstract to define nested state types.""" # -# __states: List[State] = [] +# __states: list[State] = [] # # def __getattr__(self, name: str) -> Any: # if name.startswith('__'): @@ -540,12 +530,12 @@ def get_state(self, name: str) -> Optional[State]: # raise AttributeError # # @property -# def states(self) -> List[State]: +# def states(self) -> list[State]: # """Return states.""" # return self.__states # # @states.setter -# def states(self, states: List[State]) -> None: +# def states(self, states: list[State]) -> None: # """Define states.""" # if not self.__states: # for state in states: @@ -580,7 +570,7 @@ def __init__(self, name: str, **kwargs: Any) -> None: self.states = kwargs.pop('states', []) super().__init__(name, **kwargs) - def run_on_entry(self, ctx: StateChart) -> Optional[Tuple[Any, ...]]: + def run_on_entry(self, ctx: StateChart) -> Optional[tuple[Any, ...]]: # if next( # (x for x in self.states if isinstance(x, HistoryState)), False # ): @@ -596,7 +586,7 @@ def run_on_entry(self, ctx: StateChart) -> Optional[Tuple[Any, ...]]: ) if initial and ctx.current_state != initial: ctx.change_state(initial) - results: List[Any] = [] + results: list[Any] = [] results += filter(None, [super().run_on_entry(ctx)]) # XXX: self transitions should still be possible here if ( diff --git a/src/superstate/transition.py b/src/superstate/transition.py index d39f403..c1bf5e6 100644 --- a/src/superstate/transition.py +++ b/src/superstate/transition.py @@ -3,9 +3,13 @@ from __future__ import annotations import logging -from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast +from typing import TYPE_CHECKING, Any, Optional, Union, cast -from superstate.exception import InvalidConfig, SuperstateException +from superstate.exception import ( + InvalidConfig, + InvalidState, + SuperstateException, +) from superstate.model import Action, Conditional from superstate.types import Selection, Identifier from superstate.utils import tuplize @@ -95,10 +99,10 @@ def source(self, state: State) -> None: def execute( self, ctx: StateChart, *args: Any, **kwargs: Any - ) -> Optional[List[Any]]: + ) -> Optional[list[Any]]: """Transition the state of the statechart.""" log.info("executing transition contents for event %r", self.event) - results: Optional[List[Any]] = None + results: Optional[list[Any]] = None if self.content: results = [] provider = ctx.datamodel.provider(ctx) diff --git a/src/superstate/utils.py b/src/superstate/utils.py index c75da35..9f30ec5 100644 --- a/src/superstate/utils.py +++ b/src/superstate/utils.py @@ -1,11 +1,11 @@ """Provide common utilities.""" -from typing import Any, Set, Tuple, Type, TypeVar, Union +from typing import Any, Type, TypeVar, Union T = TypeVar('T') -def lookup_subclasses(obj: Type[T]) -> Set[Type[T]]: +def lookup_subclasses(obj: Type[T]) -> set[Type[T]]: """Get all unique subsclasses of a class object.""" return set(obj.__subclasses__()).union( [s for c in obj.__subclasses__() for s in lookup_subclasses(c)] @@ -25,6 +25,6 @@ def to_bool(value: Union[bool, int, str]) -> bool: raise ValueError(f"invalid truthy statement: {value!r}") -def tuplize(value: Any) -> Tuple[Any, ...]: +def tuplize(value: Any) -> tuple[Any, ...]: """Convert various collection types to tuple.""" return tuple(value) if type(value) in (list, tuple) else (value,) diff --git a/tests/test_version.py b/tests/test_version.py index 0ec3c0e..efa662d 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -4,4 +4,4 @@ def test_version() -> None: """Test project metadata version.""" - assert __version__ == '1.6.2a2' + assert __version__ == '1.6.2a3'