Skip to content

Commit

Permalink
Add Proxy/DeferredCommand docs and deprecate ProxyCommand supplier co…
Browse files Browse the repository at this point in the history
…nstructor (#61)

* add deprecation and docs

* run black

* Update commands2/proxycommand.py

* increase stacklevel

---------

Co-authored-by: Vasista Vovveti <[email protected]>
  • Loading branch information
DeltaDizzy and TheTripleV authored Jun 23, 2024
1 parent 4a953ab commit c573294
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 9 deletions.
7 changes: 4 additions & 3 deletions commands2/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,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=3,
)

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

0 comments on commit c573294

Please sign in to comment.