From 83aed4cdf2f907096844a3d3f69d3a9039d08750 Mon Sep 17 00:00:00 2001 From: santichausis Date: Sun, 27 Sep 2026 13:13:22 -0300 Subject: [PATCH 1/4] Fix Omega Ruby/Alpha Sapphire sprite key in generation-vi sprites POKEMON_SPRITE_CONFIG used the sprites repo's folder name ("omegaruby-alphasapphire") as the dict key for generation-vi ORAS sprites, instead of the actual version-group name ("omega-ruby-alpha-sapphire", per version-group/16). Since this dict is resolved verbatim into the sprites JSON stored on Pokemon and PokemonForm, both pokemon//sprites/gen-vi/ and pokemon-form//sprites/gen-vi/ returned the wrong key. Only the dict key changes; the file path strings inside it keep referencing the real (non-hyphenated) sprites repo folder. --- data/v2/build.py | 2 +- pokemon_v2/test_models.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/data/v2/build.py b/data/v2/build.py index 07fd22e06..7a9e89cbf 100644 --- a/data/v2/build.py +++ b/data/v2/build.py @@ -403,7 +403,7 @@ "front_default": ("pokemon/versions/generation-vi/icons/", "png"), "front_female": ("pokemon/versions/generation-vi/icons/female/", "png"), }, - "omegaruby-alphasapphire": { + "omega-ruby-alpha-sapphire": { "front_default": ( "pokemon/versions/generation-vi/omegaruby-alphasapphire/", "png", diff --git a/pokemon_v2/test_models.py b/pokemon_v2/test_models.py index 5b1fed0bd..b886185c9 100644 --- a/pokemon_v2/test_models.py +++ b/pokemon_v2/test_models.py @@ -6,6 +6,7 @@ from django.test import TestCase from typing_extensions import override +from data.v2.build import POKEMON_SPRITE_CONFIG, _resolve_sprite_config from pokemon_v2.models import * @@ -228,3 +229,20 @@ def test_identifier_pattern_examples(self): self.VALID_IDENTIFIER_PATTERN.match(identifier), f"{identifier} should be invalid but was accepted", ) + + +class PokemonSpriteConfigTestCase(TestCase): + # regression test for https://github.com/PokeAPI/pokeapi/issues/1684: + # the generation-vi Omega Ruby/Alpha Sapphire sprite dict key must match + # the real version-group name ("omega-ruby-alpha-sapphire"), not the + # sprites repo's folder name ("omegaruby-alphasapphire") used in the paths. + def test_generation_vi_oras_sprite_key_matches_version_group_name(self): + gen_vi = POKEMON_SPRITE_CONFIG["versions"]["generation-vi"] + self.assertIn("omega-ruby-alpha-sapphire", gen_vi) + self.assertNotIn("omegaruby-alphasapphire", gen_vi) + + def test_resolved_sprite_config_uses_version_group_name_as_key(self): + resolved = _resolve_sprite_config(POKEMON_SPRITE_CONFIG, lambda path, extension: f"{path}.{extension}") + gen_vi = resolved["versions"]["generation-vi"] + self.assertIn("omega-ruby-alpha-sapphire", gen_vi) + self.assertNotIn("omegaruby-alphasapphire", gen_vi) From 9598727a2dd18b33ad8a6a2518345b1a37315ddf Mon Sep 17 00:00:00 2001 From: santichausis Date: Mon, 28 Sep 2026 12:49:02 -0300 Subject: [PATCH 2/4] Assert both keys and sprite folders for generation-vi groups Replace the single-key check with one that asserts, per generation-vi group, the expected key and that its paths live under the sprites repo folder. This also guards against renaming the paths to match the key, which would break resolution: the sprites repo folder really is omegaruby-alphasapphire. --- pokemon_v2/test_models.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/pokemon_v2/test_models.py b/pokemon_v2/test_models.py index b886185c9..63b3eeb5c 100644 --- a/pokemon_v2/test_models.py +++ b/pokemon_v2/test_models.py @@ -6,7 +6,7 @@ from django.test import TestCase from typing_extensions import override -from data.v2.build import POKEMON_SPRITE_CONFIG, _resolve_sprite_config +from data.v2.build import POKEMON_SPRITE_CONFIG from pokemon_v2.models import * @@ -232,17 +232,20 @@ def test_identifier_pattern_examples(self): class PokemonSpriteConfigTestCase(TestCase): - # regression test for https://github.com/PokeAPI/pokeapi/issues/1684: - # the generation-vi Omega Ruby/Alpha Sapphire sprite dict key must match - # the real version-group name ("omega-ruby-alpha-sapphire"), not the - # sprites repo's folder name ("omegaruby-alphasapphire") used in the paths. - def test_generation_vi_oras_sprite_key_matches_version_group_name(self): + def test_generation_vi_sprite_group_keys_and_paths(self): + # keys are version-group names, while paths use the sprites repo's folder names, + # which don't always match (https://github.com/PokeAPI/pokeapi/issues/1684) + expected_folders = { + "icons": "icons", + "omega-ruby-alpha-sapphire": "omegaruby-alphasapphire", + "x-y": "x-y", + } gen_vi = POKEMON_SPRITE_CONFIG["versions"]["generation-vi"] - self.assertIn("omega-ruby-alpha-sapphire", gen_vi) - self.assertNotIn("omegaruby-alphasapphire", gen_vi) - - def test_resolved_sprite_config_uses_version_group_name_as_key(self): - resolved = _resolve_sprite_config(POKEMON_SPRITE_CONFIG, lambda path, extension: f"{path}.{extension}") - gen_vi = resolved["versions"]["generation-vi"] - self.assertIn("omega-ruby-alpha-sapphire", gen_vi) - self.assertNotIn("omegaruby-alphasapphire", gen_vi) + + self.assertEqual(set(gen_vi), set(expected_folders)) + for group, folder in expected_folders.items(): + for path, _extension in gen_vi[group].values(): + self.assertTrue( + path.startswith(f"pokemon/versions/generation-vi/{folder}/"), + f"{group} path {path} is not under the {folder}/ sprites folder", + ) From 21e7536d0797c3029b0c7294a82b256f63321b2a Mon Sep 17 00:00:00 2001 From: santichausis Date: Mon, 28 Sep 2026 15:33:05 -0300 Subject: [PATCH 3/4] Fall back to the pokemon id for default form sprites _pokemon_form_sprite_lookup only looked for {pokemon_id}-{form}.png, but a default form's sprites are usually stored under the bare pokemon id. unown-a's front sprite is 201.png (201-a.png doesn't exist), so most of its form sprites came out null (#1687). Try the form-specific file first and fall back to the bare id for default forms. Non-default forms keep resolving only to their own files. --- data/v2/build.py | 17 +++++++++++++---- pokemon_v2/test_models.py | 24 +++++++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/data/v2/build.py b/data/v2/build.py index 7a9e89cbf..daacdafe3 100644 --- a/data/v2/build.py +++ b/data/v2/build.py @@ -1718,12 +1718,21 @@ def lookup(path: str, extension: str) -> str | None: def _pokemon_form_sprite_lookup(info: list[str]) -> Callable[[str, str], str | None]: form_identifier = info[2] pokemon_id = int(info[3]) - file_name = f"{pokemon_id}-{form_identifier}" if form_identifier else None + is_default = info[5] == "1" + + file_names = [] + if form_identifier: + file_names.append(f"{pokemon_id}-{form_identifier}") + # a default form's sprites are usually stored under the bare pokemon id (e.g. unown-a -> 201.png) + if is_default or not form_identifier: + file_names.append(str(pokemon_id)) def lookup(path: str, extension: str) -> str | None: - if file_name is None: - return None - return file_path_or_none(f"{path}{file_name}.{extension}") + for file_name in file_names: + sprite = file_path_or_none(f"{path}{file_name}.{extension}") + if sprite: + return sprite + return None return lookup diff --git a/pokemon_v2/test_models.py b/pokemon_v2/test_models.py index 63b3eeb5c..24b3fbc8e 100644 --- a/pokemon_v2/test_models.py +++ b/pokemon_v2/test_models.py @@ -1,12 +1,14 @@ import csv import os import re +from unittest import mock from django.conf import settings from django.test import TestCase from typing_extensions import override -from data.v2.build import POKEMON_SPRITE_CONFIG +from data.v2 import build +from data.v2.build import POKEMON_SPRITE_CONFIG, _pokemon_form_sprite_lookup from pokemon_v2.models import * @@ -249,3 +251,23 @@ def test_generation_vi_sprite_group_keys_and_paths(self): path.startswith(f"pokemon/versions/generation-vi/{folder}/"), f"{group} path {path} is not under the {folder}/ sprites folder", ) + + # https://github.com/PokeAPI/pokeapi/issues/1687: unown-a's front sprite is stored as + # 201.png, not 201-a.png, so default forms must fall back to the bare pokemon id + def test_form_sprite_lookup_falls_back_to_pokemon_id_for_default_forms(self): + # pokemon_forms.csv columns: id, identifier, form_identifier, pokemon_id, introduced_in, is_default + unown_a = ["201", "unown-a", "a", "201", "3", "1"] + unown_b = ["10001", "unown-b", "b", "201", "3", "0"] + images = {"pokemon/201.png", "pokemon/back/201-a.png", "pokemon/back/201.png"} + + with mock.patch.object(build, "RESOURCE_IMAGES", images): + unown_a_lookup = _pokemon_form_sprite_lookup(unown_a) + unown_b_lookup = _pokemon_form_sprite_lookup(unown_b) + + self.assertEqual(unown_a_lookup("pokemon/", "png"), build.MEDIA_DIR.format(file_name="pokemon/201.png")) + # a form-specific sprite still takes precedence over the fallback + self.assertEqual( + unown_a_lookup("pokemon/back/", "png"), build.MEDIA_DIR.format(file_name="pokemon/back/201-a.png") + ) + # non-default forms never borrow the base pokemon's sprite + self.assertIsNone(unown_b_lookup("pokemon/", "png")) From f6d1e4b1a25a8854aec95d7e47d9731b745996ce Mon Sep 17 00:00:00 2001 From: santichausis Date: Wed, 30 Sep 2026 20:59:24 -0300 Subject: [PATCH 4/4] Point ORAS sprite paths at the renamed omega-ruby-alpha-sapphire folder The sprites repo is renaming pokemon/versions/generation-vi/ omegaruby-alphasapphire to omega-ruby-alpha-sapphire so the folder matches the version-group name used as the key. Update the eight gen-vi ORAS paths accordingly and have the test require every gen-vi group's key and folder to match. This must land after the sprites repo rename; before it, ORAS sprites would resolve to null. --- data/v2/build.py | 16 ++++++++-------- pokemon_v2/test_models.py | 19 +++++++------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/data/v2/build.py b/data/v2/build.py index daacdafe3..8f5821cce 100644 --- a/data/v2/build.py +++ b/data/v2/build.py @@ -405,35 +405,35 @@ }, "omega-ruby-alpha-sapphire": { "front_default": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/", "png", ), "front_female": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/female/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/female/", "png", ), "front_shiny": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/shiny/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/shiny/", "png", ), "front_shiny_female": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/shiny/female/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/shiny/female/", "png", ), "back_default": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/back/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/", "png", ), "back_female": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/back/female/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/female/", "png", ), "back_shiny": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/back/shiny/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/shiny/", "png", ), "back_shiny_female": ( - "pokemon/versions/generation-vi/omegaruby-alphasapphire/back/shiny/female/", + "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/shiny/female/", "png", ), }, diff --git a/pokemon_v2/test_models.py b/pokemon_v2/test_models.py index 24b3fbc8e..9a54394d4 100644 --- a/pokemon_v2/test_models.py +++ b/pokemon_v2/test_models.py @@ -235,21 +235,16 @@ def test_identifier_pattern_examples(self): class PokemonSpriteConfigTestCase(TestCase): def test_generation_vi_sprite_group_keys_and_paths(self): - # keys are version-group names, while paths use the sprites repo's folder names, - # which don't always match (https://github.com/PokeAPI/pokeapi/issues/1684) - expected_folders = { - "icons": "icons", - "omega-ruby-alpha-sapphire": "omegaruby-alphasapphire", - "x-y": "x-y", - } + # keys are version-group names and each group's sprites live in a folder of the + # same name (https://github.com/PokeAPI/pokeapi/issues/1684) gen_vi = POKEMON_SPRITE_CONFIG["versions"]["generation-vi"] - self.assertEqual(set(gen_vi), set(expected_folders)) - for group, folder in expected_folders.items(): - for path, _extension in gen_vi[group].values(): + self.assertEqual(set(gen_vi), {"icons", "omega-ruby-alpha-sapphire", "x-y"}) + for group, sprites in gen_vi.items(): + for path, _extension in sprites.values(): self.assertTrue( - path.startswith(f"pokemon/versions/generation-vi/{folder}/"), - f"{group} path {path} is not under the {folder}/ sprites folder", + path.startswith(f"pokemon/versions/generation-vi/{group}/"), + f"{group} path {path} is not under the {group}/ sprites folder", ) # https://github.com/PokeAPI/pokeapi/issues/1687: unown-a's front sprite is stored as