Skip to content

Commit 06dc66b

Browse files
Improve type checking (#679)
1 parent a9a9bcf commit 06dc66b

File tree

62 files changed

+477
-248
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+477
-248
lines changed

launch/doc/source/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333

3434
# -- Project information -----------------------------------------------------
35+
# type: ignore
3536

3637
project = 'launch'
3738
copyright = '2018, Open Source Robotics Foundation, Inc.' # noqa

launch/examples/disable_emulate_tty_counters.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
import os
2626
import sys
27-
from typing import cast
2827
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # noqa
2928

3029
import launch # noqa: E402
@@ -37,10 +36,9 @@ def generate_launch_description():
3736
ld.add_action(launch.actions.SetLaunchConfiguration('emulate_tty', 'false'))
3837

3938
# Wire up stdout from processes
40-
def on_output(event: launch.Event) -> None:
39+
def on_output(event: launch.events.process.ProcessIO) -> None:
4140
for line in event.text.decode().splitlines():
42-
print('[{}] {}'.format(
43-
cast(launch.events.process.ProcessIO, event).process_name, line))
41+
print('[{}] {}'.format(event.process_name, line))
4442

4543
ld.add_action(launch.actions.RegisterEventHandler(launch.event_handlers.OnProcessIO(
4644
on_stdout=on_output,

launch/examples/launch_counters.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import os
2525
import platform
2626
import sys
27-
from typing import cast
2827
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # noqa
2928

3029
import launch # noqa: E402
@@ -55,10 +54,9 @@ def main(argv=sys.argv[1:]):
5554

5655
# Setup a custom event handler for all stdout/stderr from processes.
5756
# Later, this will be a configurable, but always present, extension to the LaunchService.
58-
def on_output(event: launch.Event) -> None:
57+
def on_output(event: launch.events.process.ProcessIO) -> None:
5958
for line in event.text.decode().splitlines():
60-
print('[{}] {}'.format(
61-
cast(launch.events.process.ProcessIO, event).process_name, line))
59+
print('[{}] {}'.format(event.process_name, line))
6260

6361
ld.add_action(launch.actions.RegisterEventHandler(launch.event_handlers.OnProcessIO(
6462
# this is the action ^ and this, the event handler ^

launch/launch/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
from .launch_description_source import LaunchDescriptionSource
3333
from .launch_introspector import LaunchIntrospector
3434
from .launch_service import LaunchService
35-
from .some_actions_type import SomeActionsType
36-
from .some_actions_type import SomeActionsType_types_tuple
35+
from .some_entities_type import SomeEntitiesType
36+
from .some_entities_type import SomeEntitiesType_types_tuple
3737
from .some_substitutions_type import SomeSubstitutionsType
3838
from .some_substitutions_type import SomeSubstitutionsType_types_tuple
3939
from .substitution import Substitution
@@ -57,8 +57,8 @@
5757
'LaunchDescriptionSource',
5858
'LaunchIntrospector',
5959
'LaunchService',
60-
'SomeActionsType',
61-
'SomeActionsType_types_tuple',
60+
'SomeEntitiesType',
61+
'SomeEntitiesType_types_tuple',
6262
'SomeSubstitutionsType',
6363
'SomeSubstitutionsType_types_tuple',
6464
'Substitution',

launch/launch/action.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Module for Action class."""
1616

17+
from typing import Dict
1718
from typing import Iterable
1819
from typing import List
1920
from typing import Optional
@@ -62,7 +63,7 @@ def parse(entity: 'Entity', parser: 'Parser'):
6263
from .conditions import UnlessCondition
6364
if_cond = entity.get_attr('if', optional=True)
6465
unless_cond = entity.get_attr('unless', optional=True)
65-
kwargs = {}
66+
kwargs: Dict[str, Condition] = {}
6667
if if_cond is not None and unless_cond is not None:
6768
raise RuntimeError("if and unless conditions can't be used simultaneously")
6869
if if_cond is not None:

launch/launch/actions/declare_launch_argument.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
"""Module for the DeclareLaunchArgument action."""
1616

17-
from typing import Iterable
1817
from typing import List
1918
from typing import Optional
2019
from typing import Text
@@ -109,7 +108,7 @@ def __init__(
109108
*,
110109
default_value: Optional[SomeSubstitutionsType] = None,
111110
description: Optional[Text] = None,
112-
choices: Iterable[Text] = None,
111+
choices: List[Text] = None,
113112
**kwargs
114113
) -> None:
115114
"""Create a DeclareLaunchArgument action."""
@@ -196,7 +195,7 @@ def description(self) -> Text:
196195
return self.__description
197196

198197
@property
199-
def choices(self) -> List[Text]:
198+
def choices(self) -> Optional[List[Text]]:
200199
"""Getter for self.__choices."""
201200
return self.__choices
202201

launch/launch/actions/execute_local.py

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
import launch.logging
3535

36-
from osrf_pycommon.process_utils import async_execute_process
36+
from osrf_pycommon.process_utils import async_execute_process # type: ignore
3737
from osrf_pycommon.process_utils import AsyncSubprocessProtocol
3838

3939
from .emit_event import EmitEvent
@@ -62,7 +62,7 @@
6262
from ..launch_context import LaunchContext
6363
from ..launch_description import LaunchDescription
6464
from ..launch_description_entity import LaunchDescriptionEntity
65-
from ..some_actions_type import SomeActionsType
65+
from ..some_entities_type import SomeEntitiesType
6666
from ..some_substitutions_type import SomeSubstitutionsType
6767
from ..substitution import Substitution # noqa: F401
6868
from ..substitutions import LaunchConfiguration
@@ -93,8 +93,8 @@ def __init__(
9393
cached_output: bool = False,
9494
log_cmd: bool = False,
9595
on_exit: Optional[Union[
96-
SomeActionsType,
97-
Callable[[ProcessExited, LaunchContext], Optional[SomeActionsType]]
96+
SomeEntitiesType,
97+
Callable[[ProcessExited, LaunchContext], Optional[SomeEntitiesType]]
9898
]] = None,
9999
respawn: Union[bool, SomeSubstitutionsType] = False,
100100
respawn_delay: Optional[float] = None,
@@ -189,9 +189,16 @@ def __init__(
189189
self.__sigterm_timeout = normalize_to_list_of_substitutions(sigterm_timeout)
190190
self.__sigkill_timeout = normalize_to_list_of_substitutions(sigkill_timeout)
191191
self.__emulate_tty = emulate_tty
192-
self.__output = os.environ.get('OVERRIDE_LAUNCH_PROCESS_OUTPUT', output)
193-
if not isinstance(self.__output, dict):
194-
self.__output = normalize_to_list_of_substitutions(self.__output)
192+
# Note: we need to use a temporary here so that we don't assign values with different types
193+
# to the same variable
194+
tmp_output: SomeSubstitutionsType = os.environ.get(
195+
'OVERRIDE_LAUNCH_PROCESS_OUTPUT', output
196+
)
197+
self.__output: Union[dict, List[Substitution]]
198+
if not isinstance(tmp_output, dict):
199+
self.__output = normalize_to_list_of_substitutions(tmp_output)
200+
else:
201+
self.__output = tmp_output
195202
self.__output_format = output_format
196203

197204
self.__log_cmd = log_cmd
@@ -342,7 +349,7 @@ def __on_signal_process_event(
342349
def __on_process_stdin(
343350
self,
344351
event: ProcessIO
345-
) -> Optional[SomeActionsType]:
352+
) -> Optional[SomeEntitiesType]:
346353
self.__logger.warning(
347354
"in ExecuteProcess('{}').__on_process_stdin_event()".format(id(self)),
348355
)
@@ -351,12 +358,12 @@ def __on_process_stdin(
351358

352359
def __on_process_output(
353360
self, event: ProcessIO, buffer: io.TextIOBase, logger: logging.Logger
354-
) -> Optional[SomeActionsType]:
361+
) -> None:
355362
to_write = event.text.decode(errors='replace')
356363
if buffer.closed:
357364
# buffer was probably closed by __flush_buffers on shutdown. Output without
358365
# buffering.
359-
buffer.info(
366+
logger.info(
360367
self.__output_format.format(line=to_write, this=self)
361368
)
362369
else:
@@ -402,7 +409,7 @@ def __flush_buffers(self, event, context):
402409

403410
def __on_process_output_cached(
404411
self, event: ProcessIO, buffer, logger
405-
) -> Optional[SomeActionsType]:
412+
) -> None:
406413
to_write = event.text.decode(errors='replace')
407414
last_cursor = buffer.tell()
408415
buffer.seek(0, os.SEEK_END) # go to end of buffer
@@ -429,7 +436,7 @@ def __flush_cached_buffers(self, event, context):
429436
self.__output_format.format(line=line, this=self)
430437
)
431438

432-
def __on_shutdown(self, event: Event, context: LaunchContext) -> Optional[SomeActionsType]:
439+
def __on_shutdown(self, event: Event, context: LaunchContext) -> Optional[SomeEntitiesType]:
433440
due_to_sigint = cast(Shutdown, event).due_to_sigint
434441
return self._shutdown_process(
435442
context,
@@ -584,9 +591,14 @@ async def __execute_process(self, context: LaunchContext) -> None:
584591
self.__logger.error("process has died [pid {}, exit code {}, cmd '{}'].".format(
585592
pid, returncode, ' '.join(filter(lambda part: part.strip(), cmd))
586593
))
587-
await context.emit_event(ProcessExited(returncode=returncode, **process_event_args))
594+
await context.emit_event(
595+
ProcessExited(returncode=returncode, **process_event_args)
596+
)
588597
# respawn the process if necessary
589-
if not context.is_shutdown and not self.__shutdown_future.done() and self.__respawn:
598+
if not context.is_shutdown\
599+
and self.__shutdown_future is not None\
600+
and not self.__shutdown_future.done()\
601+
and self.__respawn:
590602
if self.__respawn_delay is not None and self.__respawn_delay > 0.0:
591603
# wait for a timeout(`self.__respawn_delay`) to respawn the process
592604
# and handle shutdown event with future(`self.__shutdown_future`)
@@ -614,7 +626,7 @@ def prepare(self, context: LaunchContext):
614626
# pid is added to the dictionary in the connection_made() method of the protocol.
615627
}
616628

617-
self.__respawn = perform_typed_substitution(context, self.__respawn, bool)
629+
self.__respawn = cast(bool, perform_typed_substitution(context, self.__respawn, bool))
618630

619631
def execute(self, context: LaunchContext) -> Optional[List[LaunchDescriptionEntity]]:
620632
"""
@@ -669,7 +681,9 @@ def execute(self, context: LaunchContext) -> Optional[List[LaunchDescriptionEnti
669681
),
670682
OnProcessExit(
671683
target_action=self,
672-
on_exit=self.__on_exit,
684+
# TODO: This is also a little strange, OnProcessExit shouldn't ever be able to
685+
# take a None for the callable, but this seems to be the default case?
686+
on_exit=self.__on_exit, # type: ignore
673687
),
674688
OnProcessExit(
675689
target_action=self,
@@ -684,9 +698,13 @@ def execute(self, context: LaunchContext) -> Optional[List[LaunchDescriptionEnti
684698
self.__shutdown_future = create_future(context.asyncio_loop)
685699
self.__logger = launch.logging.get_logger(name)
686700
if not isinstance(self.__output, dict):
687-
self.__output = perform_substitutions(context, self.__output)
688-
self.__stdout_logger, self.__stderr_logger = \
689-
launch.logging.get_output_loggers(name, self.__output)
701+
self.__stdout_logger, self.__stderr_logger = \
702+
launch.logging.get_output_loggers(
703+
name, perform_substitutions(context, self.__output)
704+
)
705+
else:
706+
self.__stdout_logger, self.__stderr_logger = \
707+
launch.logging.get_output_loggers(name, self.__output)
690708
context.asyncio_loop.create_task(self.__execute_process(context))
691709
except Exception:
692710
for event_handler in event_handlers:

launch/launch/actions/execute_process.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
from ..frontend import expose_action
2929
from ..frontend import Parser
3030
from ..some_substitutions_type import SomeSubstitutionsType
31+
32+
from ..substitution import Substitution
3133
from ..substitutions import TextSubstitution
3234

3335

@@ -250,7 +252,7 @@ def _parse_cmdline(
250252
:returns: a list of command line arguments.
251253
"""
252254
result_args = []
253-
arg = []
255+
arg: List[Substitution] = []
254256

255257
def _append_arg():
256258
nonlocal arg

launch/launch/actions/opaque_coroutine.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
import asyncio
1818
import collections.abc
1919
from typing import Any
20-
from typing import Coroutine
20+
from typing import Awaitable
21+
from typing import Callable
2122
from typing import Dict
2223
from typing import Iterable
2324
from typing import List
@@ -29,19 +30,19 @@
2930
from ..event_handlers import OnShutdown
3031
from ..launch_context import LaunchContext
3132
from ..launch_description_entity import LaunchDescriptionEntity
32-
from ..some_actions_type import SomeActionsType
33+
from ..some_entities_type import SomeEntitiesType
3334
from ..utilities import ensure_argument_type
3435

3536

3637
class OpaqueCoroutine(Action):
3738
"""
38-
Action that adds a Python coroutine to the launch run loop.
39+
Action that adds a Python coroutine function to the launch run loop.
3940
40-
The signature of a coroutine should be:
41+
The signature of the coroutine function should be:
4142
4243
.. code-block:: python
4344
44-
async def coroutine(
45+
async def coroutine_func(
4546
context: LaunchContext,
4647
*args,
4748
**kwargs
@@ -52,7 +53,7 @@ async def coroutine(
5253
5354
.. code-block:: python
5455
55-
async def coroutine(
56+
async def coroutine_func(
5657
*args,
5758
**kwargs
5859
):
@@ -63,7 +64,7 @@ async def coroutine(
6364

6465
def __init__(
6566
self, *,
66-
coroutine: Coroutine,
67+
coroutine: Callable[..., Awaitable[None]],
6768
args: Optional[Iterable[Any]] = None,
6869
kwargs: Optional[Dict[Text, Any]] = None,
6970
ignore_context: bool = False,
@@ -73,7 +74,7 @@ def __init__(
7374
super().__init__(**left_over_kwargs)
7475
if not asyncio.iscoroutinefunction(coroutine):
7576
raise TypeError(
76-
"OpaqueCoroutine expected a coroutine for 'coroutine', got '{}'".format(
77+
"OpaqueCoroutine expected a coroutine function for 'coroutine', got '{}'".format(
7778
type(coroutine)
7879
)
7980
)
@@ -92,7 +93,7 @@ def __init__(
9293
self.__ignore_context = ignore_context # type: bool
9394
self.__future = None # type: Optional[asyncio.Future]
9495

95-
def __on_shutdown(self, event: Event, context: LaunchContext) -> Optional[SomeActionsType]:
96+
def __on_shutdown(self, event: Event, context: LaunchContext) -> Optional[SomeEntitiesType]:
9697
"""Cancel ongoing coroutine upon shutdown."""
9798
if self.__future is not None:
9899
self.__future.cancel()

launch/launch/actions/register_event_handler.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from ..event_handler import BaseEventHandler
2424
from ..launch_context import LaunchContext
2525
from ..launch_description_entity import LaunchDescriptionEntity
26+
from ..utilities import normalize_to_list_of_entities
2627

2728

2829
class RegisterEventHandler(Action):
@@ -57,6 +58,8 @@ def describe_conditional_sub_entities(self) -> List[Tuple[
5758
Iterable[LaunchDescriptionEntity], # list of conditional sub-entities
5859
]]:
5960
event_handler_description = self.__event_handler.describe()
61+
6062
return [
61-
(event_handler_description[0], event_handler_description[1])
63+
(event_handler_description[0],
64+
normalize_to_list_of_entities(event_handler_description[1]))
6265
] if event_handler_description[1] else []

0 commit comments

Comments
 (0)