1
0
mirror of synced 2024-12-05 03:27:54 +01:00

Improve error messages

This commit is contained in:
Stepland 2021-05-02 16:37:01 +02:00
parent b656fe55c2
commit 769c82e33b
6 changed files with 71 additions and 42 deletions

View File

@ -37,7 +37,10 @@ def guess_memon_version(obj: Any) -> Format:
except KeyError: except KeyError:
return Format.MEMON_LEGACY return Format.MEMON_LEGACY
except TypeError: except TypeError:
raise ValueError("Invalid JSON structure for memon file") raise ValueError(
"This JSON file is not a correct memon file : the top-level "
"value is not an object"
)
if version == "0.1.0": if version == "0.1.0":
return Format.MEMON_0_1_0 return Format.MEMON_0_1_0

View File

@ -99,7 +99,9 @@ def parse_command(line: str) -> Tuple[str, Optional[str]]:
return CommandVisitor().visit(command_grammar.parse(line)) # type: ignore return CommandVisitor().visit(command_grammar.parse(line)) # type: ignore
except ParseError: except ParseError:
if line.strip()[0] == "#": if line.strip()[0] == "#":
raise ParseError(f"Invalid command syntax : {line}") from None raise ParseError(
"Line starts with '#' but it couldn't be parsed as a valid command"
) from None
else: else:
raise raise

View File

