Skip to content

Commit

Permalink
94 improve exception strings (#129)
Browse files Browse the repository at this point in the history
* Changed the ValueError message to the structure of f'{self.class.name}.<var_name> must be a It is a(n) {type(<var_name>)} with the value of {var_name}'

* Changed the ValueError message to the structure of f'{self.class.name}.<var_name> must be a It is a(n) {type(<var_name>)} with the value of {var_name}.' in game_board.py and added periods to the errors in item.py.

* Changed the ValueError message to the structure of f'{self.class.name}.<var_name> must be a It is a(n) {type(<var_name>)} with the value of {var_name}.' in game_board.py and added periods to the errors in item.py.

* Changed the ValueError message to the structure of f'{self.class.name}.<var_name> must be a It is a(n) {type(<var_name>)} with the value of {var_name}.' in game_board.py and added periods to the errors in item.py.

* Changed the ValueError message to the structure of f'{self.class.name}.<var_name> must be a It is a(n) {type(<var_name>)} with the value of {var_name}.' in game_board.py and added periods to the errors in item.py.

* Changed the ValueError message to the structure of f'{self.class.name}.<var_name> must be a It is a(n) {type(<var_name>)} with the value of {var_name}.' in game_board.py and changed occupational_station.py and tile.py to ValueError to The object type of the object is not handled properly. The object type passed in is {temp}.

* Edited test files in occupiable_station.py, test_avatar.py, test_avatar_inventory.py, test_game_board_no_gen.py, test_item.py, test_occupiable_station.py, and test_station.py so that their error messages matched in both their main file and test file. Still need to fix OccupiableStation test_fail_item_occ, test_set_quantity_fail_greater_than_0, test_set_quantity_fail_stack_size, and test_stack_size_fail_quantity

* Edited test files in occupiable_station.py, test_avatar.py, test_avatar_inventory.py, test_game_board_no_gen.py, test_item.py, test_occupiable_station.py, and test_station.py so that their error messages matched in both their main file and test file. Still need to fix OccupiableStation test_fail_item_occ, test_set_quantity_fail_greater_than_0, test_set_quantity_fail_stack_size, and test_stack_size_fail_quantity

* Changes by ianth

* fixed tests that were failing.
  • Loading branch information
CaitlinAzazel authored Jan 10, 2024
1 parent 2473aaf commit 93fc5b1
Show file tree
Hide file tree
Showing 14 changed files with 213 additions and 105 deletions.
23 changes: 16 additions & 7 deletions game/common/avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,26 +181,32 @@ def held_item(self, item: Item | None) -> None:

# If it's not an item, and it's not None, raise the error
if item is not None and not isinstance(item, Item):
raise ValueError(f'{self.__class__.__name__}.held_item must be an Item or None.')
raise ValueError(
f'{self.__class__.__name__}.held_item must be an Item or None. It is a(n) '
f'{item.__class__.__name__} and has the value of {item}')

# If the item is not contained in the inventory, the error will be raised.
if not self.inventory.__contains__(item):
raise ValueError(f'{self.__class__.__name__}.held_item must be set to an item that already exists'
f' in the inventory.')
f' in the inventory. It has the value of {item}')

# If the item is contained in the inventory, set the held_index to that item's index
self.__held_index = self.inventory.index(item)

@score.setter
def score(self, score: int) -> None:
if score is None or not isinstance(score, int):
raise ValueError(f'{self.__class__.__name__}.score must be an int.')
raise ValueError(
f'{self.__class__.__name__}.score must be an int. It is a(n) {score.__class__.__name__} and has the value of '
f'{score}')
self.__score: int = score

@position.setter
def position(self, position: Vector | None) -> None:
if position is not None and not isinstance(position, Vector):
raise ValueError(f'{self.__class__.__name__}.position must be a Vector or None.')
raise ValueError(
f'{self.__class__.__name__}.position must be a Vector or None. It is a(n) '
f'{position.__class__.__name__} and has the value of {position}')
self.__position: Vector | None = position

@inventory.setter
Expand All @@ -209,16 +215,19 @@ def inventory(self, inventory: list[Item | None]) -> None:
if inventory is None or not isinstance(inventory, list) \
or (len(inventory) > 0 and any(map(lambda item: item is not None and not
isinstance(item, Item), inventory))):
raise ValueError(f'{self.__class__.__name__}.inventory must be a list of Items.')
raise ValueError(
f'{self.__class__.__name__}.inventory must be a list of Items. It is a(n) {inventory.__class__.__name__} '
f'and has the value of {inventory}')
if len(inventory) > self.max_inventory_size:
raise ValueError(f'{self.__class__.__name__}.inventory size must be less than or equal to '
f'max_inventory_size')
f'max_inventory_size. It has the value of {len(inventory)}')
self.__inventory: list[Item] = inventory

