Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Proxy/DeferredCommand docs and deprecate ProxyCommand supplier constructor #61

Merged
merged 4 commits into from
Jun 23, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions commands2/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,10 @@ def repeatedly(self) -> RepeatCommand:

def asProxy(self) -> ProxyCommand:
"""
Decorates this command to run "by proxy" by wrapping it in a ProxyCommand. This is
useful for "forking off" from command compositions when the user does not wish to extend the
command's requirements to the entire command composition.
Decorates this command to run "by proxy" by wrapping it in a ProxyCommand. Use this for
"forking off" from command compositions when the user does not wish to extend the command's
requirements to the entire command composition. ProxyCommand has unique implications and
semantics, see the `WPILib docs <https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands>`_ for a full explanation.

:returns: the decorated command
"""
Expand Down
12 changes: 9 additions & 3 deletions commands2/deferredcommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,22 @@

class DeferredCommand(Command):
"""
Defers Command construction to runtime. Runs the command returned by the supplier when this
command is initialized, and ends when it ends. Useful for performing runtime tasks before
creating a new command. If this command is interrupted, it will cancel the command.
Defers Command construction to runtime. Runs the command returned by a supplier when this command
is initialized, and ends when it ends. Useful for performing runtime tasks before creating a new
command. If this command is interrupted, it will cancel the command.

Note that the supplier *must* create a new Command each call. For selecting one of a
preallocated set of commands, use :class:`commands2.SelectCommand`.
"""

def __init__(self, supplier: Callable[[], Command], *requirements: Subsystem):
"""

Creates a new DeferredCommand that directly runs the supplied command when initialized, and
ends when it ends. Useful for lazily creating commands when the DeferredCommand is initialized,
such as if the supplied command depends on runtime state. The Supplier will be called
each time this command is initialized. The Supplier *must* create a new Command each call.

:param supplier: The command supplier
:param requirements: The command requirements.
"""
Expand Down
24 changes: 21 additions & 3 deletions commands2/proxycommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@

from .command import Command
from .util import format_args_kwargs
import warnings


class ProxyCommand(Command):
"""
Schedules the given command when this command is initialized, and ends when it ends. Useful for
forking off from CommandGroups. If this command is interrupted, it will cancel the command.
Schedules a given command when this command is initialized and ends when it ends, but does not
directly run it. Use this for including a command in a composition without adding its
requirements, but only if you know what you are doing. If you are unsure, see
`the WPILib docs <https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands>`_
for a complete explanation of proxy semantics. Do not proxy a command from a subsystem already
required by the composition, or else the composition will cancel itself when the proxy is reached.
If this command is interrupted, it will cancel the command.
"""

_supplier: Callable[[], Command]
Expand All @@ -21,9 +27,16 @@ class ProxyCommand(Command):
def __init__(self, supplier: Callable[[], Command]):
"""
Creates a new ProxyCommand that schedules the supplied command when initialized, and ends when
it is no longer scheduled. Useful for lazily creating commands at runtime.
it is no longer scheduled. Use this for lazily creating **proxied** commands at
runtime. Proxying should only be done to escape from composition requirement semantics, so if
only initialization time command construction is needed, use DeferredCommand instead.

:param supplier: the command supplier
This constructor's similarity to DeferredCommand is confusing and opens
potential footguns for users who do not fully understand the semantics and implications of
proxying, but who simply want runtime construction. Users who do know what they are doing
and need a supplier-constructed proxied command should instead proxy a DeferredCommand
using the ``asProxy`` decorator.
"""
...

Expand All @@ -43,6 +56,11 @@ def __init__(self, *args, **kwargs):
def init_supplier(supplier: Callable[[], Command]):
assert callable(supplier)
self._supplier = supplier
warnings.warn(
"The ProxyCommand supplier constructor has been deprecated",
DeprecationWarning,
stacklevel=2,
DeltaDizzy marked this conversation as resolved.
Show resolved Hide resolved
)

def init_command(command: Command):
self.setName(f"Proxy({command.getName()})")
Expand Down
Loading