Source code for classes.type_protocols

  1"""
  2Helper library to define type protocols for classes and functions in the project.
  3
  4Note:
  5    That some types in the documentation have been simplified, e.g. some generics have been
  6    omitted.
  7
  8See Also:
  9    Useful references:
 10        - https://docs.python.org/3/library/typing.html
 11        - https://docs.python.org/3/library/stdtypes.html#types-genericalias
 12        - https://typing-extensions.readthedocs.io/en/latest/
 13"""
 14
 15# pyright: strict
 16
 17from __future__ import annotations
 18
 19import sys
 20from typing import TYPE_CHECKING, Any, Callable, Hashable, Optional, Sequence, Union
 21
 22import carla  # type: ignore
 23from typing_extensions import Concatenate, Literal, ParamSpec, Protocol, TypeAlias, TypeAliasType, TypeVar
 24
 25if TYPE_CHECKING:
 26    from agents.dynamic_planning.dynamic_local_planner import DynamicLocalPlanner  # noqa: F401 ; used for typing
 27    from agents.navigation.local_planner import LocalPlanner
 28    from agents.tools.config_creation import AgentConfig, BehaviorAgentSettings, LunaticAgentSettings  # noqa: F401
 29    from classes.constants import AgentState
 30    from classes.evaluation_function import ConditionFunction
 31    from classes.rule import Context, Rule
 32    from classes.worldmodel import WorldModel
 33    from classes.ui.keyboard_controls import KeyboardControl
 34
 35__all__ = [  # noqa: RUF022
 36    "AgentConfigT",
 37    "RuleT",
 38    "CallableT",
 39    # Callables
 40    "CallableCondition",
 41    "CallableConditionT",
 42    "AnyCallableCondition",
 43    # ConditionFunction
 44    "ConditionFunctionLike",
 45    "ConditionFunctionLikeT",
 46    "AnyConditionFunctionLike",
 47    "AnyConditionFunctionLikeT",
 48    # Action
 49    "CallableAction",
 50    "CallableActionT",
 51    "AnyCallableAction",
 52    # Protocols
 53    "HasBaseSettings",
 54    "HasConfig",
 55    "HasContext",
 56    "Has_WorldModel",
 57    "HasStates",
 58    "Has_Vehicle",
 59    "HasPlanner",
 60    "HasPlannerWithConfig",
 61    "UseableWithDynamicPlanner",
 62    "CanDetectObstacles",
 63    "CanDetectNearbyObstacles",
 64    "CanDetectNearbyTrafficLights",
 65]
 66
 67_T = TypeVar("_T", default=Any)
 68_CH = TypeVar("_CH", bound=Hashable, default=Hashable)  # Generic of ConditionFunction
 69
 70_P = ParamSpec("_P", default=[])  # Free, e.g. for action function.
 71_CP = ParamSpec("_CP", default=[])  # Generic of ConditionFunction
 72
 73RuleT = TypeVar("RuleT", bound="Rule", default="Rule")
 74"""A :py:class:`typing.TypeVar`: for a :py:class:`.Rule` type."""
 75
 76_A = TypeVar("_A", bound=carla.Actor, default=carla.Actor)
 77
 78# NOTE: This requires a stub file where ActorList is Generic
 79if TYPE_CHECKING:
 80    _Generic_carlaActorList = carla.ActorList[_A]
 81else:
 82    _Generic_carlaActorList = TypeAliasType("_Generic_carlaActorList", carla.ActorList, type_params=(_A,))
 83
 84ActorList: TypeAlias = Union[_Generic_carlaActorList[_A], Sequence[_A]]
 85"""Type alias for a sequence of carla actors."""
 86
 87ControllerClassT = TypeVar("ControllerClassT", bound="KeyboardControl")
 88"""A :py:class:`typing.TypeVar`: for a :py:class:`.KeyboardControl` type."""
 89
 90
 91CallableCondition: TypeAlias = Union[
 92    Callable[Concatenate[RuleT, "Context", _CP], _CH],  # With Rule
 93    Callable[Concatenate["Context", _CP], _CH],  # Only Context
 94]
 95"""
 96A :term:`generic type` alias for a callable condition function to be used with a :py:class:`.ConditionFunction`.
 97Its first arguments must accept a :py:class:`.Rule` and a :py:class:`.Context`,
 98or only a :py:class:`.Context`, additional keyword arguments are allowed.
 99It must return a :term:`hashable` value.
100"""
101
102CallableAction: TypeAlias = Union[
103    Callable[Concatenate[RuleT, "Context", _P], _T],  # With Rule
104    Callable[Concatenate["Context", _P], _T],  # Only Context
105]
106"""
107A :term:`generic type` alias for a callable action function to be used with a :py:class:`.Rule`
108or :py:meth:`.ConditionFunction.register_action`.
109Its first arguments must accept a :py:class:`.Rule` and a :py:class:`.Context`,
110or only a :py:class:`.Context`, additional keyword arguments are allowed.
111It can return an arbitrary value.
112"""
113
114
115CallableT = TypeVar("CallableT", bound=Callable[..., Any])
116"""A :py:class:`typing.TypeVar`: for a any callable."""
117
118AgentConfigT = TypeVar("AgentConfigT", bound="AgentConfig", default="AgentConfig", infer_variance=True)
119"""A :py:class:`typing.TypeVar`: for a :py:class:`.AgentConfig` type."""
120
121ConditionFunctionLike = TypeAliasType(
122    "ConditionFunctionLike",
123    Union[CallableCondition[RuleT, _CP, _CH], "ConditionFunction[_CP, _CH]"],
124    type_params=(RuleT, _CP, _CH),
125)
126"""
127Callable that can be used for :py:attr:`.Rule.condition`.
128A callable that uses a :py:class:`Context` object as a single argument,
129or alternatively a :py:class:`Rule` and a :py:class:`Context` object (in this order).
130
131The function must return a :term:`Hashable` value.
132"""
133
134ConditionFunctionLikeT = ...  # Version limitation, fixing done below
135""":py:class:`.TypeVar` version of :py:obj:`ConditionFunctionLike`"""
136
137
138AnyConditionFunctionLike = TypeAliasType(
139    "AnyConditionFunctionLike",
140    Union[CallableCondition[RuleT, _CP, _CH], "ConditionFunction[_CP, _CH]"],
141    type_params=(RuleT, _CP, _CH),
142)
143"""
144A :term:`generic type` alias for a callable condition function to be used with a :py:class:`.Rule`.
145Its first arguments must accept a :py:class:`.Rule` and a :py:class:`.Context`,
146or only a :py:class:`.Context`, additional keyword arguments are allowed.
147It must return a :term:`hashable` value.
148"""
149
150AnyConditionFunctionLikeT = TypeVar("AnyConditionFunctionLikeT", bound=AnyConditionFunctionLike)
151
152# Python 3.11+
153if TYPE_CHECKING or sys.version_info >= (3, 11):
154    AnyCallableCondition: TypeAlias = CallableCondition[RuleT, ..., Hashable]
155    """Non generic variant of :py:obj:`CallableCondition`, can use used as :py:class:`typing.TypeAlias`."""
156
157    AnyCallableAction: TypeAlias = CallableAction[RuleT, ..., Any]
158    """Non generic variant of :py:obj:`CallableAction`, can use used as :py:class:`typing.TypeAlias`."""
159
160    ConditionFunctionLikeT = TypeVar("ConditionFunctionLikeT", bound=ConditionFunctionLike["Rule", ..., Hashable])
161    """:py:class:`.TypeVar` version of :py:obj:`ConditionFunctionLike`"""
162
163# handle version conflicts with ParamSpec, Concatenate and Ellipsis
164# NOTE: Near-Future typing_extension upgrades should make this easier.
165elif sys.version_info[:2] <= (3, 10):
166    __ellipsis_dummy = ParamSpec("__ellipsis_dummy")
167    # Create valid concatenate types
168    __ConcatRC = Concatenate["Rule", "Context", __ellipsis_dummy]
169    __ConcatC = Concatenate["Context", __ellipsis_dummy]
170
171    AnyCallableAction = CallableAction["Rule", __ellipsis_dummy, Any]
172    AnyCallableCondition = CallableCondition["Rule", __ellipsis_dummy, Hashable]
173
174    __ConditionFunctionLikeBound = ConditionFunctionLike["Rule", __ellipsis_dummy, Hashable]
175
176    # Remove dummy parameter
177    __ConcatRC.__args__ = (*__ConcatRC.__args__[:-1], ...)
178    __ConcatC.__args__ = (*__ConcatC.__args__[:-1], ...)
179    __ConditionFunctionLikeBound.__args__ = tuple(
180        a if a is not __ellipsis_dummy else ... for a in __ConditionFunctionLikeBound.__args__
181    )
182    AnyCallableCondition.__args__[0].__args__ = (__ConcatRC, Hashable)
183    AnyCallableCondition.__args__[1].__args__ = (__ConcatC, Hashable)
184    AnyCallableAction.__args__[0].__args__ = (__ConcatRC, Any)
185    AnyCallableAction.__args__[1].__args__ = (__ConcatC, Any)
186    AnyCallableCondition.__parameters__ = AnyCallableCondition.__args__[
187        0
188    ].__parameters__ = AnyCallableCondition.__args__[1].__parameters__ = (
189        AnyCallableAction.__parameters__
190    ) = AnyCallableAction.__args__[0].__parameters__ = AnyCallableAction.__args__[1].__parameters__ = (
191        __ConditionFunctionLikeBound.__parameters__
192    ) = ()
193
194    ConditionFunctionLikeT = TypeVar("ConditionFunctionLikeT", bound=__ConditionFunctionLikeBound)
195else:
196    # Works for older typing_extensions versions, e.g. 4.10.0 and Python3.10
197    AnyCallableCondition = CallableCondition["Rule", Concatenate[...], Hashable]
198    AnyCallableAction = CallableAction["Rule", Concatenate[...], Any]
199
200CallableConditionT = TypeVar("CallableConditionT", bound=AnyCallableCondition)
201""":py:class:`typing.TypeVar` variant of :py:obj:`AnyCallableCondition`."""
202
203CallableActionT = TypeVar("CallableActionT", bound=AnyCallableAction)
204""":py:class:`typing.TypeVar` variant of :py:obj:`AnyCallableAction`."""
205
206
207# ------------- Protocols -------------
208
209
[docs] 210class HasBaseSettings(Protocol[AgentConfigT]): 211 BASE_SETTINGS: type[AgentConfigT]
212 213
[docs] 214class HasConfig(Protocol[AgentConfigT]): 215 @property # Note: Must be read-only, can be normal attribute when implemented 216 def config(self) -> AgentConfigT: 217 """ 218 read-only attribute of a :py:class:`.AgentConfig` object; can also be a normal attribute. 219 """ 220 ...
221 222 223LocalPlannerT = TypeVar( 224 "LocalPlannerT", bound="LocalPlanner", default="LocalPlanner", infer_variance=True 225) # is covariant, but avoid _co style for documentation 226"""A :py:class:`typing.TypeVar`: for a :py:class:`.LocalPlanner` type.""" 227 228
[docs] 229class HasPlanner(Protocol[LocalPlannerT]): 230 """ 231 Uses a Local planner to calculate controls 232 """ 233 234 @property 235 def _local_planner(self) -> LocalPlannerT: 236 """ 237 read-only attribute for a :py:class:`.LocalPlanner` object; can also be a normal attribute. 238 239 :meta public: 240 """ 241 ... 242
[docs] 243 def _calculate_control(self, debug: bool = False, *args, **kwargs) -> carla.VehicleControl: # pyright: ignore 244 """ 245 :meta public: 246 """ 247 ...
248 249
[docs] 250class HasPlannerWithConfig(HasPlanner["DynamicLocalPlanner"], HasConfig[AgentConfigT], Protocol): 251 """ 252 Uses a :py:class:`.DynamicLocalPlanner` that works with a :py:class:`.AgentConfig` 253 """
254 255
[docs] 256class HasContext(Protocol): 257 ctx: "Context"
258 259
[docs] 260class Has_WorldModel(Protocol): 261 _world_model: "WorldModel" 262 """ 263 :meta public: 264 """
265 266
[docs] 267class HasStates(Protocol): 268 current_states: dict["AgentState", int]
269 270
[docs] 271class Has_Vehicle(Protocol): 272 _vehicle: carla.Vehicle 273 """ 274 :meta public: 275 """
276 277
[docs] 278class UseableWithDynamicPlanner(HasPlannerWithConfig, Has_Vehicle, HasContext, Protocol): 279 """Can be used with :py:class:`.DynamicLocalPlanner`."""
280 281
[docs] 282class CanDetectObstacles(Has_Vehicle, HasPlanner, HasConfig["BehaviorAgentSettings | LunaticAgentSettings"], Protocol): 283 """Can be used with :py:func:`lunatic_agent_tools.detect_obstacles`."""
284 285
[docs] 286class CanDetectNearbyObstacles(CanDetectObstacles, Protocol): 287 """Can be used with :py:func:`lunatic_agent_tools.detect_obstacles_in_path`.""" 288 289 all_obstacles_nearby: list[carla.Actor] 290 """Actors that are considered to be near the actor.""" 291
[docs] 292 def max_detection_distance(self, lane: Literal["same_lane", "other_lane"]) -> float: 293 """ 294 Convenience function to be used with :py:func:`lunatic_agent_tools.detect_vehicles` 295 and :any:`LunaticAgent.detect_obstacles_in_path`. 296 297 The max distance to consider an obstacle in the same lane or in another lane, obstacles 298 further away will be ignored. 299 300 Parameters: 301 self : An object that implements the `config` and `live_info` attributes 302 lane : The lane to consider. 303 """ 304 ...
305 306
[docs] 307class CanDetectNearbyTrafficLights(CanDetectObstacles, HasStates, Has_WorldModel, Protocol): 308 """Can be used with :py:func:`lunatic_agent_tools.detect_obstacles_in_path`.""" 309 310 traffic_lights_nearby: list[carla.TrafficLight] 311 """Actors that are considered to be near the actor.""" 312 313 _last_traffic_light: Optional[carla.TrafficLight] 314 """ 315 Last traffic light that was detected. At a red traffic light it can be checked if this is still 316 red. 317 318 Attention: 319 Not necessarily the same as :py:attr:`.InformationManager.Information.relevant_traffic_light`. 320 321 :meta public: 322 """ 323 324 _current_waypoint: carla.Waypoint 325 """ 326 :meta public: 327 """