@max_inventory_size.setter
def max_inventory_size(self, size: int) -> None:
if size is None or not isinstance(size, int):
raise ValueError(f'{self.__class__.__name__}.max_inventory_size must be an int.')
raise ValueError(f'{self.__class__.__name__}.max_inventory_size must be an int. It is a(n) {size.__class__.__name__} '
f'and has the value of {size}')
self.__max_inventory_size: int = size

# Private helper method that cleans the inventory of items that have a quantity of 0. This is a safety check
Expand Down
39 changes: 28 additions & 11 deletions game/common/items/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,46 +93,61 @@ def stack_size(self) -> int:
@durability.setter
def durability(self, durability: int | None) -> None:
if durability is not None and not isinstance(durability, int):
raise ValueError(f'{self.__class__.__name__}.durability must be an int or None.')
raise ValueError(
f'{self.__class__.__name__}.durability must be an int. It is a(n) {durability.__class__.__name__} with the value of {durability}.')
if durability is not None and self.stack_size != 1:
raise ValueError(
f'{self.__class__.__name__}.durability must be set to None if stack_size is not equal to 1.')
f'{self.__class__.__name__}.durability must be set to None if stack_size is not equal to 1.'
f' {self.__class__.__name__}.durability has the value of {durability}.')
self.__durability = durability

@value.setter
def value(self, value: int) -> None:
if value is None or not isinstance(value, int):
raise ValueError(f'{self.__class__.__name__}.value must be an int.')
raise ValueError(
f'{self.__class__.__name__}.value must be an int.'
f' It is a(n) {value.__class__.__name__} with the value of {value}.')
self.__value: int = value

@quantity.setter
def quantity(self, quantity: int) -> None:
if quantity is None or not isinstance(quantity, int):
raise ValueError(f'{self.__class__.__name__}.quantity must be an int.')
raise ValueError(
f'{self.__class__.__name__}.quantity must be an int.'
f' It is a(n) {quantity.__class__.__name__} with the value of {quantity}.')
if quantity < 0:
raise ValueError(f'{self.__class__.__name__}.quantity must be greater than or equal to 0.')
raise ValueError(
f'{self.__class__.__name__}.quantity must be greater than or equal to 0.'
f' {self.__class__.__name__}.quantity has the value of {quantity}.')

# The self.quantity is set to the lower value between stack_size and the given quantity
# The remaining given quantity is returned if it's larger than self.quantity
if quantity > self.stack_size:
raise ValueError(f'{self.__class__.__name__}.quantity cannot be greater than '
f'{self.__class__.__name__}.stack_size')
f'{self.__class__.__name__}.stack_size.'
f' {self.__class__.__name__}.quantity has the value of {quantity}.')
self.__quantity: int = quantity

@stack_size.setter
def stack_size(self, stack_size: int) -> None:
if stack_size is None or not isinstance(stack_size, int):
raise ValueError(f'{self.__class__.__name__}.stack_size must be an int.')
raise ValueError(
f'{self.__class__.__name__}.stack_size must be an int.'
f' It is a(n) {stack_size.__class__.__name__} with the value of {stack_size}.')
if self.durability is not None and stack_size != 1:
raise ValueError(f'{self.__class__.__name__}.stack_size must be 1 if {self.__class__.__name__}.durability '
f'is not None.')
f'is not None. {self.__class__.__name__}.stack_size has the value of {stack_size}.')
if self.__quantity is not None and stack_size < self.__quantity:
raise ValueError(f'{self.__class__.__name__}.stack_size must be greater than or equal to the quantity.')
raise ValueError(
f'{self.__class__.__name__}.stack_size must be greater than or equal to the quantity.'
f' {self.__class__.__name__}.stack_size has the value of {stack_size}.')
self.__stack_size: int = stack_size

def take(self, item: Self) -> Self | None:
if item is not None and not isinstance(item, Item):
raise ValueError(f'{item.__class__.__name__} is not of type Item.')
raise ValueError(
f'{item.__class__.__name__}.item must be an item.'
f' It is a(n) {item.__class__.__name__} with the value of {item}.')

# If the item is None, just return None
if item is None:
Expand All @@ -156,7 +171,9 @@ def take(self, item: Self) -> Self | None:

def pick_up(self, item: Self) -> Self | None:
if item is not None and not isinstance(item, Item):
raise ValueError(f'{item.__class__.__name__} is not of type Item.')
raise ValueError(
f'{item.__class__.__name__}.item must be an item.'
f' It is a(n) {item.__class__.__name__} with the value of {item}.')

# If the item is None, just return None
if item is None:
Expand Down
43 changes: 27 additions & 16 deletions game/common/map/game_board.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ def seed(self, seed: int | None) -> None:
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if seed is not None and not isinstance(seed, int):
raise ValueError(f'{self.__class__.__name__}.seed must be an integer or None.')
raise ValueError(
f'{self.__class__.__name__}.seed must be an int. '
f'It is a(n) {seed.__class__.__name__} with the value of {seed}.')
self.__seed = seed

