1
0
mirror of synced 2025-03-03 16:43:52 +01:00

Fix loaders using the actual enum instances as chart difficulties instead of their associated values

This commit is contained in:
Stepland 2022-04-16 01:23:52 +02:00
parent f76bc2aa88
commit 26b3d46975
11 changed files with 77 additions and 22 deletions

View File

@ -2,6 +2,9 @@
## Changed ## Changed
- Minimum required Python version is now 3.9 - Minimum required Python version is now 3.9
## Fixed ## Fixed
- Most loaders would incorrectly use the internal enum value name as difficulty
names for charts (like `Difficulty.EXTREME`) instead of the regular "display"
name (like `EXT`), not anymore !
- [eve] + [jbsq] - [eve] + [jbsq]
- Custom hakus were not taken into account when computing the time of the last - Custom hakus were not taken into account when computing the time of the last
event, not anymore ! event, not anymore !

View File

@ -27,9 +27,9 @@ from .symbols import (
) )
DIFFICULTIES = { DIFFICULTIES = {
1: Difficulty.BASIC, 1: Difficulty.BASIC.value,
2: Difficulty.ADVANCED, 2: Difficulty.ADVANCED.value,
3: Difficulty.EXTREME, 3: Difficulty.EXTREME.value,
} }
SYMBOL_TO_BEATS_TIME = {c: BeatsTime("1/4") * i for i, c in enumerate(NOTE_SYMBOLS)} SYMBOL_TO_BEATS_TIME = {c: BeatsTime("1/4") * i for i, c in enumerate(NOTE_SYMBOLS)}

View File

@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from typing import Any, Iterator, List, Optional from typing import Any, Iterator, List
from jubeatools import song from jubeatools import song
from jubeatools.formats.load_tools import make_folder_loader from jubeatools.formats.load_tools import make_folder_loader
@ -23,8 +23,8 @@ load_folder = make_folder_loader("*.eve", load_file)
def _load_eve(lines: List[str], file_path: Path, *, beat_snap: int = 240) -> song.Song: def _load_eve(lines: List[str], file_path: Path, *, beat_snap: int = 240) -> song.Song:
chart = make_chart_from_events(iter_events(lines), beat_snap=beat_snap) chart = make_chart_from_events(iter_events(lines), beat_snap=beat_snap)
dif = guess_difficulty(file_path.stem) or song.Difficulty.EXTREME dif = guess_difficulty(file_path.stem)
return song.Song(metadata=song.Metadata(), charts={dif: chart}) return song.Song(metadata=song.Metadata(), charts={dif.value: chart})
def iter_events(lines: List[str]) -> Iterator[Event]: def iter_events(lines: List[str]) -> Iterator[Event]:
@ -72,8 +72,8 @@ def parse_event(line: str) -> Event:
return Event(tick, command, value) return Event(tick, command, value)
def guess_difficulty(filename: str) -> Optional[song.Difficulty]: def guess_difficulty(filename: str) -> song.Difficulty:
try: try:
return song.Difficulty(filename.upper()) return song.Difficulty(filename.upper())
except ValueError: except ValueError:
return None return song.Difficulty.EXTREME

View File

@ -1,9 +1,11 @@
from enum import Enum
from hypothesis import given from hypothesis import given
from jubeatools import song from jubeatools import song
from jubeatools.formats import Format from jubeatools.formats import Format
from jubeatools.formats.konami.testutils import eve_compatible_song from jubeatools.formats.konami.testutils import eve_compatible_song
from jubeatools.testutils.test_patterns import dump_and_load_then_compare from jubeatools.testutils.test_patterns import dump_and_load, dump_and_load_then_compare
@given(eve_compatible_song()) @given(eve_compatible_song())
@ -14,3 +16,19 @@ def test_that_full_chart_roundtrips(song: song.Song) -> None:
bytes_decoder=lambda b: b.decode("ascii"), bytes_decoder=lambda b: b.decode("ascii"),
load_options={"beat_snap": 12}, load_options={"beat_snap": 12},
) )
@given(eve_compatible_song())
def test_that_difficulty_name_is_loaded_properly(original_song: song.Song) -> None:
recovered_song = dump_and_load(
Format.EVE,
original_song,
bytes_decoder=lambda b: b.decode("ascii"),
load_options={"beat_snap": 12},
)
original_dif, _ = original_song.charts.popitem()
recovered_dif, _ = recovered_song.charts.popitem()
assert type(original_dif) == str
assert not isinstance(original_dif, Enum)
assert type(recovered_dif) == str
assert not isinstance(recovered_dif, Enum)

