mirror of
https://github.com/veekun/pokedex.git
synced 2024-08-20 18:16:34 +00:00
Update media accessors wrt repo split
All accessors now take a `root` arg, the root of the media tree. Alternatively `root` can be a custom MediaFile subclass, which should allow neat tricks like: - Checking some kind of manifest to prevent stat() calls - Custom properties of the file objects (e.g. for HTML <img> tags) - Downloading the media on demand Tests assume media is at pokedex/data/media, skip otherwise.
This commit is contained in:
parent
134f5a00ff
commit
ab2baaa759
2 changed files with 96 additions and 50 deletions
|
@ -8,6 +8,7 @@ This, of course, takes a lot of time to run.
|
|||
|
||||
import os
|
||||
import re
|
||||
from functools import wraps
|
||||
|
||||
from nose.tools import *
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
@ -22,47 +23,66 @@ basedir = pkg_resources.resource_filename('pokedex', 'data/media')
|
|||
|
||||
path_re = re.compile('^[-a-z0-9./]*$')
|
||||
|
||||
root = pkg_resources.resource_filename('pokedex', 'data/media')
|
||||
|
||||
media_available = media.BaseMedia(root).available
|
||||
|
||||
def if_available(func):
|
||||
@wraps(func)
|
||||
def if_available_wrapper(*args, **kwargs):
|
||||
if not media_available:
|
||||
raise SkipTest('Media not available at %s' % root)
|
||||
else:
|
||||
func(*args, **kwargs)
|
||||
return if_available_wrapper
|
||||
|
||||
@if_available
|
||||
def test_totodile():
|
||||
"""Totodile's female sprite -- same as male"""
|
||||
totodile = session.query(tables.Pokemon).filter_by(identifier=u'totodile').one()
|
||||
accessor = media.PokemonMedia(totodile)
|
||||
accessor = media.PokemonMedia(root, totodile)
|
||||
assert accessor.sprite() == accessor.sprite(female=True)
|
||||
|
||||
@if_available
|
||||
def test_chimecho():
|
||||
"""Chimecho's Platinum female backsprite -- diffeent from male"""
|
||||
chimecho = session.query(tables.Pokemon).filter_by(identifier=u'chimecho').one()
|
||||
accessor = media.PokemonMedia(chimecho)
|
||||
accessor = media.PokemonMedia(root, chimecho)
|
||||
male = accessor.sprite('platinum', back=True, frame=2)
|
||||
female = accessor.sprite('platinum', back=True, female=True, frame=2)
|
||||
assert male != female
|
||||
|
||||
@if_available
|
||||
def test_venonat():
|
||||
"""Venonat's shiny Yellow sprite -- same as non-shiny"""
|
||||
venonat = session.query(tables.Pokemon).filter_by(identifier=u'venonat').one()
|
||||
accessor = media.PokemonMedia(venonat)
|
||||
accessor = media.PokemonMedia(root, venonat)
|
||||
assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True)
|
||||
|
||||
@if_available
|
||||
def test_arceus_icon():
|
||||
"""Arceus fire-form icon -- same as base icon"""
|
||||
arceus = session.query(tables.Pokemon).filter_by(identifier=u'arceus').one()
|
||||
accessor = media.PokemonMedia(arceus)
|
||||
accessor = media.PokemonMedia(root, arceus)
|
||||
fire_arceus = [f for f in arceus.forms if f.identifier == 'fire'][0]
|
||||
fire_accessor = media.PokemonFormMedia(fire_arceus)
|
||||
fire_accessor = media.PokemonFormMedia(root, fire_arceus)
|
||||
assert accessor.icon() == fire_accessor.icon()
|
||||
|
||||
@if_available
|
||||
@raises(ValueError)
|
||||
def test_strict_castform():
|
||||
"""Castform rainy form overworld with strict -- unavailable"""
|
||||
castform = session.query(tables.Pokemon).filter_by(identifier=u'castform').first()
|
||||
rainy_castform = [f for f in castform.forms if f.identifier == 'rainy'][0]
|
||||
rainy_castform = media.PokemonFormMedia(rainy_castform)
|
||||
rainy_castform = media.PokemonFormMedia(root, rainy_castform)
|
||||
rainy_castform.overworld('up', strict=True)
|
||||
|
||||
@if_available
|
||||
@raises(ValueError)
|
||||
def test_strict_exeggcute():
|
||||
"""Exeggcutes's female backsprite, with strict -- unavailable"""
|
||||
exeggcute = session.query(tables.Pokemon).filter_by(identifier=u'exeggcute').one()
|
||||
accessor = media.PokemonMedia(exeggcute)
|
||||
accessor = media.PokemonMedia(root, exeggcute)
|
||||
accessor.sprite(female=True, strict=True)
|
||||
|
||||
|
||||
|
@ -73,6 +93,7 @@ def get_all_filenames():
|
|||
all_filenames = set()
|
||||
|
||||
for dirpath, dirnames, filenames in os.walk(basedir):
|
||||
dirnames[:] = [dirname for dirname in dirnames if dirname != '.git']
|
||||
for filename in filenames:
|
||||
path = os.path.join(dirpath, filename)
|
||||
assert path_re.match(path), path
|
||||
|
@ -101,6 +122,7 @@ def hit(filenames, method, *args, **kwargs):
|
|||
pass
|
||||
return True
|
||||
|
||||
@if_available
|
||||
def check_get_everything():
|
||||
"""
|
||||
For every the accessor method, loop over the Cartesian products of all
|
||||
|
@ -121,23 +143,23 @@ def check_get_everything():
|
|||
# Some small stuff first
|
||||
|
||||
for damage_class in session.query(tables.MoveDamageClass).all():
|
||||
assert hit(filenames, media.DamageClassMedia(damage_class).icon)
|
||||
assert hit(filenames, media.DamageClassMedia(root, damage_class).icon)
|
||||
|
||||
for habitat in session.query(tables.PokemonHabitat).all():
|
||||
assert hit(filenames, media.HabitatMedia(habitat).icon)
|
||||
assert hit(filenames, media.HabitatMedia(root, habitat).icon)
|
||||
|
||||
for shape in session.query(tables.PokemonShape).all():
|
||||
assert hit(filenames, media.ShapeMedia(shape).icon)
|
||||
assert hit(filenames, media.ShapeMedia(root, shape).icon)
|
||||
|
||||
for item_pocket in session.query(tables.ItemPocket).all():
|
||||
assert hit(filenames, media.ItemPocketMedia(item_pocket).icon)
|
||||
assert hit(filenames, media.ItemPocketMedia(item_pocket).icon, selected=True)
|
||||
assert hit(filenames, media.ItemPocketMedia(root, item_pocket).icon)
|
||||
assert hit(filenames, media.ItemPocketMedia(root, item_pocket).icon, selected=True)
|
||||
|
||||
for contest_type in session.query(tables.ContestType).all():
|
||||
assert hit(filenames, media.ContestTypeMedia(contest_type).icon)
|
||||
assert hit(filenames, media.ContestTypeMedia(root, contest_type).icon)
|
||||
|
||||
for elemental_type in session.query(tables.Type).all():
|
||||
assert hit(filenames, media.TypeMedia(elemental_type).icon)
|
||||
assert hit(filenames, media.TypeMedia(root, elemental_type).icon)
|
||||
|
||||
# Items
|
||||
versions_for_items = [
|
||||
|
@ -146,7 +168,7 @@ def check_get_everything():
|
|||
]
|
||||
|
||||
for item in session.query(tables.Item).all():
|
||||
accessor = media.ItemMedia(item)
|
||||
accessor = media.ItemMedia(root, item)
|
||||
assert hit(filenames, accessor.berry_image) or not item.berry
|
||||
for rotation in (0, 90, 180, 270):
|
||||
assert hit(filenames, accessor.underground, rotation=rotation) or (
|
||||
|
@ -158,11 +180,11 @@ def check_get_everything():
|
|||
|
||||
for color in 'red green blue pale prism'.split():
|
||||
for big in (True, False):
|
||||
accessor = media.UndergroundSphereMedia(color=color, big=big)
|
||||
accessor = media.UndergroundSphereMedia(root, color=color, big=big)
|
||||
assert hit(filenames, accessor.underground)
|
||||
|
||||
for rock_type in 'i ii o o-big s t z'.split():
|
||||
accessor = media.UndergroundRockMedia(rock_type)
|
||||
accessor = media.UndergroundRockMedia(root, rock_type)
|
||||
for rotation in (0, 90, 180, 270):
|
||||
success = hit(filenames, accessor.underground, rotation=rotation)
|
||||
assert success or rotation
|
||||
|
@ -170,19 +192,17 @@ def check_get_everything():
|
|||
# Pokemon!
|
||||
accessors = []
|
||||
|
||||
accessors.append(media.UnknownPokemonMedia())
|
||||
accessors.append(media.EggMedia())
|
||||
accessors.append(media.UnknownPokemonMedia(root))
|
||||
accessors.append(media.EggMedia(root))
|
||||
manaphy = session.query(tables.Pokemon).filter_by(identifier=u'manaphy').one()
|
||||
accessors.append(media.EggMedia(manaphy))
|
||||
accessors.append(media.SubstituteMedia())
|
||||
|
||||
print 'Loading pokemon'
|
||||
accessors.append(media.EggMedia(root, manaphy))
|
||||
accessors.append(media.SubstituteMedia(root))
|
||||
|
||||
for form in session.query(tables.PokemonForm).filter(tables.PokemonForm.identifier != '').all():
|
||||
accessors.append(media.PokemonFormMedia(form))
|
||||
accessors.append(media.PokemonFormMedia(root, form))
|
||||
|
||||
for pokemon in session.query(tables.Pokemon).all():
|
||||
accessors.append(media.PokemonMedia(pokemon))
|
||||
accessors.append(media.PokemonMedia(root, pokemon))
|
||||
|
||||
for accessor in accessors:
|
||||
assert hit(filenames, accessor.footprint) or not accessor.form
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
|
||||
"""Media accessors
|
||||
|
||||
Most media accessor __init__s take an ORM object from the pokedex package.
|
||||
All media accessor __init__s take a `root` argument, which should be a path
|
||||
to the root of the media directory.
|
||||
Alternatively, `root` can be a custom MediaFile subclass.
|
||||
|
||||
Most __init__s take an ORM object as a second argument.
|
||||
|
||||
Their various methods take a number of arguments specifying exactly which
|
||||
file you want (such as the female sprite, backsprite, etc.).
|
||||
ValueError is raised when the specified file cannot be found.
|
||||
|
@ -26,22 +31,25 @@ All images are in the PNG format, except animations (GIF). All sounds are OGGs.
|
|||
"""
|
||||
|
||||
import os
|
||||
import pkg_resources
|
||||
from functools import partial
|
||||
|
||||
class MediaFile(object):
|
||||
"""Represents a file: picture, sound, etc.
|
||||
|
||||
Attributes:
|
||||
relative_path: Filesystem path relative to the media directory
|
||||
path_elements: List of directory/file names that make up relative_path
|
||||
relative_path: Filesystem path relative to the root
|
||||
path: Absolute path to the file
|
||||
|
||||
exists: True if the file exists
|
||||
|
||||
media_available: false if no media is available at the given root.
|
||||
|
||||
open(): Open the file
|
||||
"""
|
||||
def __init__(self, *path_elements):
|
||||
def __init__(self, root, *path_elements):
|
||||
self.path_elements = path_elements
|
||||
self._dexpath = '/'.join(('data', 'media') + path_elements)
|
||||
self.root = root
|
||||
|
||||
@property
|
||||
def relative_path(self):
|
||||
|
@ -49,7 +57,7 @@ class MediaFile(object):
|
|||
|
||||
@property
|
||||
def path(self):
|
||||
return pkg_resources.resource_filename('pokedex', self._dexpath)
|
||||
return os.path.join(self.root, *self.path_elements)
|
||||
|
||||
def open(self):
|
||||
"""Open this file for reading, in the appropriate mode (i.e. binary)
|
||||
|
@ -58,7 +66,11 @@ class MediaFile(object):
|
|||
|
||||
@property
|
||||
def exists(self):
|
||||
return pkg_resources.resource_exists('pokedex', self._dexpath)
|
||||
return os.path.exists(self.path)
|
||||
|
||||
@property
|
||||
def media_available(self):
|
||||
return os.path.isdir(self.root)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.path == other.path
|
||||
|
@ -70,15 +82,25 @@ class MediaFile(object):
|
|||
return '<Pokedex file %s>' % self.relative_path
|
||||
|
||||
class BaseMedia(object):
|
||||
def __init__(self, root):
|
||||
if isinstance(root, basestring):
|
||||
self.file_class = partial(MediaFile, root)
|
||||
else:
|
||||
self.file_class = root
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
return self.file_class().media_available
|
||||
|
||||
def from_path_elements(self, path_elements, basename, extension,
|
||||
surely_exists=False):
|
||||
filename = basename + extension
|
||||
path_elements = [self.toplevel_dir] + path_elements + [filename]
|
||||
mfile = MediaFile(*path_elements)
|
||||
mfile = self.file_class(*path_elements)
|
||||
if surely_exists or mfile.exists:
|
||||
return mfile
|
||||
else:
|
||||
raise ValueError('File %s not found' % mfile.relative_path)
|
||||
raise ValueError('File %s not found' % mfile.path)
|
||||
|
||||
class _BasePokemonMedia(BaseMedia):
|
||||
toplevel_dir = 'pokemon'
|
||||
|
@ -104,8 +126,8 @@ class _BasePokemonMedia(BaseMedia):
|
|||
'black-white': (5, set('back shiny female'.split())),
|
||||
}
|
||||
|
||||
def __init__(self, pokemon_id, form_postfix=None):
|
||||
BaseMedia.__init__(self)
|
||||
def __init__(self, root, pokemon_id, form_postfix=None):
|
||||
BaseMedia.__init__(self, root)
|
||||
self.pokemon_id = str(pokemon_id)
|
||||
self.form_postfix = form_postfix
|
||||
|
||||
|
@ -316,13 +338,13 @@ class _BasePokemonMedia(BaseMedia):
|
|||
class PokemonFormMedia(_BasePokemonMedia):
|
||||
"""Media related to a Pokemon form
|
||||
"""
|
||||
def __init__(self, pokemon_form):
|
||||
def __init__(self, root, pokemon_form):
|
||||
pokemon_id = pokemon_form.form_base_pokemon_id
|
||||
if pokemon_form.identifier:
|
||||
form_postfix = '-' + pokemon_form.identifier
|
||||
else:
|
||||
form_postfix = None
|
||||
_BasePokemonMedia.__init__(self, pokemon_id, form_postfix)
|
||||
_BasePokemonMedia.__init__(self, root, pokemon_id, form_postfix)
|
||||
self.form = pokemon_form
|
||||
pokemon = pokemon_form.form_base_pokemon
|
||||
self.has_gender_differences = pokemon.has_gender_differences
|
||||
|
@ -331,8 +353,8 @@ class PokemonFormMedia(_BasePokemonMedia):
|
|||
class PokemonMedia(_BasePokemonMedia):
|
||||
"""Media related to a Pokemon
|
||||
"""
|
||||
def __init__(self, pokemon):
|
||||
_BasePokemonMedia.__init__(self, pokemon.id)
|
||||
def __init__(self, root, pokemon):
|
||||
_BasePokemonMedia.__init__(self, root, pokemon.id)
|
||||
self.form = pokemon.default_form
|
||||
self.has_gender_differences = (pokemon.has_gender_differences)
|
||||
self.introduced_in = pokemon.generation_id
|
||||
|
@ -342,8 +364,8 @@ class UnknownPokemonMedia(_BasePokemonMedia):
|
|||
|
||||
Note that not a lot of files are available for it.
|
||||
"""
|
||||
def __init__(self):
|
||||
_BasePokemonMedia.__init__(self, '0')
|
||||
def __init__(self, root):
|
||||
_BasePokemonMedia.__init__(self, root, '0')
|
||||
|
||||
class EggMedia(_BasePokemonMedia):
|
||||
"""Media related to a pokemon egg
|
||||
|
@ -352,20 +374,20 @@ class EggMedia(_BasePokemonMedia):
|
|||
|
||||
Give a Manaphy as `pokemon` to get the Manaphy egg.
|
||||
"""
|
||||
def __init__(self, pokemon=None):
|
||||
def __init__(self, root, pokemon=None):
|
||||
if pokemon and pokemon.identifier == 'manaphy':
|
||||
postfix = '-manaphy'
|
||||
else:
|
||||
postfix = None
|
||||
_BasePokemonMedia.__init__(self, 'egg', postfix)
|
||||
_BasePokemonMedia.__init__(self, root, 'egg', postfix)
|
||||
|
||||
class SubstituteMedia(_BasePokemonMedia):
|
||||
"""Media related to the Substitute sprite
|
||||
|
||||
Note that not a lot of files are available for Substitute.
|
||||
"""
|
||||
def __init__(self):
|
||||
_BasePokemonMedia.__init__(self, 'substitute')
|
||||
def __init__(self, root):
|
||||
_BasePokemonMedia.__init__(self, root, 'substitute')
|
||||
|
||||
class _BaseItemMedia(BaseMedia):
|
||||
toplevel_dir = 'items'
|
||||
|
@ -383,7 +405,8 @@ class _BaseItemMedia(BaseMedia):
|
|||
class ItemMedia(_BaseItemMedia):
|
||||
"""Media related to an item
|
||||
"""
|
||||
def __init__(self, item):
|
||||
def __init__(self, root, item):
|
||||
_BaseItemMedia.__init__(self, root)
|
||||
self.item = item
|
||||
self.identifier = item.identifier
|
||||
|
||||
|
@ -459,7 +482,8 @@ class UndergroundRockMedia(_BaseItemMedia):
|
|||
|
||||
rock_type can be one of: i, ii, o, o-big, s, t, z
|
||||
"""
|
||||
def __init__(self, rock_type):
|
||||
def __init__(self, root, rock_type):
|
||||
_BaseItemMedia.__init__(self, root)
|
||||
self.identifier = 'rock-%s' % rock_type
|
||||
|
||||
class UndergroundSphereMedia(_BaseItemMedia):
|
||||
|
@ -467,13 +491,15 @@ class UndergroundSphereMedia(_BaseItemMedia):
|
|||
|
||||
color can be one of: red, blue, green, pale, prism
|
||||
"""
|
||||
def __init__(self, color, big=False):
|
||||
def __init__(self, root, color, big=False):
|
||||
_BaseItemMedia.__init__(self, root)
|
||||
self.identifier = '%s-sphere' % color
|
||||
if big:
|
||||
self.identifier += '-big'
|
||||
|
||||
class _SimpleIconMedia(BaseMedia):
|
||||
def __init__(self, thing):
|
||||
def __init__(self, root, thing):
|
||||
BaseMedia.__init__(self, root)
|
||||
self.identifier = thing.identifier
|
||||
|
||||
def icon(self):
|
||||
|
|
Loading…
Reference in a new issue