@property
Expand All @@ -149,7 +151,9 @@ def game_map(self, game_map: list[list[Tile]]) -> None:
any(map(lambda l: not isinstance(l, list), game_map)) or
any([any(map(lambda g: not isinstance(g, Tile), tile_list))
for tile_list in game_map])):
raise ValueError(f'{self.__class__.__name__}.game_map must be a list[list[Tile]].')
raise ValueError(
f'{self.__class__.__name__}.game_map must be a list[list[Tile]]. '
f'It is a(n) {game_map.__class__.__name__} with the value of {game_map}.')
self.__game_map = game_map

@property
Expand All @@ -161,7 +165,9 @@ def map_size(self, map_size: Vector) -> None:
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if map_size is None or not isinstance(map_size, Vector):
raise ValueError(f'{self.__class__.__name__}.map_size must be a Vector.')
raise ValueError(
f'{self.__class__.__name__}.map_size must be a Vector. '
f'It is a(n) {map_size.__class__.__name__} with the value of {map_size}.')
self.__map_size = map_size

@property
Expand All @@ -173,13 +179,10 @@ def locations(self, locations: dict[tuple[Vector]:list[GameObject]] | None) -> N
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if locations is not None and not isinstance(locations, dict):
raise ValueError("Locations must be a dict. The key must be a tuple of Vector Objects, and the "
"value a list of GameObject.")
# if locations is not None:
# for k, v in locations.items():
# if len(k) != len(v):
# raise ValueError("Cannot set the locations for the game_board. A key has a different "
# "length than its key.")
raise ValueError(
f'Locations must be a dict. The key must be a tuple of Vector Objects, '
f'and the value a list of GameObject. '
f'It is a(n) {locations.__class__.__name__} with the value of {locations}.')

self.__locations = locations

Expand All @@ -192,7 +195,9 @@ def walled(self, walled: bool) -> None:
if self.game_map is not None:
raise RuntimeError(f'{self.__class__.__name__} variables cannot be changed once generate_map is run.')
if walled is None or not isinstance(walled, bool):
raise ValueError(f'{self.__class__.__name__}.walled must be a bool.')
raise ValueError(
f'{self.__class__.__name__}.walled must be a bool. '
f'It is a(n) {walled.__class__.__name__} with the value of {walled}.')

self.__walled = walled

Expand All @@ -213,7 +218,9 @@ def generate_map(self) -> None:
def __populate_map(self) -> None:
for k, v in self.locations.items():
if len(k) == 0 or len(v) == 0: # Key-Value lengths must be > 0 and equal
raise ValueError("A key-value pair from game_board.locations has a length of 0. ")
raise ValueError(
f'A key-value pair from game_board.locations has a length of 0. '
f'The length of the keys is {len(k)} and the length of the values is {len(v)}.')

