[copybara] Resync robotpy (#8585)

Project import generated by Copybara.
    
GitOrigin-RevId: fd000778e9b78c72cc7ca7b2ebe476129b78c6e0
This commit is contained in:
PJ Reiniger
2026-02-08 10:36:35 -05:00
committed by GitHub
parent 4aa21e947d
commit 227c89ab23
53 changed files with 1049 additions and 170 deletions

View File

@@ -0,0 +1,24 @@
Copyright (c) 2009-2021 FIRST and other WPILib contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of FIRST, WPILib, nor the names of other WPILib
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY FIRST AND OTHER WPILIB CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,7 @@
robotpy-commands-v2
===================
Python wrappers around a modified version of the new WPILib commands library.
* Documentation @ https://robotpy.readthedocs.io/projects/commands-v2
* Examples @ https://github.com/robotpy/examples/tree/main/commands-v2

View File

@@ -9,7 +9,7 @@ from .trigger import Trigger
class CommandGenericHID:
"""
A version of :class:`wpilib.interfaces.GenericHID` with :class:`.Trigger` factories for command-based.
A version of :class:`wpilib.GenericHID` with :class:`.Trigger` factories for command-based.
"""
def __init__(self, port: int):

View File

@@ -6,7 +6,7 @@ from .trigger import Trigger
class JoystickButton(Trigger):
"""
A Button that gets its state from a :class:`wpilib.interfaces.GenericHID`.
A Button that gets its state from a :class:`wpilib.GenericHID`.
"""
def __init__(self, joystick: GenericHID, buttonNumber: int):
@@ -14,6 +14,6 @@ class JoystickButton(Trigger):
Creates a joystick button for triggering commands.
:param joystick: The GenericHID object that has the button (e.g. Joystick, KinectStick, etc)
:param buttonNumber: The button number (see :func:`wpilib.interfaces.GenericHID.getRawButton`
:param buttonNumber: The button number (see :func:`wpilib.GenericHID.getRawButton`
"""
super().__init__(lambda: joystick.getRawButton(buttonNumber))

View File

@@ -112,8 +112,7 @@ class NetworkButton(Trigger):
if inst is not None and table is not None and field is not None:
return init_inst_table_field(inst, table, field)
raise TypeError(
f"""
raise TypeError(f"""
TypeError: NetworkButton(): incompatible function arguments. The following argument types are supported:
1. (self: NetworkButton, topic: BooleanTopic)
2. (self: NetworkButton, sub: BooleanSubscriber)
@@ -122,5 +121,4 @@ TypeError: NetworkButton(): incompatible function arguments. The following argum
5. (self: NetworkButton, inst: NetworkTableInstance, table: str, field: str)
Invoked with: {format_args_kwargs(self, *args, **kwargs)}
"""
)
""")

View File

@@ -6,7 +6,7 @@ from .trigger import Trigger
class POVButton(Trigger):
"""
A Button that gets its state from a POV on a :class:`wpilib.interfaces.GenericHID`.
A Button that gets its state from a POV on a :class:`wpilib.GenericHID`.
"""
def __init__(self, joystick: GenericHID, angle: int, povNumber: int = 0):
@@ -15,6 +15,6 @@ class POVButton(Trigger):
:param joystick: The GenericHID object that has the POV
:param angle: The desired angle in degrees (e.g. 90, 270)
:param povNumber: The POV number (see :func:`wpilib.interfaces.GenericHID.getPOV`)
:param povNumber: The POV number (see :func:`wpilib.GenericHID.getPOV`)
"""
super().__init__(lambda: joystick.getPOV(povNumber) == angle)

View File

@@ -73,16 +73,29 @@ class Trigger:
if loop is not None and condition is not None:
return init_loop_condition(loop, condition)
raise TypeError(
f"""
raise TypeError(f"""
TypeError: Trigger(): incompatible function arguments. The following argument types are supported:
1. (self: Trigger)
2. (self: Trigger, condition: () -> bool)
3. (self: Trigger, loop: EventLoop, condition: () -> bool)
Invoked with: {format_args_kwargs(self, *args, **kwargs)}
"""
)
""")
def _add_binding(self, body: Callable[[bool, bool], None]) -> None:
"""
Adds a binding to the EventLoop.
:param body: The body of the binding to add.
"""
state = SimpleNamespace(previous=self._condition())
@self._loop.bind
def _():
current = self._condition()
body(state.previous, current)
state.previous = current
def onTrue(self, command: Command) -> Self:
"""
@@ -92,14 +105,10 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if not state.pressed_last and pressed:
@self._add_binding
def _(previous, current):
if not previous and current:
command.schedule()
state.pressed_last = pressed
return self
@@ -111,14 +120,10 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if state.pressed_last and not pressed:
@self._add_binding
def _(previous, current):
if previous and not current:
command.schedule()
state.pressed_last = pressed
return self
@@ -130,17 +135,11 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if state.pressed_last != pressed:
@self._add_binding
def _(previous, current):
if previous != current:
command.schedule()
state.pressed_last = pressed
return self
def whileTrue(self, command: Command) -> Self:
@@ -155,16 +154,12 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if not state.pressed_last and pressed:
@self._add_binding
def _(previous, current):
if not previous and current:
command.schedule()
elif state.pressed_last and not pressed:
elif previous and not current:
command.cancel()
state.pressed_last = pressed
return self
@@ -180,16 +175,12 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if state.pressed_last and not pressed:
@self._add_binding
def _(previous, current):
if previous and not current:
command.schedule()
elif not state.pressed_last and pressed:
elif not previous and current:
command.cancel()
state.pressed_last = pressed
return self
@@ -201,17 +192,13 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if not state.pressed_last and pressed:
@self._add_binding
def _(previous, current):
if not previous and current:
if command.isScheduled():
command.cancel()
else:
command.schedule()
state.pressed_last = pressed
return self
@@ -223,17 +210,13 @@ Invoked with: {format_args_kwargs(self, *args, **kwargs)}
:returns: this trigger, so calls can be chained
"""
state = SimpleNamespace(pressed_last=self._condition())
@self._loop.bind
def _():
pressed = self._condition()
if state.pressed_last and not pressed:
@self._add_binding
def _(previous, current):
if previous and not current:
if command.isScheduled():
command.cancel()
else:
command.schedule()
state.pressed_last = pressed
return self

View File

@@ -9,13 +9,14 @@ from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Union
import hal
from typing_extensions import Self
from wpilib import (
RobotBase,
DriverStation,
EventLoop,
RobotBase,
TimedRobot,
Watchdog,
reportWarning,
)
from wpiutil import Sendable, SendableBuilder, SendableRegistry
from .command import Command, InterruptionBehavior

View File

@@ -79,15 +79,13 @@ class ProxyCommand(Command):
elif callable(args[0]):
return init_supplier(args[0])
raise TypeError(
f"""
raise TypeError(f"""
TypeError: ProxyCommand(): incompatible function arguments. The following argument types are supported:
1. (self: ProxyCommand, supplier: () -> Command)
2. (self: ProxyCommand, command: Command)
Invoked with: {format_args_kwargs(self, *args, **kwargs)}
"""
)
""")
def initialize(self):
self._command = self._supplier()

View File

@@ -1,4 +1,3 @@
from .sysidroutine import SysIdRoutine
__all__ = ["SysIdRoutine"]

View File

@@ -11,7 +11,6 @@ from wpimath.units import seconds, volts
from typing import Callable, Optional
volts_per_second = float

View File

@@ -63,15 +63,13 @@ class WaitUntilCommand(Command):
elif callable(args[0]):
return init_condition(args[0])
raise TypeError(
f"""
raise TypeError(f"""
TypeError: WaitUntilCommand(): incompatible function arguments. The following argument types are supported:
1. (self: WaitUntilCommand, condition: () -> bool)
2. (self: WaitUntilCommand, time: wpimath.units.seconds)
Invoked with: {format_args_kwargs(self, *args, **kwargs)}
"""
)
""")
def isFinished(self) -> bool:
return self._condition()

View File

@@ -0,0 +1,36 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "robotpy-commands-v2"
version = "0.0.0"
description = "WPILib command framework v2"
readme = "README.md"
requires-python = ">=3.10"
license = "BSD-3-Clause"
license-files = ["LICENSE"]
dependencies = [
"wpilib==0.0.0",
"typing_extensions>=4.1.0,<5",
]
[[project.authors]]
name = "RobotPy Development Team"
email = "robotpy@googlegroups.com"
[[project.maintainers]]
name = "RobotPy Development Team"
email = "robotpy@googlegroups.com"
[project.urls]
"Source code" = "https://github.com/robotpy/robotpy-commands-v2"
[tool.hatch.version]
source = "vcs"
[tool.hatch.build.targets.sdist]
packages = ["commands2"]
[tool.hatch.build.targets.wheel]
packages = ["commands2"]

View File

@@ -5,7 +5,6 @@ import inspect
import commands2
from wpilib.simulation import DriverStationSim, pauseTiming, resumeTiming, stepTiming
Y = TypeVar("Y")