Skip to content

Commit f3c522c

Browse files
committed
Add DeferredCommand
1 parent 3463a23 commit f3c522c

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-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

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

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)