# random.sample returns a randomized list which is used in __help_populate()
j = random.sample(k, k=len(k))
Expand Down Expand Up @@ -253,7 +260,9 @@ def __help_populate(self, vector_list: list[Vector], game_object_list: list[Game
temp_tile = temp_tile.occupied_by

if temp_tile.occupied_by is not None:
raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.")
raise ValueError(
f'Last item on the given tile doesn\'t have the \'occupied_by\' attribute. '
f'It is a(n) {temp_tile.occupied_by.__class__.__name__} with the value of {temp_tile.occupied_by}.')

temp_tile.occupied_by = game_object

Expand All @@ -268,7 +277,9 @@ def __help_populate(self, vector_list: list[Vector], game_object_list: list[Game

for game_object in remaining_objects:
if not hasattr(temp_tile, 'occupied_by') or temp_tile.occupied_by is not None:
raise ValueError("Last item on the given tile doesn't have the 'occupied_by' attribute.")
raise ValueError(
f'Last item on the given tile doesn\'t have the \'occupied_by\' attribute.'
f' It is a(n) {temp_tile.occupied_by.__class__.__name__} with the value of {temp_tile.occupied_by}.')
temp_tile.occupied_by = game_object
temp_tile = temp_tile.occupied_by

Expand Down Expand Up @@ -329,8 +340,8 @@ def __from_json_helper(self, data: dict) -> GameObject:
return Avatar().from_json(data)
# If adding more ObjectTypes that can be placed on the game_board, specify here
case _:
raise ValueError(f'The location (dict) must have a valid key (tuple of vectors) and a valid value ('
f'list of GameObjects).')
raise ValueError(
f'The object type of the object is not handled properly. The object type passed in is {temp}.')

def from_json(self, data: dict) -> Self:
super().from_json(data)
Expand Down
6 changes: 4 additions & 2 deletions game/common/map/occupiable.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ def occupied_by(self) -> GameObject | None:
@occupied_by.setter
def occupied_by(self, occupied_by: GameObject | None) -> None:
if occupied_by is not None and isinstance(occupied_by, Item):
raise ValueError(f'{self.__class__.__name__}.occupied_by cannot be an Item.')
raise ValueError(
f'{self.__class__.__name__}.occupied_by must be a GameObject. It is a(n) {occupied_by.__class__.__name__} with the value of {occupied_by}.')
if occupied_by is not None and not isinstance(occupied_by, GameObject):
raise ValueError(f'{self.__class__.__name__}.occupied_by must be None or an instance of GameObject.')
raise ValueError(
f'{self.__class__.__name__}.occupied_by must be None or an instance of GameObject. It is a(n) {occupied_by.__class__.__name__} with the value of {occupied_by}.')
self.__occupied_by = occupied_by

def to_json(self) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion game/common/map/tile.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ def from_json(self, data: dict) -> Self:
case ObjectType.WALL:
self.occupied_by: Wall = Wall().from_json(occupied_by)
case _:
raise Exception(f'Could not parse occupied_by: {occupied_by}')
raise Exception(f'The object type of the object is not handled properly. The object type passed in is {occupied_by}.')
return self
20 changes: 13 additions & 7 deletions game/common/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Player(GameObject):
items, etc.). For more details on the difference between the Player and Avatar classes, refer to the README
document.
"""

def __init__(self, code: object | None = None, team_name: str | None = None, actions: list[ActionType] = [],
avatar: Avatar | None = None):
super().__init__()
Expand Down Expand Up @@ -50,7 +51,8 @@ def actions(self, actions: list[ActionType] | list) -> None: # showing it retur
if actions is None or not isinstance(actions, list) \
or (len(actions) > 0
and any(map(lambda action_type: not isinstance(action_type, ActionType), actions))):
raise ValueError(f'{self.__class__.__name__}.action must be an empty list or a list of action types')
raise ValueError(
f'{self.__class__.__name__}.action must be an empty list or a list of action types. It is a(n) {actions.__class__.__name__} and has the value of {actions}.')
# ^if it's not either throw an error
self.__actions = actions

Expand All @@ -61,7 +63,8 @@ def functional(self) -> bool:
@functional.setter # do this for all the setters
def functional(self, functional: bool) -> None: # this enforces the type hinting
if functional is None or not isinstance(functional, bool): # if this statement is true throw an error
raise ValueError(f'{self.__class__.__name__}.functional must be a boolean')
raise ValueError(
f'{self.__class__.__name__}.functional must be a boolean. It is a(n) {functional.__class__.__name__} and has the value of {functional}.')
self.__functional = functional

@property
Expand All @@ -71,7 +74,8 @@ def team_name(self) -> str | None:
@team_name.setter
def team_name(self, team_name: str | None) -> None:
if team_name is not None and not isinstance(team_name, str):
raise ValueError(f'{self.__class__.__name__}.team_name must be a String or None')
raise ValueError(
f'{self.__class__.__name__}.team_name must be a String or None. It is a(n) {team_name.__class__.__name__} and has the value of {team_name}.')
self.__team_name = team_name

@property
Expand All @@ -91,7 +95,8 @@ def avatar(self) -> Avatar:
@avatar.setter
def avatar(self, avatar: Avatar) -> None:
if avatar is not None and not isinstance(avatar, Avatar):
raise ValueError(f'{self.__class__.__name__}.avatar must be Avatar or None')
raise ValueError(
f'{self.__class__.__name__}.avatar must be Avatar or None. It is a(n) {avatar.__class__.__name__} and has the value of {avatar}.')
self.__avatar = avatar

@property
Expand All @@ -101,7 +106,8 @@ def object_type(self) -> ObjectType:
@object_type.setter
def object_type(self, object_type: ObjectType) -> None:
if object_type is None or not isinstance(object_type, ObjectType):
raise ValueError(f'{self.__class__.__name__}.object_type must be ObjectType')
raise ValueError(
f'{self.__class__.__name__}.object_type must be ObjectType. It is a(n) {object_type.__class__.__name__} and has the value of {object_type}.')
self.__object_type = object_type

def to_json(self):
Expand Down Expand Up @@ -150,12 +156,12 @@ def from_json(self, data):
# self.action = Action().from_json(data['action']) if data['action'] is not None else None
# self.avatar = Avatar().from_json(data['avatar']) if data['avatar'] is not None else None

# to String
# to String
def __str__(self):
p = f"""ID: {self.id}
Team name: {self.team_name}
Actions:
"""
# This concatenates every action from the list of actions to the string
[p:= p + action for action in self.actions]
[p := p + action for action in self.actions]
return p
Loading

0 comments on commit 93fc5b1

Please sign in to comment.