diff --git a/docs/conf.py b/docs/conf.py index a5c5eac..0b00a1f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- import sys import os @@ -46,15 +45,15 @@ intersphinx_mapping = { "commandsv1": ( - "https://robotpy.readthedocs.io/projects/commands-v1/en/%s/" % rtd_version, + f"https://robotpy.readthedocs.io/projects/commands-v1/en/{rtd_version}/", None, ), "networktables": ( - "https://robotpy.readthedocs.io/projects/pynetworktables/en/%s/" % rtd_version, + f"https://robotpy.readthedocs.io/projects/pynetworktables/en/{rtd_version}/", None, ), "wpilib": ( - "https://robotpy.readthedocs.io/projects/wpilib/en/%s/" % rtd_version, + f"https://robotpy.readthedocs.io/projects/wpilib/en/{rtd_version}/", None, ), } diff --git a/magicbot/inject.py b/magicbot/inject.py index 1a4198a..9a10500 100644 --- a/magicbot/inject.py +++ b/magicbot/inject.py @@ -1,5 +1,5 @@ import logging -from typing import Any, Dict, Optional +from typing import Any, Optional logger = logging.getLogger(__name__) @@ -9,8 +9,8 @@ class MagicInjectError(ValueError): def get_injection_requests( - type_hints: Dict[str, type], cname: str, component: Optional[Any] = None -) -> Dict[str, type]: + type_hints: dict[str, type], cname: str, component: Optional[Any] = None +) -> dict[str, type]: """ Given a dict of type hints, filter it to the requested injection types. @@ -52,8 +52,8 @@ def get_injection_requests( def find_injections( - requests: Dict[str, type], injectables: Dict[str, Any], cname: str -) -> Dict[str, Any]: + requests: dict[str, type], injectables: dict[str, Any], cname: str +) -> dict[str, Any]: """ Get a dict of the variables to inject into a given component. @@ -73,15 +73,13 @@ def find_injections( # Raise error if injectable syntax used but no injectable was found. if injectable is None: raise MagicInjectError( - "Component %s has variable %s (type %s), which is absent from robot" - % (cname, n, inject_type) + f"Component {cname} has variable {n} (type {inject_type}), which is absent from robot" ) # Raise error if injectable declared with type different than the initial type if not isinstance(injectable, inject_type): raise MagicInjectError( - "Component %s variable %s does not match type in robot! (Got %s, expected %s)" - % (cname, n, type(injectable), inject_type) + f"Component {cname} variable {n} does not match type in robot! (Got {type(injectable)}, expected {inject_type})" ) to_inject[n] = injectable diff --git a/magicbot/magic_reset.py b/magicbot/magic_reset.py index cedb39a..f6a4ada 100644 --- a/magicbot/magic_reset.py +++ b/magicbot/magic_reset.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Generic, TypeVar +from typing import Any, Generic, TypeVar V = TypeVar("V") @@ -36,7 +36,7 @@ def __init__(self, default: V) -> None: self.default = default -def collect_resets(cls: type) -> Dict[str, Any]: +def collect_resets(cls: type) -> dict[str, Any]: """ Get all the ``will_reset_to`` variables and their values from a class. diff --git a/magicbot/magic_tunable.py b/magicbot/magic_tunable.py index 091e2d8..557c82f 100644 --- a/magicbot/magic_tunable.py +++ b/magicbot/magic_tunable.py @@ -3,7 +3,8 @@ import inspect import typing import warnings -from typing import Callable, Generic, Optional, Sequence, TypeVar, Union, overload +from typing import Callable, Generic, Optional, TypeVar, Union, overload +from collections.abc import Sequence import ntcore from ntcore import NetworkTableInstance @@ -185,9 +186,9 @@ def setup_tunables(component, cname: str, prefix: Optional[str] = "components") cls = component.__class__ if prefix is None: - prefix = "/%s" % cname + prefix = f"/{cname}" else: - prefix = "/%s/%s" % (prefix, cname) + prefix = f"/{prefix}/{cname}" NetworkTables = NetworkTableInstance.getDefault() @@ -202,9 +203,9 @@ def setup_tunables(component, cname: str, prefix: Optional[str] = "components") continue if prop._ntsubtable: - key = "%s/%s/%s" % (prefix, prop._ntsubtable, n) + key = f"{prefix}/{prop._ntsubtable}/{n}" else: - key = "%s/%s" % (prefix, n) + key = f"{prefix}/{n}" topic = prop._topic_type(NetworkTables.getTopic(key)) ntvalue = topic.getEntry(prop._ntdefault) @@ -344,9 +345,9 @@ def collect_feedbacks(component, cname: str, prefix: Optional[str] = "components .. note:: This isn't useful for normal use. """ if prefix is None: - prefix = "/%s" % cname + prefix = f"/{cname}" else: - prefix = "/%s/%s" % (prefix, cname) + prefix = f"/{prefix}/{cname}" nt = NetworkTableInstance.getDefault().getTable(prefix) feedbacks = [] diff --git a/magicbot/magicrobot.py b/magicbot/magicrobot.py index a109f24..8842c1b 100644 --- a/magicbot/magicrobot.py +++ b/magicbot/magicrobot.py @@ -5,7 +5,7 @@ import types import typing -from typing import Any, Callable, Dict, List, Tuple +from typing import Any, Callable import hal import wpilib @@ -72,9 +72,9 @@ def __init__(self) -> None: self.__last_error_report = -10 - self._components: List[Tuple[str, Any]] = [] - self._feedbacks: List[Tuple[Callable[[], Any], Callable[[Any], Any]]] = [] - self._reset_components: List[Tuple[Dict[str, Any], Any]] = [] + self._components: list[tuple[str, Any]] = [] + self._feedbacks: list[tuple[Callable[[], Any], Callable[[Any], Any]]] = [] + self._reset_components: list[tuple[dict[str, Any], Any]] = [] self.__done = False @@ -125,7 +125,7 @@ def robotInit(self) -> None: self.watchdog = SimpleWatchdog(self.control_loop_wait_time) - self.__periodics: List[Tuple[Callable[[], None], str]] = [ + self.__periodics: list[tuple[Callable[[], None], str]] = [ (self.robotPeriodic, "robotPeriodic()"), ] @@ -397,7 +397,7 @@ def autonomous(self) -> None: except: self.onException(forceReport=True) - auto_functions: Tuple[Callable[[], None], ...] = (self._enabled_periodic,) + auto_functions: tuple[Callable[[], None], ...] = (self._enabled_periodic,) if self.use_teleop_in_autonomous: auto_functions = (self.teleopPeriodic,) + auto_functions @@ -609,8 +609,7 @@ def _create_components(self) -> None: # If the type is not actually a type, give a meaningful error if not isinstance(ctyp, type): raise TypeError( - "%s has a non-type annotation on %s (%r); lone non-injection variable annotations are disallowed, did you want to assign a static variable?" - % (cls.__name__, m, ctyp) + f"{cls.__name__} has a non-type annotation on {m} ({ctyp!r}); lone non-injection variable annotations are disallowed, did you want to assign a static variable?" ) component = self._create_component(m, ctyp, injectables) @@ -650,7 +649,7 @@ def _create_components(self) -> None: self._components = components - def _collect_injectables(self) -> Dict[str, Any]: + def _collect_injectables(self) -> dict[str, Any]: injectables = {} cls = type(self) @@ -673,7 +672,7 @@ def _collect_injectables(self) -> Dict[str, Any]: return injectables - def _create_component(self, name: str, ctyp: type, injectables: Dict[str, Any]): + def _create_component(self, name: str, ctyp: type, injectables: dict[str, Any]): type_hints = typing.get_type_hints(ctyp.__init__) NoneType = type(None) init_return_type = type_hints.pop("return", NoneType) @@ -690,8 +689,7 @@ def _create_component(self, name: str, ctyp: type, injectables: Dict[str, Any]): # Ensure that mandatory methods are there if not callable(getattr(component, "execute", None)): raise ValueError( - "Component %s (%r) must have a method named 'execute'" - % (name, component) + f"Component {name} ({component!r}) must have a method named 'execute'" ) # Automatically inject a logger object @@ -701,7 +699,7 @@ def _create_component(self, name: str, ctyp: type, injectables: Dict[str, Any]): return component - def _setup_vars(self, cname: str, component, injectables: Dict[str, Any]) -> None: + def _setup_vars(self, cname: str, component, injectables: dict[str, Any]) -> None: self.logger.debug("Injecting magic variables into %s", cname) type_hints = typing.get_type_hints(type(component)) diff --git a/magicbot/state_machine.py b/magicbot/state_machine.py index 76c3a44..3d462c6 100644 --- a/magicbot/state_machine.py +++ b/magicbot/state_machine.py @@ -1,17 +1,15 @@ -import functools import inspect import logging from typing import ( Any, Callable, ClassVar, - Dict, NoReturn, Optional, - Sequence, Union, overload, ) +from collections.abc import Sequence import wpilib @@ -85,7 +83,7 @@ def __init__( if invalid_args: raise ValueError( - "Invalid parameter names in %s: %s" % (name, ",".join(invalid_args)) + "Invalid parameter names in {}: {}".format(name, ",".join(invalid_args)) ) self.name = name @@ -136,7 +134,7 @@ def __set_name__(self, owner: type, name: str) -> None: class _StateData: def __init__(self, wrapper: _State) -> None: self.name = wrapper.name - self.duration_attr = "%s_duration" % self.name + self.duration_attr = f"{self.name}_duration" self.expires: float = 0xFFFFFFFF self.ran = False self.run = wrapper.run @@ -261,7 +259,7 @@ def default_state(f: StateMethod) -> _State: return _State(f, first=False, must_finish=True, is_default=True) -def _get_class_members(cls: type) -> Dict[str, Any]: +def _get_class_members(cls: type) -> dict[str, Any]: """Get the members of the given class in definition order, bases first.""" d = {} for cls in reversed(cls.__mro__): diff --git a/robotpy_ext/autonomous/selector.py b/robotpy_ext/autonomous/selector.py index 2f34fa6..aaa8ed7 100644 --- a/robotpy_ext/autonomous/selector.py +++ b/robotpy_ext/autonomous/selector.py @@ -3,7 +3,8 @@ import logging import os from glob import glob -from typing import Callable, Sequence, Union +from typing import Callable, Union +from collections.abc import Sequence import hal import wpilib @@ -151,7 +152,7 @@ def __init__(self, autonomous_pkgname, *args, **kwargs): if mode_name in self.modes: if not wpilib.DriverStation.isFMSAttached(): raise RuntimeError( - "Duplicate name %s in %s" % (mode_name, module_filename) + f"Duplicate name {mode_name} in {module_filename}" ) logger.error( @@ -195,8 +196,9 @@ def __init__(self, autonomous_pkgname, *args, **kwargs): elif len(default_modes) != 1: if not wpilib.DriverStation.isFMSAttached(): raise RuntimeError( - "More than one autonomous mode was specified as default! (modes: %s)" - % (", ".join(default_modes)) + "More than one autonomous mode was specified as default! (modes: {})".format( + ", ".join(default_modes) + ) ) # must PutData after setting up objects diff --git a/robotpy_ext/autonomous/stateful_autonomous.py b/robotpy_ext/autonomous/stateful_autonomous.py index f944201..aabddec 100644 --- a/robotpy_ext/autonomous/stateful_autonomous.py +++ b/robotpy_ext/autonomous/stateful_autonomous.py @@ -39,7 +39,7 @@ def __init__(self, f: Callable, first: bool): ) if arg.kind is arg.KEYWORD_ONLY: raise ValueError( - "Cannot use keyword-only parameters for function %s" % name + f"Cannot use keyword-only parameters for function {name}" ) if arg.name in allowed_args: args.append(arg.name) @@ -48,7 +48,7 @@ def __init__(self, f: Callable, first: bool): if invalid_args: raise ValueError( - "Invalid parameter names in %s: %s" % (name, ",".join(invalid_args)) + "Invalid parameter names in {}: {}".format(name, ",".join(invalid_args)) ) functools.update_wrapper(self, f) @@ -271,7 +271,7 @@ def register_sd_var(self, name, default, add_prefix=True, vmin=-1, vmax=1): # communicate the min/max value for numbers to the dashboard if is_number: - name = "%s|%0.3f|%0.3f" % (name, vmin, vmax) + name = f"{name}|{vmin:0.3f}|{vmax:0.3f}" self.__tunables.append(name) self.__table.putStringArray(self.MODE_NAME + "_tunables", self.__tunables) @@ -279,14 +279,14 @@ def register_sd_var(self, name, default, add_prefix=True, vmin=-1, vmax=1): def __register_sd_var_internal(self, name, default, add_prefix, readback): if " " in name: raise ValueError( - "ERROR: Cannot use spaces in a tunable variable name (%s)" % name + f"ERROR: Cannot use spaces in a tunable variable name ({name})" ) is_number = False sd_name = name if add_prefix: - sd_name = "%s\\%s" % (self.MODE_NAME, name) + sd_name = f"{self.MODE_NAME}\\{name}" if isinstance(default, bool): self.__table.putBoolean(sd_name, default) diff --git a/robotpy_ext/common_drivers/distance_sensors_sim.py b/robotpy_ext/common_drivers/distance_sensors_sim.py index 1102736..99f4522 100644 --- a/robotpy_ext/common_drivers/distance_sensors_sim.py +++ b/robotpy_ext/common_drivers/distance_sensors_sim.py @@ -1,5 +1,4 @@ import math -import wpilib from wpilib.simulation import AnalogInputSim diff --git a/robotpy_ext/common_drivers/driver_base.py b/robotpy_ext/common_drivers/driver_base.py index 4769d68..7a0b296 100644 --- a/robotpy_ext/common_drivers/driver_base.py +++ b/robotpy_ext/common_drivers/driver_base.py @@ -1,4 +1,4 @@ -class DriverBase(object): +class DriverBase: """ This should be the base class for all drivers in the cdl, currently all it does is spit out a warning message if the driver has not been verified. @@ -16,7 +16,5 @@ def __init__(self): """ if not self.verified: print( - "Warning, device driver {} has not been verified yet, please use with caution!".format( - self.__class__.__name__ - ) + f"Warning, device driver {self.__class__.__name__} has not been verified yet, please use with caution!" ) diff --git a/robotpy_ext/common_drivers/units.py b/robotpy_ext/common_drivers/units.py index 97138dc..34f6c16 100644 --- a/robotpy_ext/common_drivers/units.py +++ b/robotpy_ext/common_drivers/units.py @@ -1,4 +1,4 @@ -class Unit(object): +class Unit: """The class for all of the units here""" def __init__(self, base_unit, base_to_unit, unit_to_base): diff --git a/robotpy_ext/misc/orderedclass.py b/robotpy_ext/misc/orderedclass.py index a2054fb..808e0f8 100644 --- a/robotpy_ext/misc/orderedclass.py +++ b/robotpy_ext/misc/orderedclass.py @@ -1,6 +1,6 @@ """ - Taken from the python documentation, distributed under - that same license +Taken from the python documentation, distributed under +that same license """ import collections diff --git a/robotpy_ext/misc/simple_watchdog.py b/robotpy_ext/misc/simple_watchdog.py index 73b4ecf..1864626 100644 --- a/robotpy_ext/misc/simple_watchdog.py +++ b/robotpy_ext/misc/simple_watchdog.py @@ -1,6 +1,5 @@ import wpilib import logging -from typing import List, Tuple logger = logging.getLogger("simple_watchdog") @@ -38,7 +37,7 @@ def __init__(self, timeout: float): self._expirationTime = 0 # us self._lastTimeoutPrintTime = 0 # us self._lastEpochsPrintTime = 0 # us - self._epochs: List[Tuple[str, int]] = [] + self._epochs: list[tuple[str, int]] = [] def getTime(self) -> float: """Returns the time in seconds since the watchdog was last fed.""" diff --git a/tests/test_magicbot_tunable.py b/tests/test_magicbot_tunable.py index a90a13b..6ee65cf 100644 --- a/tests/test_magicbot_tunable.py +++ b/tests/test_magicbot_tunable.py @@ -71,10 +71,9 @@ class Component: class_var_typing_list: ClassVar[tunable[List[int]]] = tunable([]) inst_typing_list: List[int] = tunable([]) - # TODO(davo): re-enable after py3.8 is dropped - # generic_list = tunable[list[int]]([]) - # class_var_list: ClassVar[tunable[list[int]]] = tunable([]) - # inst_list: list[int] = tunable([]) + generic_list = tunable[list[int]]([]) + class_var_list: ClassVar[tunable[list[int]]] = tunable([]) + inst_list: list[int] = tunable([]) component = Component() setup_tunables(component, "test_type_hinted_sequences") @@ -88,9 +87,9 @@ class Component: "generic_typing_list", "class_var_typing_list", "inst_typing_list", - # "generic_list", - # "class_var_list", - # "inst_list", + "generic_list", + "class_var_list", + "inst_list", ]: assert nt.getTopic(name).getTypeString() == "int[]" entry = nt.getEntry(name)