autodiscover opmodes + opmode example#260
Conversation
98427ce to
cf18107
Compare
|
|
||
|
|
||
| @overload | ||
| def autonomous(cls: _OpModeType, /) -> _OpModeType: ... |
There was a problem hiding this comment.
need docstrings for these decorators, should also document them in OpModeRobot
| return False | ||
|
|
||
|
|
||
| def _iter_scan_files(root: Path): |
There was a problem hiding this comment.
This doesnt really need to be a separate function -- but also, should we only scan the current directory, or maybe a specified directory?
If the user creates a .venv next to robot.py, then it will scan that entire thing and load any opmodes found. That seems bad.
There was a problem hiding this comment.
If the user creates a .venv next to robot.py
This is the default of tools like uv, so we definitely need to make sure this doesn't walk virtualenvs ever.
|
|
||
| def __init__(self): | ||
| super().__init__() | ||
| _discover_decorated_opmodes(self) |
There was a problem hiding this comment.
All of the associated code with this seems a bit messy, need to reorg it a bit
| """Provides one Gamepad per standard driver station port.""" | ||
|
|
||
| def __init__(self): | ||
| self._gamepads = tuple(Gamepad(port) for port in range(6)) |
There was a problem hiding this comment.
Suggest using the constant JOYSTICK_PORTS instead of a magic number
Also, why a tuple instead of an array? I don't know that it matters but I was curious if there are benefits I don't know about.
There was a problem hiding this comment.
The AI decided it was the right fit :)
Generally, a tuple is appropriate for something that shouldn't be changed, and a list is more appropriate for things that are mutable.
| is rejected with ``TypeError``. | ||
| """ | ||
|
|
||
| __wpilib_user_controls_type__: type | None = None |
There was a problem hiding this comment.
nit: __dunder__ names are considered reserved for use by the Python interpreter/standard library, and we shouldn't invent our own. https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers
| user_controls: Any | None, | ||
| ) -> dict[str, Any]: | ||
| kwargs: dict[str, Any] = {} | ||
| initializer = opmodeCls.__dict__.get("__init__") |
There was a problem hiding this comment.
Is there a reason we explicitly look for __init__? Calling inspect.signature() on a type is perfectly valid, and would mean we don't have to remove the self parameter.
|
|
||
| def addOpMode( | ||
| self, | ||
| opmodeCls: type, |
There was a problem hiding this comment.
If we don't grab __init__ out of the object dict, we can pass through a Callable instead. That should make the type checker happier below too.
| opmodeCls: Callable[..., OpMode], |
| """ | ||
|
|
||
| constructor_kwargs = _resolve_opmode_constructor_kwargs( | ||
| opmodeCls, self, self._user_controls |
There was a problem hiding this comment.
Just thinking about how we could use this in magicbot, I think we'd want to be able to dependency inject components too, rather than passing around the entire robot. I suppose we could work with it either way though.
There was a problem hiding this comment.
So... we just add a general purpose injection mechanism instead of something specific to user controls?
This was to support the Java thing:
@UserControlsInstance(DefaultUserControls.class)
public class Robot extends OpModeRobot {
...
I'm not sure that a general purpose injection mechanism is a great idea. This is a bit weird too though.
7226c87 to
d4937dd
Compare
This is purely vibe-coded, but this is at least an initial look at what this will look like