Skip to content

Commit d957464

Browse files
committed
Add DeferredCommand
1 parent 3463a23 commit d957464

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

Diff for: commands2/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from .commandscheduler import CommandScheduler
77
from .conditionalcommand import ConditionalCommand
8+
from .deferredcommand import DeferredCommand
89
from .exceptions import IllegalCommandUse
910
from .functionalcommand import FunctionalCommand
1011
from .instantcommand import InstantCommand
@@ -38,6 +39,7 @@
3839
"Command",
3940
"CommandScheduler",
4041
"ConditionalCommand",
42+
"DeferredCommand",
4143
"FunctionalCommand",
4244
"IllegalCommandUse",
4345
"InstantCommand",

Diff for: commands2/deferredcommand.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from typing import Callable
2+
3+
from wpiutil import SendableBuilder
4+
5+
from .command import Command
6+
from .commandscheduler import CommandScheduler
7+
from .printcommand import PrintCommand
8+
from .subsystem import Subsystem
9+
10+
11+
class DeferredCommand(Command):
12+
"""
13+
Defers Command construction to runtime. Runs the command returned by the supplier when this
14+
command is initialized, and ends when it ends. Useful for performing runtime tasks before
15+
creating a new command. If this command is interrupted, it will cancel the command.
16+
17+
Note that the supplier *must* create a new Command each call. For selecting one of a
18+
preallocated set of commands, use :class:`commands2.SelectCommand`.
19+
"""
20+
21+
def __init__(self, supplier: Callable[[], Command], *requirements: Subsystem):
22+
"""
23+
:param supplier: The command supplier
24+
:param requirements: The command requirements.
25+
"""
26+
super().__init__()
27+
28+
assert callable(supplier)
29+
30+
self._null_command = PrintCommand(
31+
f"[DeferredCommand] Supplied command (from {supplier!r} was None!"
32+
)
33+
self._supplier = supplier
34+
self._command = self._null_command
35+
self.addRequirements(*requirements)
36+
37+
def initialize(self):
38+
cmd = self._supplier()
39+
if cmd is not None:
40+
self._command = cmd
41+
CommandScheduler.getInstance().registerComposedCommands([self._command])
42+
self._command.initialize()
43+
44+
def execute(self):
45+
self._command.execute()
46+
47+
def isFinished(self):
48+
return self._command.isFinished()
49+
50+
def end(self, interrupted):
51+
self._command.end(interrupted)
52+
self._command = self._null_command
53+
54+
def initSendable(self, builder: SendableBuilder):
55+
super().initSendable(builder)
56+
builder.addStringProperty(
57+
"deferred",
58+
lambda: "null"
59+
if self._command is self._null_command
60+
else self._command.getName(),
61+
lambda _: None,
62+
)

Diff for: tests/test_deferred_command.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import commands2
2+
3+
from util import * # type: ignore
4+
5+
6+
def test_deferred_functions(scheduler: commands2.CommandScheduler):
7+
inner_command = commands2.Command()
8+
command = commands2.DeferredCommand(lambda: inner_command)
9+
10+
start_spying_on(inner_command)
11+
start_spying_on(command)
12+
13+
command.initialize()
14+
verify(inner_command).initialize()
15+
16+
command.execute()
17+
verify(inner_command).execute()
18+
19+
assert not command.isFinished()
20+
verify(inner_command).isFinished()
21+
22+
inner_command.isFinished = lambda: True
23+
assert command.isFinished()
24+
verify(inner_command, times=times(2)).isFinished()
25+
26+
command.end(False)
27+
verify(inner_command).end(False)
28+
29+
30+
def test_deferred_supplier_only_called_during_init(
31+
scheduler: commands2.CommandScheduler,
32+
):
33+
supplier_called = 0
34+
35+
def supplier() -> commands2.Command:
36+
nonlocal supplier_called
37+
supplier_called += 1
38+
return commands2.Command()
39+
40+
command = commands2.DeferredCommand(supplier)
41+
assert supplier_called == 0
42+
43+
scheduler.schedule(command)
44+
assert supplier_called == 1
45+
scheduler.run()
46+
47+
scheduler.schedule(command)
48+
assert supplier_called == 1
49+
50+
51+
def test_deferred_requirements(scheduler: commands2.CommandScheduler):
52+
subsystem = commands2.Subsystem()
53+
command = commands2.DeferredCommand(lambda: commands2.Command(), subsystem)
54+
55+
assert subsystem in command.getRequirements()
56+
57+
58+
def test_deferred_null_command(scheduler: commands2.CommandScheduler):
59+
command = commands2.DeferredCommand(lambda: None) # type: ignore
60+
command.initialize()
61+
command.execute()
62+
command.isFinished()
63+
command.end(False)

0 commit comments

Comments
 (0)