Repository: pytransitions/transitions (language: python)

Issue:
Explicitly reexport package names
When in strict mode, mypy objects to imports from transitions with errors like
```
error: Module "transitions.extensions" does not explicitly export attribute "LockedMachine"; implicit reexport disabled  [attr-defined]
```
This occurs because the ``__init__.py`` files import names into the package namespace, but do not explicitly reexport them.

The fix is for ``__init__.py`` files to provide an explicit ``__all__``.

Meanwhile, downstream packages can workaround this by adding to ``mypy.ini``
```
[mypy-transitions]
implicit_reexport = True
```


Discussion / hints:

Fix patch:
diff --git a/Changelog.md b/Changelog.md
index 348fe5a6..a9414b0e 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -13,8 +13,11 @@
 - Bug #626: Process exceptions with `Machine.on_exception` in may_<trigger> as well (thanks @match1)
 - PR #666: Improved handling of removing transitions for `GraphMachine` and `HiearachicalMachine` (thanks @drpjm)
 - Typing:
+  + Added `--strict` mypy flag for `transitions` type checks (not `tests` though)
   + Made state, event and machine property mandatory in (Nested)EventData
   + Transition.dest may be None
+  + Introduced (Async)CallbackFunc to declutter callback-related signatures
+  + Add stub file for extension package for explicite reexport (thanks @DrewDevereux)
 
 ## 0.9.0 (September 2022)
 
diff --git a/transitions/__init__.py b/transitions/__init__.py
index 763a364c..28e11b9e 100644
--- a/transitions/__init__.py
+++ b/transitions/__init__.py
@@ -9,7 +9,7 @@
 from .version import __version__
 from .core import (State, Transition, Event, EventData, Machine, MachineError)
 
-__copyright__ = "Copyright (c) 2021 Tal Yarkoni, Alexander Neumann"
+__copyright__ = "Copyright (c) 2024 Tal Yarkoni, Alexander Neumann"
 __license__ = "MIT"
-__summary__ = "A lightweight, object-oriented finite state machine in Python"
-__uri__ = "https://github.com/tyarkoni/transitions"
+__summary__ = "A lightweight, object-oriented finite state machine implementation in Python with many extensions"
+__uri__ = "https://github.com/pytransitions/transitions"
diff --git a/transitions/core.pyi b/transitions/core.pyi
index db9b75ed..b216bfa3 100644
--- a/transitions/core.pyi
+++ b/transitions/core.pyi
@@ -10,13 +10,14 @@ from enum import Enum, EnumMeta
 
 _LOGGER: Logger
 
-Callback = Union[str, Callable]
+CallbackFunc = Callable[..., Optional[bool]]
+Callback = Union[str, CallbackFunc]
 CallbackList = List[Callback]
 CallbacksArg = Optional[Union[Callback, CallbackList]]
-ModelState = Union[str, Enum, List]
+ModelState = Union[str, Enum, List["ModelState"]]
 ModelParameter = Union[Union[Literal['self'], Any], List[Union[Literal['self'], Any]]]
 
-def listify(obj: Union[None, list, tuple, EnumMeta, Any]) -> Union[list, tuple, EnumMeta]: ...
+def listify(obj: Union[None, List[Any], Tuple[Any], EnumMeta, Any]) -> Union[List[Any], Tuple[Any], EnumMeta]: ...
 
 def _prep_ordered_arg(desired_length: int, arguments: CallbacksArg) -> CallbackList: ...
 
@@ -89,7 +90,7 @@ class Event:
     transitions: DefaultDict[str, List[Transition]]
     def __init__(self, name: str, machine: Machine) -> None: ...
     def add_transition(self, transition: Transition) -> None: ...
-    def trigger(self, model: object, *args: List, **kwargs: Dict) -> bool: ...
+    def trigger(self, model: object, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
     def _trigger(self, event_data: EventData) -> bool: ...
     def _process(self, event_data: EventData) -> bool: ...
     def _is_valid_source(self, state: State) -> bool: ...
@@ -105,7 +106,7 @@ class Machine:
     event_cls: Type[Event]
     self_literal: Literal['self']
     _queued: bool
-    _transition_queue: Deque[partial]
+    _transition_queue: Deque[CallbackFunc]
     _before_state_change: CallbackList
     _after_state_change: CallbackList
     _prepare_event: CallbackList
@@ -178,9 +179,9 @@ class Machine:
                    on_enter: CallbacksArg = ..., on_exit: CallbacksArg = ...,
                    ignore_invalid_triggers: Optional[bool] = ..., **kwargs: Dict[str, Any]) -> None: ...
     def _add_model_to_state(self, state: State, model: object) -> None: ...
