From ffddf77a83a4848a720ceeab510a2d29f8da4190 Mon Sep 17 00:00:00 2001 From: Fenhl Date: Thu, 12 Dec 2024 05:15:43 +0000 Subject: [PATCH] More mypy error fixes --- RuleParser.py | 4 ++-- Rules.py | 10 +++++++--- SettingsList.py | 4 ++++ mypy.ini | 10 ++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 mypy.ini diff --git a/RuleParser.py b/RuleParser.py index d948982d2..811dfcad5 100644 --- a/RuleParser.py +++ b/RuleParser.py @@ -137,10 +137,10 @@ def visit_Tuple(self, node: ast.Tuple) -> Any: item, count = node.elts - if not isinstance(item, ast.Name) and not (isinstance(item, ast.Constant) and isinstance(item.value, str)): - raise Exception('Parse Error: first value must be an item. Got %s' % item.__class__.__name__, None if self.current_spot is None else self.current_spot.name, ast.dump(node, False)) if isinstance(item, ast.Constant) and isinstance(item.value, str): item = ast.Name(id=escape_name(item.value), ctx=ast.Load()) + if not isinstance(item, ast.Name): + raise Exception('Parse Error: first value must be an item. Got %s' % item.__class__.__name__, None if self.current_spot is None else self.current_spot.name, ast.dump(node, False)) if not (isinstance(count, ast.Name) or (isinstance(count, ast.Constant) and isinstance(count.value, int))): raise Exception('Parse Error: second value must be a number. Got %s' % item.__class__.__name__, None if self.current_spot is None else self.current_spot.name, ast.dump(node, False)) diff --git a/Rules.py b/Rules.py index e12a5adfd..64744217f 100644 --- a/Rules.py +++ b/Rules.py @@ -54,8 +54,8 @@ def set_rules(world: World) -> None: world.shop_prices[location.name] = dist_location.price location.add_rule(create_shop_rule(location)) else: - add_item_rule(location, lambda location, item: item.type == 'Shop' and item.world.id == location.world.id) - elif location.type in ['Scrub', 'GrottoScrub']: + add_item_rule(location, lambda location, item: item.type == 'Shop' and item.world is not None and location.world is not None and item.world.id == location.world.id) + elif location.type in ('Scrub', 'GrottoScrub'): location.add_rule(create_shop_rule(location)) else: add_item_rule(location, lambda location, item: item.type != 'Shop') @@ -99,6 +99,8 @@ def required_wallets(price: Optional[int]) -> int: if price > 99: return 1 return 0 + + assert location.world is not None return location.world.parser.parse_rule('(Progressive_Wallet, %d)' % required_wallets(location.price)) @@ -140,6 +142,7 @@ def set_shop_rules(world: World): wallet2 = world.parser.parse_rule('(Progressive_Wallet, 2)') is_adult = world.parser.parse_rule('is_adult') for location in world.get_filled_locations(): + assert location.item is not None if location.item.type == 'Shop': # Add wallet requirements if location.item.name in ['Buy Fish', 'Buy Goron Tunic', 'Buy Bombchu (20)', 'Buy Bombs (30)']: @@ -162,7 +165,7 @@ def set_shop_rules(world: World): 'Buy Red Potion for 40 Rupees', 'Buy Red Potion for 50 Rupees', "Buy Fairy's Spirit"]: - location.add_rule(State.has_bottle) + location.add_rule(State.has_bottle) # type: ignore #TODO figure out why mypy doesn't accept this if location.item.name in ['Buy Bombchu (10)', 'Buy Bombchu (20)', 'Buy Bombchu (5)']: location.add_rule(found_bombchus) @@ -181,6 +184,7 @@ def set_entrances_based_rules(worlds: Collection[World]) -> None: if location.type == 'Shop': # If All Locations Reachable is on, prevent shops only ever reachable as child from containing Buy Goron Tunic and Buy Zora Tunic items if not world.check_beatable_only: + assert location.parent_region is not None if not search.can_reach(location.parent_region, age='adult'): forbid_item(location, 'Buy Goron Tunic') forbid_item(location, 'Buy Zora Tunic') diff --git a/SettingsList.py b/SettingsList.py index aa11dd1f4..aef9e9838 100644 --- a/SettingsList.py +++ b/SettingsList.py @@ -5463,6 +5463,8 @@ def build_close_match(name: str, value_type: str, source_list: Optional[list[str elif value_type == 'location': source = location_table.keys() elif value_type == 'entrance': + assert isinstance(source_list, dict) + assert isinstance(source, list) for pool in source_list.values(): for entrance in pool: source.append(entrance.name) @@ -5471,6 +5473,7 @@ def build_close_match(name: str, value_type: str, source_list: Optional[list[str elif value_type == 'setting': source = SettingInfos.setting_infos.keys() elif value_type == 'choice': + assert source_list is not None source = source_list # Ensure name and source are type string to prevent errors close_match = difflib.get_close_matches(str(name), map(str, source), 1) @@ -5500,6 +5503,7 @@ def validate_settings(settings_dict: dict[str, Any], *, check_conflicts: bool = continue # Ensure that the given choice is a valid choice for the setting elif info.choice_list and choice not in info.choice_list: + assert isinstance(choice, str) raise ValueError('%r is not a valid choice for setting %r. %s' % (choice, setting, build_close_match(choice, 'choice', info.choice_list))) # Ensure no conflicting settings are specified if check_conflicts and info.disable is not None: diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 000000000..427572ca3 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,10 @@ +[mypy] +files = OoTRandomizer.py + +#TODO also type check these modules +[mypy-Goals.*] +ignore_errors = True +[mypy-Plandomizer.*] +ignore_errors = True +[mypy-Spoiler.*] +ignore_errors = True