From d4b66b8e335d1210b60ec726b4110cb36a776d43 Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Sat, 11 Jan 2020 16:41:28 -0500 Subject: [PATCH 1/2] replace asserts with ValueError or TypeError --- ev3dev2/__init__.py | 3 ++ ev3dev2/display.py | 15 ++++----- ev3dev2/led.py | 18 +++++------ ev3dev2/motor.py | 70 ++++++++++++++++++++++++------------------ ev3dev2/sensor/lego.py | 8 ++--- ev3dev2/sound.py | 4 +-- ev3dev2/unit.py | 24 ++++++++++----- 7 files changed, 82 insertions(+), 60 deletions(-) diff --git a/ev3dev2/__init__.py b/ev3dev2/__init__.py index 2d2895e..7dc13bf 100644 --- a/ev3dev2/__init__.py +++ b/ev3dev2/__init__.py @@ -142,6 +142,9 @@ def library_load_warning_message(library_name, dependent_class): class DeviceNotFound(Exception): pass +class ThreadNotRunning(Exception): + pass + # ----------------------------------------------------------------------------- # Define the base class from which all other ev3dev classes are defined. diff --git a/ev3dev2/display.py b/ev3dev2/display.py index 41d4ccf..4278bfb 100644 --- a/ev3dev2/display.py +++ b/ev3dev2/display.py @@ -396,7 +396,8 @@ def text_pixels(self, text, clear_screen=True, x=0, y=0, text_color='black', fon if font is not None: if isinstance(font, str): - assert font in fonts.available(), "%s is an invalid font" % font + if font not in fonts.available(): + raise ValueError("%s is an invalid font" % font) font = fonts.load(font) return self.draw.text((x, y), text, fill=text_color, font=font) else: @@ -424,13 +425,13 @@ def text_grid(self, text, clear_screen=True, x=0, y=0, text_color='black', font= """ - assert 0 <= x < Display.GRID_COLUMNS,\ - "grid columns must be between 0 and %d, %d was requested" %\ - ((Display.GRID_COLUMNS - 1, x)) + if not 0 <= x < Display.GRID_COLUMNS: + raise ValueError("grid columns must be between 0 and %d, %d was requested" %\ + (Display.GRID_COLUMNS - 1, x)) - assert 0 <= y < Display.GRID_ROWS,\ - "grid rows must be between 0 and %d, %d was requested" %\ - ((Display.GRID_ROWS - 1), y) + if not 0 <= y < Display.GRID_ROWS: + raise ValueError("grid rows must be between 0 and %d, %d was requested" %\ + (Display.GRID_ROWS - 1, y)) return self.text_pixels(text, clear_screen, x * Display.GRID_COLUMN_PIXELS, diff --git a/ev3dev2/led.py b/ev3dev2/led.py index 50a545b..c6926ba 100644 --- a/ev3dev2/led.py +++ b/ev3dev2/led.py @@ -311,14 +311,14 @@ def set_color(self, group, color, pct=1): color_tuple = color if isinstance(color, str): - assert color in self.led_colors, \ - "%s is an invalid LED color, valid choices are %s" % \ - (color, ', '.join(self.led_colors.keys())) + if color not in self.led_colors: + raise ValueError("%s is an invalid LED color, valid choices are %s" % \ + (color, ', '.join(self.led_colors.keys()))) color_tuple = self.led_colors[color] - assert group in self.led_groups, \ - "%s is an invalid LED group, valid choices are %s" % \ - (group, ', '.join(self.led_groups.keys())) + if group not in self.led_groups: + raise ValueError("%s is an invalid LED group, valid choices are %s" % \ + (group, ', '.join(self.led_groups.keys()))) for led, value in zip(self.led_groups[group], color_tuple): led.brightness_pct = value * pct @@ -337,9 +337,9 @@ def set(self, group, **kwargs): if not self.leds: return - assert group in self.led_groups, \ - "%s is an invalid LED group, valid choices are %s" % \ - (group, ', '.join(self.led_groups.keys())) + if group not in self.led_groups: + raise ValueError("%s is an invalid LED group, valid choices are %s" % \ + (group, ', '.join(self.led_groups.keys()))) for led in self.led_groups[group]: for k in kwargs: diff --git a/ev3dev2/motor.py b/ev3dev2/motor.py index 2e2b853..e4302c0 100644 --- a/ev3dev2/motor.py +++ b/ev3dev2/motor.py @@ -39,7 +39,7 @@ from logging import getLogger from os.path import abspath -from ev3dev2 import get_current_platform, Device, list_device_names +from ev3dev2 import get_current_platform, Device, list_device_names, ThreadNotRunning from ev3dev2.stopwatch import StopWatch log = getLogger(__name__) @@ -112,8 +112,8 @@ class SpeedPercent(SpeedValue): """ def __init__(self, percent): - assert -100 <= percent <= 100,\ - "{} is an invalid percentage, must be between -100 and 100 (inclusive)".format(percent) + if not -100 <= percent <= 100: + raise ValueError("{} is an invalid percentage, must be between -100 and 100 (inclusive)".format(percent)) self.percent = percent @@ -121,7 +121,8 @@ def __str__(self): return str(self.percent) + "%" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return SpeedPercent(self.percent * other) def to_native_units(self, motor): @@ -143,7 +144,8 @@ def __str__(self): return "{:.2f}".format(self.native_counts) + " counts/sec" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return SpeedNativeUnits(self.native_counts * other) def to_native_units(self, motor=None): @@ -165,16 +167,17 @@ def __str__(self): return str(self.rotations_per_second) + " rot/sec" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return SpeedRPS(self.rotations_per_second * other) def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired rotations-per-second """ - assert abs(self.rotations_per_second) <= motor.max_rps,\ - "invalid rotations-per-second: {} max RPS is {}, {} was requested".format( - motor, motor.max_rps, self.rotations_per_second) + if abs(self.rotations_per_second) > motor.max_rps: + raise ValueError("invalid rotations-per-second: {} max RPS is {}, {} was requested".format( + motor, motor.max_rps, self.rotations_per_second)) return self.rotations_per_second/motor.max_rps * motor.max_speed @@ -190,16 +193,17 @@ def __str__(self): return str(self.rotations_per_minute) + " rot/min" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return SpeedRPM(self.rotations_per_minute * other) def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired rotations-per-minute """ - assert abs(self.rotations_per_minute) <= motor.max_rpm,\ - "invalid rotations-per-minute: {} max RPM is {}, {} was requested".format( - motor, motor.max_rpm, self.rotations_per_minute) + if abs(self.rotations_per_minute) > motor.max_rpm: + raise ValueError("invalid rotations-per-minute: {} max RPM is {}, {} was requested".format( + motor, motor.max_rpm, self.rotations_per_minute)) return self.rotations_per_minute/motor.max_rpm * motor.max_speed @@ -215,16 +219,17 @@ def __str__(self): return str(self.degrees_per_second) + " deg/sec" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return SpeedDPS(self.degrees_per_second * other) def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired degrees-per-second """ - assert abs(self.degrees_per_second) <= motor.max_dps,\ - "invalid degrees-per-second: {} max DPS is {}, {} was requested".format( - motor, motor.max_dps, self.degrees_per_second) + if abs(self.degrees_per_second) > motor.max_dps: + raise ValueError("invalid degrees-per-second: {} max DPS is {}, {} was requested".format( + motor, motor.max_dps, self.degrees_per_second)) return self.degrees_per_second/motor.max_dps * motor.max_speed @@ -240,16 +245,17 @@ def __str__(self): return str(self.degrees_per_minute) + " deg/min" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return SpeedDPM(self.degrees_per_minute * other) def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired degrees-per-minute """ - assert abs(self.degrees_per_minute) <= motor.max_dpm,\ - "invalid degrees-per-minute: {} max DPM is {}, {} was requested".format( - motor, motor.max_dpm, self.degrees_per_minute) + if abs(self.degrees_per_minute) > motor.max_dpm: + raise ValueError("invalid degrees-per-minute: {} max DPM is {}, {} was requested".format( + motor, motor.max_dpm, self.degrees_per_minute)) return self.degrees_per_minute/motor.max_dpm * motor.max_speed @@ -949,8 +955,9 @@ def _speed_native_units(self, speed, label=None): # If speed is not a SpeedValue object we treat it as a percentage if not isinstance(speed, SpeedValue): - assert -100 <= speed <= 100,\ - "{}{} is an invalid speed percentage, must be between -100 and 100 (inclusive)".format("" if label is None else (label + ": ") , speed) + if not -100 <= speed <= 100: + raise ValueError("{}{} is an invalid speed percentage, must be between -100 and 100 (inclusive)".format( + "" if label is None else (label + ": ") , speed)) speed = SpeedPercent(speed) return speed.to_native_units(self) @@ -1666,8 +1673,8 @@ def set_args(self, **kwargs): def set_polarity(self, polarity, motors=None): valid_choices = (LargeMotor.POLARITY_NORMAL, LargeMotor.POLARITY_INVERSED) - assert polarity in valid_choices,\ - "%s is an invalid polarity choice, must be %s" % (polarity, ', '.join(valid_choices)) + if polarity not in valid_choices: + raise ValueError("%s is an invalid polarity choice, must be %s" % (polarity, ', '.join(valid_choices))) motors = motors if motors is not None else self.motors.values() for motor in motors: @@ -2203,7 +2210,8 @@ def follow_gyro_angle(self, speed_native_units = speed.to_native_units(self.left_motor) MAX_SPEED = SpeedNativeUnits(self.max_speed) - assert speed_native_units <= MAX_SPEED, "Speed exceeds the max speed of the motors" + if speed_native_units > MAX_SPEED: + raise ValueError("Speed exceeds the max speed of the motors") while follow_for(self, **kwargs): current_angle = self._gyro.angle @@ -2371,8 +2379,8 @@ def get_speed_steering(self, steering, speed): automatically. """ - assert steering >= -100 and steering <= 100,\ - "{} is an invalid steering, must be between -100 and 100 (inclusive)".format(steering) + if steering < -100 or steering > 100: + raise ValueError("{} is an invalid steering, must be between -100 and 100 (inclusive)".format(steering)) # We don't have a good way to make this generic for the pair... so we # assume that the left motor's speed stats are the same as the right @@ -2690,7 +2698,8 @@ def turn_to_angle(self, speed, angle_target_degrees, brake=True, block=True): """ Rotate in place to `angle_target_degrees` at `speed` """ - assert self.odometry_thread_id, "odometry_start() must be called to track robot coordinates" + if not self.odometry_thread_id: + raise ThreadNotRunning("odometry_start() must be called to track robot coordinates") # Make both target and current angles positive numbers between 0 and 360 if angle_target_degrees < 0: @@ -2729,7 +2738,8 @@ def on_to_coordinates(self, speed, x_target_mm, y_target_mm, brake=True, block=T """ Drive to (`x_target_mm`, `y_target_mm`) coordinates at `speed` """ - assert self.odometry_thread_id, "odometry_start() must be called to track robot coordinates" + if not self.odometry_thread_id: + raise ThreadNotRunning("odometry_start() must be called to track robot coordinates") # stop moving self.off(brake='hold') diff --git a/ev3dev2/sensor/lego.py b/ev3dev2/sensor/lego.py index b5c7301..f4e9531 100644 --- a/ev3dev2/sensor/lego.py +++ b/ev3dev2/sensor/lego.py @@ -643,9 +643,8 @@ def wait_until_angle_changed_by(self, delta, direction_sensitive=False): If ``direction_sensitive`` is False (default) we will wait until angle has changed by ``delta`` in either direction. """ - assert self.mode in (self.MODE_GYRO_G_A, self.MODE_GYRO_ANG, - self.MODE_TILT_ANG),\ - 'Gyro mode should be MODE_GYRO_ANG, MODE_GYRO_G_A or MODE_TILT_ANG' + if self.mode not in (self.MODE_GYRO_G_A, self.MODE_GYRO_ANG, self.MODE_TILT_ANG): + raise ValueError("{} mode is {}, it should be MODE_GYRO_ANG, MODE_GYRO_G_A or MODE_TILT_ANG".format(self, self.mode)) start_angle = self.value(0) if direction_sensitive: @@ -775,7 +774,8 @@ def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, nam super(InfraredSensor, self).__init__(address, name_pattern, name_exact, driver_name='lego-ev3-ir', **kwargs) def _normalize_channel(self, channel): - assert channel >= 1 and channel <= 4, "channel is %s, it must be 1, 2, 3, or 4" % channel + if channel < 1 or channel > 4: + raise ValueError("channel is %s, it must be 1, 2, 3, or 4" % channel) channel = max(1, min(4, channel)) - 1 return channel diff --git a/ev3dev2/sound.py b/ev3dev2/sound.py index a1cdbad..eb02f65 100644 --- a/ev3dev2/sound.py +++ b/ev3dev2/sound.py @@ -116,8 +116,8 @@ class Sound(object): ) def _validate_play_type(self, play_type): - assert play_type in self.PLAY_TYPES, \ - "Invalid play_type %s, must be one of %s" % (play_type, ','.join(str(t) for t in self.PLAY_TYPES)) + if play_type not in self.PLAY_TYPES: + raise ValueError("Invalid play_type %s, must be one of %s" % (play_type, ','.join(str(t) for t in self.PLAY_TYPES))) def _audio_command(self, command, play_type): if is_micropython(): diff --git a/ev3dev2/unit.py b/ev3dev2/unit.py index 2a15abb..984d1b9 100644 --- a/ev3dev2/unit.py +++ b/ev3dev2/unit.py @@ -62,7 +62,8 @@ def __str__(self): return str(self.millimeters) + "mm" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceMillimeters(self.millimeters * other) @property @@ -82,7 +83,8 @@ def __str__(self): return str(self.centimeters) + "cm" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceCentimeters(self.centimeters * other) @property @@ -102,7 +104,8 @@ def __str__(self): return str(self.decimeters) + "dm" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceDecimeters(self.decimeters * other) @property @@ -122,7 +125,8 @@ def __str__(self): return str(self.meters) + "m" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceMeters(self.meters * other) @property @@ -142,7 +146,8 @@ def __str__(self): return str(self.inches) + "in" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceInches(self.inches * other) @property @@ -162,7 +167,8 @@ def __str__(self): return str(self.feet) + "ft" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceFeet(self.feet * other) @property @@ -182,7 +188,8 @@ def __str__(self): return str(self.yards) + "yd" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceYards(self.yards * other) @property @@ -202,7 +209,8 @@ def __str__(self): return str(self.studs) + "stud" def __mul__(self, other): - assert isinstance(other, (float, int)), "{} can only be multiplied by an int or float".format(self) + if not isinstance(other, (float, int)): + raise TypeError("{} can only be multiplied by an int or float, not {}".format(self, type(other))) return DistanceStuds(self.studs * other) @property From 62371020d5f3170df4a41732165cc7042b70581b Mon Sep 17 00:00:00 2001 From: Daniel Walton Date: Sat, 11 Jan 2020 16:41:53 -0500 Subject: [PATCH 2/2] replace asserts with ValueError or TypeError --- debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/changelog b/debian/changelog index e221d7b..74d0361 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ python-ev3dev2 (2.1.0) UNRELEASED; urgency=medium [Daniel Walton] * RPyC update docs and make it easier to use + * replace asserts with ValueError or TypeError [Matěj Volf] * LED animation fix duration None