-    def _checked_assignment(self, model: object, name: str, func: Callable) -> None: ...
+    def _checked_assignment(self, model: object, name: str, func: CallbackFunc) -> None: ...
     def _add_trigger_to_model(self, trigger: str, model: object) -> None: ...
-    def _get_trigger(self, model: object, trigger_name: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
+    def _get_trigger(self, model: object, trigger_name: str, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
     def get_triggers(self, *args: Union[str, Enum, State]) -> List[str]: ...
     def add_transition(self, trigger: str,
                        source: Union[StateIdentifier, List[StateIdentifier]],
@@ -201,13 +202,13 @@ class Machine:
     def get_transitions(self, trigger: str = ...,
                         source: StateIdentifier = ..., dest: StateIdentifier = ...) -> List[Transition]: ...
     def remove_transition(self, trigger: str, source: str = ..., dest: str = ...) -> None: ...
-    def dispatch(self, trigger: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
-    def callbacks(self, funcs: Iterable[Union[str, Callable]], event_data: EventData) -> None: ...
-    def callback(self, func: Union[str, Callable], event_data: EventData) -> None: ...
+    def dispatch(self, trigger: str, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
+    def callbacks(self, funcs: Iterable[Callback], event_data: EventData) -> None: ...
+    def callback(self, func: Callback, event_data: EventData) -> None: ...
     @staticmethod
-    def resolve_callable(func: Union[str, Callable], event_data: EventData) -> Callable:  ...
+    def resolve_callable(func: Callback, event_data: EventData) -> CallbackFunc:  ...
     def _has_state(self, state: StateIdentifier, raise_error: bool = ...) -> bool: ...
-    def _process(self, trigger: partial) -> bool: ...
+    def _process(self, trigger: Callable[[], bool]) -> bool: ...
     def _identify_callback(self, name: str) -> Tuple[Optional[str], Optional[str]]: ...
     def __getattr__(self, name: str) -> Any: ...
 
diff --git a/transitions/extensions/__init__.pyi b/transitions/extensions/__init__.pyi
new file mode 100644
index 00000000..bdb28374
--- /dev/null
+++ b/transitions/extensions/__init__.pyi
@@ -0,0 +1,9 @@
+from .asyncio import AsyncMachine as AsyncMachine, HierarchicalAsyncMachine as HierarchicalAsyncMachine
+from .diagrams import GraphMachine as GraphMachine, HierarchicalGraphMachine as HierarchicalGraphMachine
+from .factory import AsyncGraphMachine as AsyncGraphMachine, HierarchicalAsyncGraphMachine as HierarchicalAsyncGraphMachine
+from .factory import MachineFactory as MachineFactory, LockedHierarchicalGraphMachine as LockedHierarchicalGraphMachine
+from .factory import LockedHierarchicalMachine as LockedHierarchicalMachine, LockedGraphMachine as LockedGraphMachine
+from .locking import LockedMachine as LockedMachine
+from .nesting import HierarchicalMachine as HierarchicalMachine
+
+
diff --git a/transitions/extensions/asyncio.pyi b/transitions/extensions/asyncio.pyi
index 592a08f1..45bcea77 100644
--- a/transitions/extensions/asyncio.pyi
+++ b/transitions/extensions/asyncio.pyi
@@ -1,6 +1,6 @@
-from ..core import Condition, Event, EventData, Machine, State, Transition, StateConfig, ModelParameter, TransitionConfig
+from ..core import Callback, Condition, Event, EventData, Machine, State, Transition, StateConfig, ModelParameter, TransitionConfig
 from .nesting import HierarchicalMachine, NestedEvent, NestedState, NestedTransition
-from typing import Any, Optional, List, Type, Dict, Deque, Callable, Union, Iterable, DefaultDict, Literal, Sequence
+from typing import Any, Awaitable, Optional, List, Type, Dict, Deque, Callable, Union, Iterable, DefaultDict, Literal, Sequence
 from asyncio import Task
 from functools import partial
 from logging import Logger
@@ -11,6 +11,9 @@ from ..core import StateIdentifier, CallbacksArg, CallbackList
 
 _LOGGER: Logger
 
+AsyncCallbackFunc = Callable[..., Awaitable[Optional[bool]]]
+AsyncCallback = Union[str, AsyncCallbackFunc]
+
 class AsyncState(State):
     async def enter(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
     async def exit(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
@@ -42,7 +45,7 @@ class AsyncEvent(Event):
     machine: AsyncMachine
     transitions: DefaultDict[str, List[AsyncTransition]]  # type: ignore
 
-    async def trigger(self, model: object, *args: List, **kwargs: Dict[str, Any]) -> bool: ...  # type: ignore[override]
+    async def trigger(self, model: object, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...  # type: ignore[override]
     async def _trigger(self, event_data: AsyncEventData) -> bool: ...  # type: ignore[override]
     async def _process(self, event_data: AsyncEventData) -> bool: ...  # type: ignore[override]
 
@@ -56,12 +59,12 @@ class AsyncMachine(Machine):
     state_cls: Type[NestedAsyncState]
     transition_cls: Type[AsyncTransition]
     event_cls: Type[AsyncEvent]
-    async_tasks: Dict[int, List[Task]]
+    async_tasks: Dict[int, List[Task[Any]]]
     events: Dict[str, AsyncEvent]  # type: ignore
     queued: Union[bool, Literal["model"]]
-    protected_tasks: List[Task]
-    current_context: ContextVar
-    _transition_queue_dict: Dict[int, Deque[Callable]]
+    protected_tasks: List[Task[Any]]
+    current_context: ContextVar[Optional[Task[Any]]]
+    _transition_queue_dict: Dict[int, Deque[AsyncCallbackFunc]]
     def __init__(self, model: Optional[ModelParameter] = ...,
                  states: Optional[Union[Sequence[StateConfig], Type[Enum]]] = ...,
                  initial: Optional[StateIdentifier] = ...,
@@ -75,17 +78,16 @@ class AsyncMachine(Machine):
                  **kwargs: Dict[str, Any]) -> None: ...
     def add_model(self, model: Union[Union[Literal["self"], object], Sequence[Union[Literal["self"], object]]],
                   initial: Optional[StateIdentifier] = ...) -> None: ...
-    async def dispatch(self, trigger: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...  # type: ignore[override]
-    async def callbacks(self, funcs: Iterable[Union[str, Callable]], event_data: AsyncEventData) -> None: ...  # type: ignore[override]
-    async def callback(self, func: Union[str, Callable], event_data: AsyncEventData) -> None: ...  # type: ignore[override]
+    async def dispatch(self, trigger: str, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...  # type: ignore[override]
+    async def callbacks(self, funcs: Iterable[Callback], event_data: AsyncEventData) -> None: ...  # type: ignore[override]
+    async def callback(self, func: AsyncCallback, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
     @staticmethod
-    async def await_all(callables: List[Callable]) -> List: ...
+    async def await_all(callables: List[AsyncCallbackFunc]) -> Awaitable[List[Any]]: ...
     async def switch_model_context(self, model: object) -> None: ...
     def get_state(self, state: Union[str, Enum]) -> AsyncState: ...
-    async def process_context(self, func: partial, model: object) -> bool: ...
+    async def process_context(self, func: Callable[[], Awaitable[None]], model: object) -> bool: ...
     def remove_model(self, model: object) -> None: ...
-    def _process(self, trigger: partial) -> bool: ...
-    async def _process_async(self, trigger: partial, model: object) -> bool: ...
+    async def _process_async(self, trigger: Callable[[], Awaitable[None]], model: object) -> bool: ...
 
 
 class HierarchicalAsyncMachine(HierarchicalMachine, AsyncMachine):  # type: ignore
@@ -93,7 +95,7 @@ class HierarchicalAsyncMachine(HierarchicalMachine, AsyncMachine):  # type: igno
     transition_cls: Type[NestedAsyncTransition]
     event_cls: Type[NestedAsyncEvent]  # type: ignore
     async def trigger_event(self, model: object, trigger: str, # type: ignore[override]
-                            *args: List, **kwargs: Dict[str, Any]) -> bool: ...
+                            *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
     async def _trigger_event(self, event_data: AsyncEventData, trigger: str) -> bool: ...  # type: ignore[override]
 
 
@@ -101,18 +103,18 @@ class AsyncTimeout(AsyncState):
     dynamic_methods: List[str]
     timeout: float
     _on_timeout: CallbacksArg
-    runner: Dict[int, Task]
-    def __init__(self, *args: List, **kwargs: Dict[str, Any]) -> None: ...
+    runner: Dict[int, Task[Any]]
+    def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     async def enter(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
     async def exit(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
-    def create_timer(self, event_data: AsyncEventData) -> Task: ...
+    def create_timer(self, event_data: AsyncEventData) -> Task[Any]: ...
     async def _process_timeout(self, event_data: AsyncEventData) -> None: ...
     @property
     def on_timeout(self) -> CallbackList: ...
     @on_timeout.setter
     def on_timeout(self, value: CallbacksArg) -> None: ...
 
-class _DictionaryMock(dict):
+class _DictionaryMock(Dict[Any, Any]):
     _value: Any
     def __init__(self, item: Any) -> None: ...
     def __setitem__(self, key: Any, item: Any) -> None: ...
diff --git a/transitions/extensions/diagrams.pyi b/transitions/extensions/diagrams.pyi
index b42cdcfe..2fd98c23 100644
--- a/transitions/extensions/diagrams.pyi
+++ b/transitions/extensions/diagrams.pyi
@@ -17,7 +17,7 @@ GraphvizParameters = Dict[str, Union[str, Dict[str, Any]]]
 
 class TransitionGraphSupport(Transition):
     label: str
-    def __init__(self, *args: List, **kwargs: Dict[str, Any]) -> None: ...
+    def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     def _change_state(self, event_data: EventData) -> None: ...
 
 
diff --git a/transitions/extensions/diagrams_base.pyi b/transitions/extensions/diagrams_base.pyi
index 97df1f60..f63b3ebc 100644
--- a/transitions/extensions/diagrams_base.pyi
+++ b/transitions/extensions/diagrams_base.pyi
@@ -1,5 +1,5 @@
 import abc
-from typing import Protocol, Optional, Union, List, Dict, IO, Tuple, Generator
+from typing import BinaryIO, Protocol, Optional, Union, List, Dict, Tuple, Generator
 
 from .diagrams import GraphMachine, HierarchicalGraphMachine
 from ..core import ModelState
@@ -7,7 +7,7 @@ from ..core import ModelState
 
 class GraphProtocol(Protocol):
 
-    def draw(self, filename: Optional[Union[str, IO]], format:Optional[str] = ...,
+    def draw(self, filename: Optional[Union[str, BinaryIO]], format:Optional[str] = ...,
              prog: Optional[str] = ..., args:str = ...) -> Optional[str]: ...
 
 class GraphModelProtocol(Protocol):
diff --git a/transitions/extensions/diagrams_graphviz.pyi b/transitions/extensions/diagrams_graphviz.pyi
index ddd76ab3..b81f7f43 100644
--- a/transitions/extensions/diagrams_graphviz.pyi
+++ b/transitions/extensions/diagrams_graphviz.pyi
@@ -2,7 +2,7 @@ from ..core import State, ModelState
 from .diagrams import GraphMachine
 from .diagrams_base import BaseGraph
 from logging import Logger
-from typing import Type, Optional, Dict, List, Union, IO, DefaultDict, Any
+from typing import BinaryIO, Type, Optional, Dict, List, Union, DefaultDict, Any
 try:
     from graphviz import Digraph
     from graphviz.dot import SubgraphContext
@@ -16,7 +16,7 @@ except ImportError:
 _LOGGER: Logger
 
 class Graph(BaseGraph):
-    custom_styles: Dict[str, DefaultDict]
+    custom_styles: Dict[str, DefaultDict[str, Union[str, DefaultDict[str, str]]]]
     def __init__(self, machine: Type[GraphMachine]) -> None: ...
     def set_previous_transition(self, src: str, dst: str) -> None: ...
     def set_node_style(self, state: ModelState, style: str) -> None: ...
@@ -28,12 +28,12 @@ class Graph(BaseGraph):
     def generate(self) -> None: ...
     def get_graph(self, title: Optional[str] = ...,  # type: ignore[no-any-unimported]
                   roi_state: Optional[str] = ...) -> Digraph: ...
-    def draw(self, filename: Optional[Union[str, IO]], format:Optional[str] = ...,
+    def draw(self, filename: Optional[Union[str, BinaryIO]], format:Optional[str] = ...,
              prog: Optional[str] = ..., args:str = ...) -> Optional[str]: ...
 
 class NestedGraph(Graph):
     _cluster_states: List[str]
-    def __init__(self, *args: List, **kwargs: Dict[str, Any]) -> None: ...
+    def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     def set_previous_transition(self, src: str, dst: str) -> None: ...
     def _add_nodes(self, states: List[Dict[str, str]],  # type: ignore[no-any-unimported]
                    container: Union[Digraph, SubgraphContext]) -> None: ...
diff --git a/transitions/extensions/diagrams_pygraphviz.pyi b/transitions/extensions/diagrams_pygraphviz.pyi
index 56cdb1ba..2a82a657 100644
--- a/transitions/extensions/diagrams_pygraphviz.pyi
+++ b/transitions/extensions/diagrams_pygraphviz.pyi
@@ -30,7 +30,7 @@ class Graph(BaseGraph):
 
 class NestedGraph(Graph):
     seen_transitions: Any
-    def __init__(self, *args: List, **kwargs: Dict[str, Any]) -> None: ...
+    def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     def _add_nodes(self,  # type: ignore[override, no-any-unimported]
                    states: List[Dict[str, Union[str, List[Dict[str, str]]]]],
                    container: AGraph, prefix: str = ..., default_style: str = ...) -> None: ...
diff --git a/transitions/extensions/factory.pyi b/transitions/extensions/factory.pyi
index f7e49163..f329b4fc 100644
--- a/transitions/extensions/factory.pyi
+++ b/transitions/extensions/factory.pyi
@@ -1,8 +1,8 @@
-from ..core import Machine, State
+from ..core import CallbackFunc, Machine, State
 from .diagrams import GraphMachine, NestedGraphTransition, HierarchicalGraphMachine
 from .locking import LockedMachine
 from .nesting import HierarchicalMachine, NestedEvent
-from typing import Type, Dict, Tuple, Callable, Union
+from typing import Any, Type, Dict, Tuple, Callable, Union
 
 try:
     from transitions.extensions.asyncio import AsyncMachine, AsyncTransition
@@ -39,13 +39,13 @@ class LockedHierarchicalMachine(LockedMachine, HierarchicalMachine):  # type: ig
 
 class LockedGraphMachine(GraphMachine, LockedMachine):  # type: ignore
     @staticmethod
-    def format_references(func: Callable) -> str: ...
+    def format_references(func: CallbackFunc) -> str: ...
 
 class LockedHierarchicalGraphMachine(GraphMachine, LockedHierarchicalMachine):  # type: ignore
     transition_cls: Type[NestedGraphTransition]
     event_cls: Type[NestedEvent]
     @staticmethod
-    def format_references(func: Callable) -> str: ...
+    def format_references(func: CallbackFunc) -> str: ...
 
 class AsyncGraphMachine(GraphMachine, AsyncMachine):
     # AsyncTransition already considers graph models when necessary
diff --git a/transitions/extensions/locking.pyi b/transitions/extensions/locking.pyi
index 36cf68ff..f201c14e 100644
--- a/transitions/extensions/locking.pyi
+++ b/transitions/extensions/locking.pyi
@@ -1,17 +1,18 @@
+from contextlib import AbstractContextManager
 from transitions.core import Event, Machine, ModelParameter, TransitionConfig, CallbacksArg, StateConfig
-from typing import Any, Dict, ContextManager, Literal, Optional, Type, List, DefaultDict, Union, Callable, Sequence
+from typing import Any, Dict, Literal, Optional, Type, List, DefaultDict, Union, Callable, Sequence
 from types import TracebackType
 from logging import Logger
 from threading import Lock
+from enum import Enum
 
 from ..core import StateIdentifier, State
 
 _LOGGER: Logger
 
-from enum import Enum
-
+LockContext = AbstractContextManager[None]
 
-class PicklableLock(ContextManager):
+class PicklableLock(LockContext):
     lock: Lock
     def __init__(self) -> None: ...
     def __getstate__(self) -> Dict[str, Any]: ...
@@ -20,7 +21,7 @@ class PicklableLock(ContextManager):
     def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException],
                  exc_tb: Optional[TracebackType]) -> None: ...
 
-class IdentManager(ContextManager):
+class IdentManager(LockContext):
     current: int
     def __init__(self) -> None: ...
     def __enter__(self) -> None: ...
@@ -29,14 +30,14 @@ class IdentManager(ContextManager):
 
 class LockedEvent(Event):
     machine: LockedMachine
-    def trigger(self, model: object, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
+    def trigger(self, model: object, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
 
 
 class LockedMachine(Machine):
     event_cls: Type[LockedEvent]
     _ident: IdentManager
-    machine_context: List[ContextManager]
-    model_context_map: DefaultDict[int, List[ContextManager]]
+    machine_context: List[LockContext]
+    model_context_map: DefaultDict[int, List[LockContext]]
     def __init__(self, model: Optional[ModelParameter] = ...,
                  states: Optional[Union[Sequence[StateConfig], Type[Enum]]] = ...,
                  initial: Optional[StateIdentifier] = ...,
@@ -47,17 +48,17 @@ class LockedMachine(Machine):
                  name: str = ..., queued: bool = ...,
                  prepare_event: CallbacksArg = ..., finalize_event: CallbacksArg = ...,
                  model_attribute: str = ..., on_exception: CallbacksArg = ...,
-                 machine_context: Optional[Union[List[ContextManager], ContextManager]] = ...,
+                 machine_context: Optional[Union[List[LockContext], LockContext]] = ...,
                  **kwargs: Dict[str, Any]) -> None: ...
     def __getstate__(self) -> Dict[str, Any]: ...
     def __setstate__(self, state: Dict[str, Any]) -> None: ...
     def add_model(self, model:  Union[Union[Literal['self'], object], List[Union[Literal['self'], object]]],
                   initial: Optional[StateIdentifier] = ...,
-                  model_context: Optional[Union[ContextManager, List[ContextManager]]] = ...) -> None: ...
+                  model_context: Optional[Union[LockContext, List[LockContext]]] = ...) -> None: ...
     def remove_model(self, model: Union[Union[Literal['self'], object],
                                         List[Union[Literal['self'], object]]]) -> None: ...
     def __getattribute__(self, item: str) -> Any: ...
     def __getattr__(self, item: str) -> Any: ...
     def _add_model_to_state(self, state: State, model: object) -> None: ...
     def _get_qualified_state_name(self, state: State) -> str: ...
-    def _locked_method(self, func: Callable, *args: List, **kwargs: Dict[str, Any]) -> Any: ...
+    def _locked_method(self, func: Callable[..., Any], *args: List[Any], **kwargs: Dict[str, Any]) -> Any: ...
diff --git a/transitions/extensions/markup.pyi b/transitions/extensions/markup.pyi
index c4ea6efa..aa580a70 100644
--- a/transitions/extensions/markup.pyi
+++ b/transitions/extensions/markup.pyi
@@ -1,6 +1,6 @@
 import numbers
 
-from ..core import Machine, StateIdentifier, CallbacksArg, StateConfig, Event, TransitionConfig, ModelParameter
+from ..core import CallbackFunc, Machine, StateIdentifier, CallbacksArg, StateConfig, Event, TransitionConfig, ModelParameter
 from .nesting import HierarchicalMachine
 from typing import  List, Dict, Union, Optional, Callable, Tuple, Any, Type, Sequence, TypedDict
 
@@ -46,7 +46,7 @@ class MarkupMachine(Machine):
                    on_enter: CallbacksArg = ..., on_exit: CallbacksArg = ...,
                    ignore_invalid_triggers: Optional[bool] = ..., **kwargs: Dict[str, Any]) -> None: ...
     @staticmethod
-    def format_references(func: Callable) -> str: ...
+    def format_references(func: CallbackFunc) -> str: ...
     def _convert_states_and_transitions(self, root: MarkupConfig) -> None: ...
     def _convert_states(self, root: MarkupConfig) -> None: ...
     def _convert_transitions(self, root: MarkupConfig) -> None: ...
@@ -61,7 +61,7 @@ class HierarchicalMarkupMachine(MarkupMachine, HierarchicalMachine):  # type: ig
     pass
 
 
-def rep(func: Union[Callable, str, numbers.Number],
-        format_references: Optional[Callable] = ...) -> str: ...
-def _convert(obj: object, attributes: List[str], format_references: Optional[Callable]) -> MarkupConfig: ...
+def rep(func: Union[CallbackFunc, str, Enum],
+        format_references: Optional[Callable[[CallbackFunc], str]] = ...) -> str: ...
+def _convert(obj: object, attributes: List[str], format_references: Optional[Callable[[CallbackFunc], str]]) -> MarkupConfig: ...
 
diff --git a/transitions/extensions/nesting.pyi b/transitions/extensions/nesting.pyi
index 0f8b7f72..b01e3668 100644
--- a/transitions/extensions/nesting.pyi
+++ b/transitions/extensions/nesting.pyi
@@ -1,4 +1,4 @@
-from ..core import Event, EventData, Machine, State, Transition, CallbacksArg, Callback, ModelParameter, TransitionConfig
+from ..core import CallbackFunc, Event, EventData, Machine, State, Transition, CallbacksArg, Callback, ModelParameter, TransitionConfig
 from collections import defaultdict as defaultdict
 from typing import OrderedDict, Sequence, Union, List, Dict, Optional, Type, Tuple, Callable, Any, Collection
 from types import TracebackType
@@ -9,10 +9,10 @@ from functools import partial
 _LOGGER: Logger
 
 class FunctionWrapper:
-    _func: Optional[Callable]
-    def __init__(self, func: Callable, path: List[str]) -> None: ...
-    def add(self, func: Callable, path: List[str]) -> None: ...
-    def __call__(self, *args: List, **kwargs: Dict[str, Any]) -> Any: ...
+    _func: Optional[CallbackFunc]
+    def __init__(self, func: CallbackFunc, path: List[str]) -> None: ...
+    def add(self, func: CallbackFunc, path: List[str]) -> None: ...
+    def __call__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> Any: ...
 
 
 class NestedEvent(Event):
@@ -55,13 +55,13 @@ def _build_state_list(state_tree: StateTree, separator: str,
 def resolve_order(state_tree: Dict[str, str]) -> List[List[str]]: ...
 
 class NestedTransition(Transition):
-    def _resolve_transition(self, event_data: NestedEventData) -> Tuple[StateTree, List[partial], List[partial]]: ...
+    def _resolve_transition(self, event_data: NestedEventData) -> Tuple[StateTree, List[Callable[[], None]], List[Callable[[], Any]]]: ...
     def _change_state(self, event_data: NestedEventData) -> None: ...  # type: ignore[override]
     def _enter_nested(self, root: List[str], dest: List[str], prefix_path: List[str],
-                      event_data: NestedEventData) -> Tuple[StateTree, List[partial]]: ...
+                      event_data: NestedEventData) -> Tuple[StateTree, List[Callable[[], None]]]: ...
     @staticmethod
     def _update_model(event_data: NestedEventData, tree: StateTree) -> None: ...
-    def __deepcopy__(self, memo: Dict) -> NestedTransition: ...
+    def __deepcopy__(self, memo: Dict[str, Any]) -> NestedTransition: ...
 
 ScopeTuple = Tuple[Union[NestedState, 'HierarchicalMachine'], OrderedDict[str, NestedState],
                    Dict[str, NestedEvent], List[str]]
@@ -126,8 +126,8 @@ class HierarchicalMachine(Machine):
     def on_exit(self, state_name: str, callback: Callback) -> None: ...
     def set_state(self, state: Union[NestedStateIdentifier, List[NestedStateIdentifier]],  # type: ignore[override]
                   model: Optional[object] = ...) -> None: ...
-    def to_state(self, model: object, state_name: str, *args: List, **kwargs: Dict[str, Any]) -> None: ...
-    def trigger_event(self, model: object, trigger: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
+    def to_state(self, model: object, state_name: str, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
+    def trigger_event(self, model: object, trigger: str, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
     def _add_model_to_state(self, state: NestedState, model: object) -> None: ...  # type: ignore[override]
     def _add_dict_state(self, state: Dict[str, Any], ignore_invalid_triggers: bool, remap: Optional[Dict[str, str]],
                         **kwargs: Dict[str, Any]) -> None: ...
@@ -141,15 +141,15 @@ class HierarchicalMachine(Machine):
     def build_state_tree(self, model_states: Union[str, Enum, Sequence[Union[str, Enum, Sequence[Any]]]],
                          separator: str, tree: Optional[StateTree] = ...) -> StateTree: ...
     @classmethod
-    def _create_transition(cls, *args: List, **kwargs: Dict[str, Any]) -> NestedTransition: ...
+    def _create_transition(cls, *args: List[Any], **kwargs: Dict[str, Any]) -> NestedTransition: ...
     @classmethod
-    def _create_event(cls, *args: List, **kwargs: Dict[str, Any]) -> NestedEvent: ...
+    def _create_event(cls, *args: List[Any], **kwargs: Dict[str, Any]) -> NestedEvent: ...
     @classmethod
-    def _create_state(cls, *args: List, **kwargs: Dict[str, Any]) -> NestedState: ...
+    def _create_state(cls, *args: List[Any], **kwargs: Dict[str, Any]) -> NestedState: ...
     def _get_enum_path(self, enum_state: Enum, prefix: Optional[List[str]] =...) -> List[str]: ...
     def _get_state_path(self, state: NestedState, prefix: Optional[List[str]] = ...) -> List[str]: ...
     def _check_event_result(self, res: bool, model: object, trigger: str) -> bool: ...
-    def _get_trigger(self, model: object, trigger_name: str, *args: List, **kwargs: Dict[str, Any]) -> bool: ...
+    def _get_trigger(self, model: object, trigger_name: str, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
     def _has_state(self, state: NestedState, raise_error: bool = ...) -> bool: ...  # type: ignore[override]
     def _init_state(self, state: NestedState) -> None: ...
     def _recursive_initial(self, value: NestedStateIdentifier) -> Union[str, List[str]]: ...
diff --git a/transitions/extensions/states.pyi b/transitions/extensions/states.pyi
index c7354d03..882204a2 100644
--- a/transitions/extensions/states.pyi
+++ b/transitions/extensions/states.pyi
@@ -19,7 +19,7 @@ class Timeout(State):
     timeout: float
     _on_timeout: Optional[List[Callback]]
     runner: Dict[int, Timer]
-    def __init__(self, *args: List, **kwargs: Dict[str, Any]) -> None: ...
+    def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     def enter(self, event_data: EventData) -> None: ...
     def exit(self, event_data: EventData) -> None: ...
     def _process_timeout(self, event_data: EventData) -> None: ...
@@ -32,10 +32,10 @@ class Volatile(State):
     volatile_cls: Any
     volatile_hook: str
     initialized: bool
-    def __init__(self, *args: List, **kwargs: Dict[str, Any]) -> None: ...
+    def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     def enter(self, event_data: EventData) -> None: ...
     def exit(self, event_data: EventData) -> None: ...
 
-def add_state_features(*args: Type) -> Any: ...
+def add_state_features(*args: Union[Type[State], List[Type[State]]]) -> Any: ...
 
 class VolatileObject: ...


Test patch:
diff --git a/tests/test_codestyle.py b/tests/test_codestyle.py
index 69092540..b952447f 100644
--- a/tests/test_codestyle.py
+++ b/tests/test_codestyle.py
@@ -26,7 +26,7 @@ def test_conformance(self):
 
     @unittest.skipIf(mypy is None, "mypy not found")
     def test_mypy_package(self):
-        call = ['mypy', '--config-file', 'mypy.ini', 'transitions']
+        call = ['mypy', '--config-file', 'mypy.ini', '--strict', 'transitions', 'tests/test_imports.py']
 
         # when run from root directory (e.g. tox) else when run from test directory (e.g. pycharm)
         project_root = '.' if exists('transitions') else '..'
diff --git a/tests/test_imports.py b/tests/test_imports.py
new file mode 100644
index 00000000..64827fda
--- /dev/null
+++ b/tests/test_imports.py
@@ -0,0 +1,11 @@
+def test_imports() -> None:
+    from transitions import Machine
+    from transitions.extensions import GraphMachine, HierarchicalGraphMachine, HierarchicalMachine, LockedMachine
+    from transitions.extensions import MachineFactory, LockedHierarchicalGraphMachine, LockedHierarchicalMachine
+    from transitions.extensions import LockedGraphMachine
+    try:
+        # only available for Python 3
+        from transitions.extensions import AsyncMachine, HierarchicalAsyncMachine
+        from transitions.extensions import AsyncGraphMachine, HierarchicalAsyncGraphMachine
+    except (ImportError, SyntaxError):  # pragma: no cover
+        pass