View File

@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from typing import Any, Optional from typing import Any
from jubeatools import song from jubeatools import song
from jubeatools.formats.load_tools import make_folder_loader from jubeatools.formats.load_tools import make_folder_loader
@ -31,8 +31,8 @@ def load_jbsq_file(
raw_data = construct.jbsq.parse(bytes_) raw_data = construct.jbsq.parse(bytes_)
events = [make_event_from_construct(e) for e in raw_data.events] events = [make_event_from_construct(e) for e in raw_data.events]
chart = make_chart_from_events(events, beat_snap=beat_snap) chart = make_chart_from_events(events, beat_snap=beat_snap)
dif = guess_difficulty(file_path.stem) or song.Difficulty.EXTREME dif = guess_difficulty(file_path.stem)
return song.Song(metadata=song.Metadata(), charts={dif: chart}) return song.Song(metadata=song.Metadata(), charts={dif.value: chart})
def make_event_from_construct(e: construct.Event) -> konami.Event: def make_event_from_construct(e: construct.Event) -> konami.Event:
@ -43,8 +43,8 @@ def make_event_from_construct(e: construct.Event) -> konami.Event:
) )
def guess_difficulty(filename: str) -> Optional[song.Difficulty]: def guess_difficulty(filename: str) -> song.Difficulty:
try: try:
return song.Difficulty(filename[-3:].upper()) return song.Difficulty(filename[-3:].upper())
except ValueError: except ValueError:
return None return song.Difficulty.EXTREME

View File

@ -1,9 +1,11 @@
from enum import Enum
from hypothesis import given from hypothesis import given
from jubeatools import song from jubeatools import song
from jubeatools.formats import Format from jubeatools.formats import Format
from jubeatools.formats.konami.testutils import eve_compatible_song from jubeatools.formats.konami.testutils import eve_compatible_song
from jubeatools.testutils.test_patterns import dump_and_load_then_compare from jubeatools.testutils.test_patterns import dump_and_load, dump_and_load_then_compare
from .construct import jbsq from .construct import jbsq
@ -16,3 +18,19 @@ def test_that_full_chart_roundtrips(song: song.Song) -> None:
bytes_decoder=lambda b: str(jbsq.parse(b)), bytes_decoder=lambda b: str(jbsq.parse(b)),
load_options={"beat_snap": 12}, load_options={"beat_snap": 12},
) )
@given(eve_compatible_song())
def test_that_difficulty_name_is_loaded_properly(original_song: song.Song) -> None:
recovered_song = dump_and_load(
Format.JBSQ,
original_song,
bytes_decoder=lambda b: str(jbsq.parse(b)),
load_options={"beat_snap": 12},
)
original_dif, _ = original_song.charts.popitem()
recovered_dif, _ = recovered_song.charts.popitem()
assert type(original_dif) == str
assert not isinstance(original_dif, Enum)
assert type(recovered_dif) == str
assert not isinstance(recovered_dif, Enum)

View File

