Skip to content

Commit ab856cf

Browse files
haudren-wovenmergify[bot]
authored andcommitted
Improve type checking (#679)
(cherry picked from commit 06dc66b) # Conflicts: # launch/launch/logging/__init__.py # launch/launch/substitutions/launch_log_dir.py # launch/launch/substitutions/python_expression.py # launch/launch/substitutions/this_launch_file.py # launch/launch/substitutions/this_launch_file_dir.py
1 parent 1fafc2c commit ab856cf

File tree

62 files changed

+552
-240
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

+552
-240
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: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
import launch.logging
3636

37-
from osrf_pycommon.process_utils import async_execute_process
37+
from osrf_pycommon.process_utils import async_execute_process # type: ignore
3838
from osrf_pycommon.process_utils import AsyncSubprocessProtocol
3939

4040
from .emit_event import EmitEvent
@@ -63,7 +63,7 @@
6363
from ..launch_context import LaunchContext
6464
from ..launch_description import LaunchDescription
6565
from ..launch_description_entity import LaunchDescriptionEntity
66-
from ..some_actions_type import SomeActionsType
66+
from ..some_entities_type import SomeEntitiesType
6767
from ..some_substitutions_type import SomeSubstitutionsType
6868
from ..substitution import Substitution # noqa: F401
6969
from ..substitutions import LaunchConfiguration
@@ -97,8 +97,8 @@ def __init__(
9797
cached_output: bool = False,
9898
log_cmd: bool = False,
9999
on_exit: Optional[Union[
100-
SomeActionsType,
101-
Callable[[ProcessExited, LaunchContext], Optional[SomeActionsType]]
100+
SomeEntitiesType,
101+
Callable[[ProcessExited, LaunchContext], Optional[SomeEntitiesType]]
102102
]] = None,
103103
respawn: Union[bool, SomeSubstitutionsType] = False,
104104
respawn_delay: Optional[float] = None,
@@ -193,9 +193,16 @@ def __init__(
193193
self.__sigterm_timeout = normalize_to_list_of_substitutions(sigterm_timeout)
194194
self.__sigkill_timeout = normalize_to_list_of_substitutions(sigkill_timeout)
195195
self.__emulate_tty = emulate_tty
196-
self.__output = os.environ.get('OVERRIDE_LAUNCH_PROCESS_OUTPUT', output)
197-
if not isinstance(self.__output, dict):
198-
self.__output = normalize_to_list_of_substitutions(self.__output)
196+
# Note: we need to use a temporary here so that we don't assign values with different types
197+
# to the same variable
198+
tmp_output: SomeSubstitutionsType = os.environ.get(
199+
'OVERRIDE_LAUNCH_PROCESS_OUTPUT', output
200+
)
201+
self.__output: Union[dict, List[Substitution]]
202+
if not isinstance(tmp_output, dict):
203+
self.__output = normalize_to_list_of_substitutions(tmp_output)
204+
else:
205+
self.__output = tmp_output
199206
self.__output_format = output_format
200207

201208
self.__log_cmd = log_cmd
@@ -336,7 +343,7 @@ def __on_signal_process_event(
336343
def __on_process_stdin(
337344
self,
338345
event: ProcessIO
339-
) -> Optional[SomeActionsType]:
346+
) -> Optional[SomeEntitiesType]:
340347
self.__logger.warning(
341348
"in ExecuteProcess('{}').__on_process_stdin_event()".format(id(self)),
342349
)
@@ -345,7 +352,7 @@ def __on_process_stdin(
345352

346353
def __on_process_output(
347354
self, event: ProcessIO, buffer: io.TextIOBase, logger: logging.Logger
348-
) -> Optional[SomeActionsType]:
355+
) -> None:
349356
to_write = event.text.decode(errors='replace')
350357
if buffer.closed:
351358
# buffer was probably closed by __flush_buffers on shutdown. Output without
@@ -396,7 +403,7 @@ def __flush_buffers(self, event, context):
396403

397404
def __on_process_output_cached(
398405
self, event: ProcessIO, buffer, logger
399-
) -> Optional[SomeActionsType]:
406+
) -> None:
400407
to_write = event.text.decode(errors='replace')
401408
last_cursor = buffer.tell()
402409
buffer.seek(0, os.SEEK_END) # go to end of buffer
@@ -423,7 +430,7 @@ def __flush_cached_buffers(self, event, context):
423430
self.__output_format.format(line=line, this=self)
424431
)
425432

426-
def __on_shutdown(self, event: Event, context: LaunchContext) -> Optional[SomeActionsType]:
433+
def __on_shutdown(self, event: Event, context: LaunchContext) -> Optional[SomeEntitiesType]:
427434
due_to_sigint = cast(Shutdown, event).due_to_sigint
428435
return self._shutdown_process(
429436
context,
@@ -578,9 +585,14 @@ async def __execute_process(self, context: LaunchContext) -> None:
578585
self.__logger.error("process has died [pid {}, exit code {}, cmd '{}'].".format(
579586
pid, returncode, ' '.join(filter(lambda part: part.strip(), cmd))
580587
))
581-
await context.emit_event(ProcessExited(returncode=returncode, **process_event_args))
588+
await context.emit_event(
589+
ProcessExited(returncode=returncode, **process_event_args)
590+
)
582591
# respawn the process if necessary
583-
if not context.is_shutdown and not self.__shutdown_future.done() and self.__respawn:
592+
if not context.is_shutdown\
593+
and self.__shutdown_future is not None\
594+
and not self.__shutdown_future.done()\
595+
and self.__respawn:
584596
if self.__respawn_delay is not None and self.__respawn_delay > 0.0:
585597
# wait for a timeout(`self.__respawn_delay`) to respawn the process
586598
# and handle shutdown event with future(`self.__shutdown_future`)
@@ -608,7 +620,7 @@ def prepare(self, context: LaunchContext):
608620
# pid is added to the dictionary in the connection_made() method of the protocol.
609621
}
610622

611-
self.__respawn = perform_typed_substitution(context, self.__respawn, bool)
623+
self.__respawn = cast(bool, perform_typed_substitution(context, self.__respawn, bool))
612624

613625
def execute(self, context: LaunchContext) -> Optional[List[LaunchDescriptionEntity]]:
614626
"""
@@ -663,7 +675,9 @@ def execute(self, context: LaunchContext) -> Optional[List[LaunchDescriptionEnti
663675
),
664676
OnProcessExit(
665677
target_action=self,
666-
on_exit=self.__on_exit,
678+
# TODO: This is also a little strange, OnProcessExit shouldn't ever be able to
679+
# take a None for the callable, but this seems to be the default case?
680+
on_exit=self.__on_exit, # type: ignore
667681
),
668682
OnProcessExit(
669683
target_action=self,
@@ -678,9 +692,13 @@ def execute(self, context: LaunchContext) -> Optional[List[LaunchDescriptionEnti
678692
self.__shutdown_future = create_future(context.asyncio_loop)
679693
self.__logger = launch.logging.get_logger(name)
680694
if not isinstance(self.__output, dict):
681-
self.__output = perform_substitutions(context, self.__output)
682-
self.__stdout_logger, self.__stderr_logger = \
683-
launch.logging.get_output_loggers(name, self.__output)
695+
self.__stdout_logger, self.__stderr_logger = \
696+
launch.logging.get_output_loggers(
697+
name, perform_substitutions(context, self.__output)
698+
)
699+
else:
700+
self.__stdout_logger, self.__stderr_logger = \
701+
launch.logging.get_output_loggers(name, self.__output)
684702
context.asyncio_loop.create_task(self.__execute_process(context))
685703
except Exception:
686704
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
@@ -29,6 +29,8 @@
2929
from ..frontend import expose_action
3030
from ..frontend import Parser
3131
from ..some_substitutions_type import SomeSubstitutionsType
32+
33+
from ..substitution import Substitution
3234
from ..substitutions import TextSubstitution
3335

3436
_global_process_counter_lock = threading.Lock()
@@ -254,7 +256,7 @@ def _parse_cmdline(
254256
:returns: a list of command line arguments.
255257
"""
256258
result_args = []
257-
arg = []
259+
arg: List[Substitution] = []
258260

259261
def _append_arg():
260262
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)