Fix loaders using the actual enum instances as chart difficulties instead of their associated values
This commit is contained in:
parent
f76bc2aa88
commit
26b3d46975
@ -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 !
|
||||||
|
@ -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)}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -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})
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user