Source code for classes.type_protocols

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