@ -93,6 +93,32 @@ class DoubleColumnChartLine:
def __str__(self) -> str: def __str__(self) -> str:
return f"{self.position} |{self.timing}|" return f"{self.position} |{self.timing}|"
def raise_if_unfit(self, bytes_per_panel: int) -> None:
self.raise_if_position_unfit(bytes_per_panel)
self.raise_if_timing_unfit(bytes_per_panel)
def raise_if_position_unfit(self, bytes_per_panel: int) -> None:
expected_length = 4 * bytes_per_panel
actual_length = len(self.position.encode("shift-jis-2004"))
if expected_length != actual_length:
raise SyntaxError(
f"Invalid position part. Since #bpp={bytes_per_panel}, the \
position part of a line should be {expected_length} bytes long, \
but {self.position!r} is {actual_length} bytes long"
)
def raise_if_timing_unfit(self, bytes_per_panel: int) -> None:
if self.timing is None:
return
length = len(self.timing.encode("shift-jis-2004"))
if length % bytes_per_panel != 0:
raise SyntaxError(
f"Invalid timing part. Since #bpp={bytes_per_panel}, the timing \
part of a line should be divisible by {bytes_per_panel}, but \
{self.timing!r} is {length} bytes long so it's not"
)
class DoubleColumnChartLineVisitor(NodeVisitor): class DoubleColumnChartLineVisitor(NodeVisitor):
def __init__(self) -> None: def __init__(self) -> None:
@ -145,7 +171,10 @@ def split_double_byte_line(line: str) -> List[str]:
""" """
encoded_line = line.encode("shift-jis-2004") encoded_line = line.encode("shift-jis-2004")
if len(encoded_line) % 2 != 0: if len(encoded_line) % 2 != 0:
raise ValueError(f"Invalid chart line : {line}") raise ValueError(
"Line of odd length encountered while trying to split a double-byte "
f"line : {line!r}"
)
symbols = [] symbols = []
for i in range(0, len(encoded_line), 2): for i in range(0, len(encoded_line), 2):
symbols.append(encoded_line[i : i + 2].decode("shift-jis-2004")) symbols.append(encoded_line[i : i + 2].decode("shift-jis-2004"))
@ -166,7 +195,8 @@ class UnfinishedLongNote:
def ends_at(self, end: BeatsTime) -> LongNote: def ends_at(self, end: BeatsTime) -> LongNote:
if end < self.time: if end < self.time:
raise ValueError( raise ValueError(
f"Invalid end time ({end}) for long note starting at {self.time}" "Invalid end time. A long note starting at "
f"{self.time} cannot end at {end} (which is earlier)"
) )
return LongNote( return LongNote(
time=self.time, time=self.time,
@ -228,7 +258,7 @@ def pick_correct_long_note_candidates(
solutions: List[Solution] = problem.getSolutions() solutions: List[Solution] = problem.getSolutions()
if not solutions: if not solutions:
raise SyntaxError( raise SyntaxError(
"Invalid long note arrow pattern in bloc :\n" "Impossible arrow pattern found in block :\n"
+ "\n".join("".join(line) for line in bloc) + "\n".join("".join(line) for line in bloc)
) )
solution = min(solutions, key=long_note_solution_heuristic) solution = min(solutions, key=long_note_solution_heuristic)
@ -283,7 +313,7 @@ class JubeatAnalyserParser:
try: try:
method = getattr(self, f"do_{command}") method = getattr(self, f"do_{command}")
except AttributeError: except AttributeError:
raise SyntaxError(f"Unknown analyser command : {command}") from None raise SyntaxError(f"Unknown jubeat analyser command : {command}") from None
if value is not None: if value is not None:
method(value) method(value)
@ -341,7 +371,7 @@ class JubeatAnalyserParser:
if bpp not in (1, 2): if bpp not in (1, 2):
raise ValueError(f"Unexcpected bpp value : {value}") raise ValueError(f"Unexcpected bpp value : {value}")
elif self.circle_free and bpp == 1: elif self.circle_free and bpp == 1:
raise ValueError("#bpp can only be 2 when #circlefree is activated") raise ValueError("Can't set #bpp to 1 when #circlefree is on")
else: else:
self.bytes_per_panel = int(value) self.bytes_per_panel = int(value)
@ -349,18 +379,17 @@ class JubeatAnalyserParser:
self.hold_by_arrow = int(value) == 1 self.hold_by_arrow = int(value) == 1
def do_holdbytilde(self, value: str) -> None: def do_holdbytilde(self, value: str) -> None:
if int(value): raise NotImplementedError("jubeatools does not support #holdbytilde")
raise ValueError("jubeatools does not support #holdbytilde")
def do_circlefree(self, raw_value: str) -> None: def do_circlefree(self, raw_value: str) -> None:
activate = bool(int(raw_value)) activate = bool(int(raw_value))
if activate and self.bytes_per_panel != 2: if activate and self.bytes_per_panel != 2:
raise ValueError("#circlefree can only be activated when #bpp=2") raise ValueError("#circlefree can only be on when #bpp=2")
self.circle_free = activate self.circle_free = activate
def _wrong_format(self, f: str) -> None: def _wrong_format(self, f: str) -> None:
raise ValueError( raise ValueError(
f"{f} command indicates this file uses another jubeat analyser " f"{f} command means that this file uses another jubeat analyser "
"format than the one the currently selected parser is designed for" "format than the one the currently selected parser is designed for"
) )
@ -381,15 +410,15 @@ class JubeatAnalyserParser:
length_as_shift_jis = len(symbol.encode("shift-jis-2004")) length_as_shift_jis = len(symbol.encode("shift-jis-2004"))
if length_as_shift_jis != bpp: if length_as_shift_jis != bpp:
raise ValueError( raise ValueError(
f"Invalid symbol definition. Since #bpp={bpp}, timing symbols " f"Invalid symbol definition. Since #bpp={bpp}, timing symbols \
f"should be {bpp} bytes long but '{symbol}' is {length_as_shift_jis}" should be {bpp} bytes long but '{symbol}' is {length_as_shift_jis}"
) )
if timing > self.beats_per_section: if timing > self.beats_per_section:
message = ( raise ValueError(
"Invalid symbol definition conscidering the number of beats per section :\n" f"Invalid symbol definition. Since sections only last \
f"*{symbol}:{timing}" {self.beats_per_section} beats, a symbol cannot happen \
afterwards at {timing}"
) )
raise ValueError(message)
self.symbols[symbol] = timing self.symbols[symbol] = timing
def is_short_line(self, line: str) -> bool: def is_short_line(self, line: str) -> bool:

View File

@ -55,19 +55,28 @@ class Frame:
bars: Dict[int, Dict[int, str]] = field(default_factory=dict) bars: Dict[int, Dict[int, str]] = field(default_factory=dict)
def dump(self, length: Decimal) -> Iterator[str]: def dump(self, length: Decimal) -> Iterator[str]:
# Check that bars are contiguous self.raise_if_unfit()
for a, b in windowed(sorted(self.bars), 2):
if b is not None and a is not None and b - a != 1:
raise ValueError("Frame has discontinuous bars")
# Check all bars are in the same 4-bar group
if self.bars.keys() != set(bar % 4 for bar in self.bars):
raise ValueError("Frame contains bars from different 4-bar groups")
for pos, bar in zip_longest(self.dump_positions(), self.dump_bars(length)): for pos, bar in zip_longest(self.dump_positions(), self.dump_bars(length)):
if bar is None: if bar is None:
bar = "" bar = ""
yield f"{pos} {bar}" yield f"{pos} {bar}"
def raise_if_unfit(self) -> None:
if not self.bars_are_contiguous():
raise ValueError("Frame has discontinuous bars")
if not self.all_bars_are_from_the_same_group():
raise ValueError("Frame contains bars from different 4-bar groups")
def bars_are_contiguous(self) -> bool:
return all(
b - a == 1
for a, b in windowed(sorted(self.bars), 2)
if b is not None and a is not None
)
def all_bars_are_from_the_same_group(self) -> bool:
return self.bars.keys() == set(bar % 4 for bar in self.bars)
def dump_positions(self) -> Iterator[str]: def dump_positions(self) -> Iterator[str]:
for y in range(4): for y in range(4):
yield "".join( yield "".join(
@ -121,9 +130,7 @@ class MemoDumpedSection(JubeatAnalyserDumpedSection):
chosen_symbols[time_in_section] = symbol chosen_symbols[time_in_section] = symbol
bars[bar_index][time_index] = symbol bars[bar_index][time_index] = symbol
elif time_in_section not in self.symbols: elif time_in_section not in self.symbols:
raise ValueError( raise ValueError(f"No symbol defined for time : {time_in_section}")
f"No symbol defined for time in section : {time_in_section}"
)
# Create frame by bar # Create frame by bar
section_symbols = ChainMap(chosen_symbols, self.symbols) section_symbols = ChainMap(chosen_symbols, self.symbols)

View File

@ -98,13 +98,7 @@ class MemoParser(JubeatAnalyserParser):
self._do_bpp(value) self._do_bpp(value)
def append_chart_line(self, line: DoubleColumnChartLine) -> None: def append_chart_line(self, line: DoubleColumnChartLine) -> None:
if len(line.position.encode("shift-jis-2004")) != 4 * self.bytes_per_panel: line.raise_if_unfit(self.bytes_per_panel)
raise SyntaxError(
f"Invalid chart line for #bpp={self.bytes_per_panel} : {line}"
)
if line.timing is not None and self.bytes_per_panel == 2:
if len(line.timing.encode("shift-jis-2004")) % 2 != 0:
raise SyntaxError(f"Invalid chart line for #bpp=2 : {line}")
self.current_chart_lines.append(line) self.current_chart_lines.append(line)
if len(self.current_chart_lines) == 4: if len(self.current_chart_lines) == 4:
self._push_frame() self._push_frame()

View File

@ -92,13 +92,7 @@ class Memo1Parser(JubeatAnalyserParser):
self._do_bpp(value) self._do_bpp(value)
def append_chart_line(self, line: DoubleColumnChartLine) -> None: def append_chart_line(self, line: DoubleColumnChartLine) -> None:
if len(line.position.encode("shift-jis-2004")) != 4 * self.bytes_per_panel: line.raise_if_unfit(self.bytes_per_panel)
raise SyntaxError(
f"Invalid chart line for #bpp={self.bytes_per_panel} : {line}"
)
if line.timing is not None and self.bytes_per_panel == 2:
if len(line.timing.encode("shift-jis-2004")) % 2 != 0:
raise SyntaxError(f"Invalid chart line for #bpp=2 : {line}")
self.current_chart_lines.append(line) self.current_chart_lines.append(line)
if len(self.current_chart_lines) == 4: if len(self.current_chart_lines) == 4:
self._push_frame() self._push_frame()