@ -14,7 +14,7 @@ simple_beat_strat = jbst.beat_time(
def eve_compatible_song(draw: st.DrawFn) -> song.Song: def eve_compatible_song(draw: st.DrawFn) -> song.Song:
"""eve only keeps notes, hakus, timing info and difficulty, """eve only keeps notes, hakus, timing info and difficulty,
the precision you can get out of it is also severly limited""" the precision you can get out of it is also severly limited"""
diff = draw(st.sampled_from(list(song.Difficulty))) diff = draw(st.sampled_from(list(d.value for d in song.Difficulty)))
chart = draw( chart = draw(
jbst.chart( jbst.chart(
timing_strat=jbst.timing_info( timing_strat=jbst.timing_info(

View File

@ -39,7 +39,7 @@ def load_malody_file(raw_dict: dict) -> song.Song:
time_map = load_timing_info(file.time, bgm) time_map = load_timing_info(file.time, bgm)
timing = time_map.convert_to_timing_info() timing = time_map.convert_to_timing_info()
chart = song.Chart(level=Decimal(0), timing=timing, notes=load_notes(file.note)) chart = song.Chart(level=Decimal(0), timing=timing, notes=load_notes(file.note))
dif = file.meta.version or song.Difficulty.EXTREME dif = file.meta.version or song.Difficulty.EXTREME.value
return song.Song(metadata=metadata, charts={dif: chart}) return song.Song(metadata=metadata, charts={dif: chart})

View File

@ -16,8 +16,8 @@ from jubeatools.testutils.test_patterns import dump_and_load_then_compare
@st.composite @st.composite
def difficulty(draw: st.DrawFn) -> str: def difficulty(draw: st.DrawFn) -> str:
d: song.Difficulty = draw(st.sampled_from(list(song.Difficulty))) diff: str = draw(st.sampled_from(list(d.value for d in song.Difficulty)))
return d.value return diff
@st.composite @st.composite

View File

@ -21,7 +21,6 @@ from typing import (
Iterable, Iterable,
Iterator, Iterator,
List, List,
Mapping,
Optional, Optional,
Sequence, Sequence,
Set, Set,
@ -271,6 +270,9 @@ class Difficulty(str, Enum):
ADVANCED = "ADV" ADVANCED = "ADV"
EXTREME = "EXT" EXTREME = "EXT"
def __str__(self) -> str:
return self.value
@dataclass @dataclass
class Song: class Song:
@ -278,7 +280,7 @@ class Song:
A Song is a set of charts with associated metadata""" A Song is a set of charts with associated metadata"""
metadata: Metadata metadata: Metadata
charts: Mapping[str, Chart] = field(default_factory=dict) charts: Dict[str, Chart] = field(default_factory=dict)
common_timing: Optional[Timing] = None common_timing: Optional[Timing] = None
common_hakus: Optional[Set[BeatsTime]] = None common_hakus: Optional[Set[BeatsTime]] = None

View File

@ -24,6 +24,20 @@ def dump_and_load_then_compare(
load_options: Optional[dict] = None, load_options: Optional[dict] = None,
dump_options: Optional[dict] = None, dump_options: Optional[dict] = None,
) -> None: ) -> None:
recovered_song = dump_and_load(
format_, song, bytes_decoder, temp_path, load_options, dump_options
)
assert recovered_song == song
def dump_and_load(
format_: Format,
song: song.Song,
bytes_decoder: Callable[[bytes], str] = lambda b: b.decode("utf-8"),
temp_path: Callable[[], ContextManager[Path]] = open_temp_dir,
load_options: Optional[dict] = None,
dump_options: Optional[dict] = None,
) -> song.Song:
load_options = load_options or {} load_options = load_options or {}
dump_options = dump_options or {} dump_options = dump_options or {}
loader = LOADERS[format_] loader = LOADERS[format_]
@ -39,4 +53,4 @@ def dump_and_load_then_compare(
recovered_song = loader(folder_path, **load_options) recovered_song = loader(folder_path, **load_options)
recovered_song.minimize_timings() recovered_song.minimize_timings()
recovered_song.minimize_hakus() recovered_song.minimize_hakus()
assert recovered_song == song return recovered_song