diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..8e6fd7c --- /dev/null +++ b/.flake8 @@ -0,0 +1,32 @@ +[flake8] +ignore = + # break after binary op. + # black does its thing and I leave it that way + W503 + # f-string is missing placeholders + # really don't want to be bothered for so little + F541 + # whitespace before ':' + # let black handle that + E203 + # blank line contains whitespace + # let black handle that as well + W293 + # do not assign a lambda expression, use a def + # I know when I need that one, thank you very much + E731 +exclude = + .git + __pycache__ + dist + build +per-file-ignores = + # Allow re-exporting in __init__.py files + __init__.py: F401 + # Allow star imports in test example files + example*.py: F405, F403 + # Silence weird false positive on inline comments ... + jubeatools/formats/jubeat_analyser/symbols.py: E262 + # there's a field named "l" in a marshmallow schema + jubeatools/formats/memon/memon.py: E741 +max-line-length = 120 \ No newline at end of file diff --git a/docs/repo maintenance.md b/docs/repo maintenance.md index 7df64bf..0bd6c29 100644 --- a/docs/repo maintenance.md +++ b/docs/repo maintenance.md @@ -9,9 +9,11 @@ ## Making a new release Sanity checks before anything serious happens, from the repo's root : -1. Run mypy and fix **all** the errors
`$ poetry run mypy .` 1. Format the code
`$ poetry run sh ./utils/format_code.sh` +1. Run checks
`$ poetry run sh ./utils/check_code.sh` 1. Make sure the unit tests pass
`$ poetry run pytest` +1. Fix all encountered errors +1. Rince and repeat until everythings is ok Now that this is done you can move on to actually making a new version, while still being in the repo's root : diff --git a/jubeatools/cli.py b/jubeatools/cli.py index 1127cad..4a8d833 100644 --- a/jubeatools/cli.py +++ b/jubeatools/cli.py @@ -1,11 +1,12 @@ """Command Line Interface""" + from pathlib import Path -from typing import Any, Dict, Optional +from typing import Any, Dict import click from jubeatools.formats import DUMPERS, LOADERS -from jubeatools.formats.enum import JUBEAT_ANALYSER_FORMATS, Format +from jubeatools.formats.enum import Format from jubeatools.formats.guess import guess_format diff --git a/jubeatools/formats/__init__.py b/jubeatools/formats/__init__.py index b21ca82..37d5997 100644 --- a/jubeatools/formats/__init__.py +++ b/jubeatools/formats/__init__.py @@ -2,10 +2,7 @@ Module containing all the load/dump code for all file formats """ -from pathlib import Path -from typing import IO, Any, Callable, Dict - -from jubeatools.song import Song +from typing import Dict from .enum import Format from .jubeat_analyser import ( diff --git a/jubeatools/formats/jubeat_analyser/__init__.py b/jubeatools/formats/jubeat_analyser/__init__.py index 6636ffe..9226fe1 100644 --- a/jubeatools/formats/jubeat_analyser/__init__.py +++ b/jubeatools/formats/jubeat_analyser/__init__.py @@ -16,11 +16,7 @@ The machine-readable variants or these text formats are partially documented - http://yosh52.web.fc2.com/jubeat/holdmarker.html """ -from .memo1.dump import dump_memo1 -from .memo1.load import load_memo1 -from .memo2.dump import dump_memo2 -from .memo2.load import load_memo2 -from .memo.dump import dump_memo -from .memo.load import load_memo -from .mono_column.dump import dump_mono_column -from .mono_column.load import load_mono_column +from .memo import dump_memo, load_memo +from .memo1 import dump_memo1, load_memo1 +from .memo2 import dump_memo2, load_memo2 +from .mono_column import dump_mono_column, load_mono_column diff --git a/jubeatools/formats/jubeat_analyser/command.py b/jubeatools/formats/jubeat_analyser/command.py index 3f99e4e..24ecec7 100644 --- a/jubeatools/formats/jubeat_analyser/command.py +++ b/jubeatools/formats/jubeat_analyser/command.py @@ -24,9 +24,8 @@ Known hash commands : - #bpp # bytes per panel (2 by default) """ -from decimal import Decimal from numbers import Number -from typing import Any, Iterable, List, Optional, Tuple, Union +from typing import Any, List, Optional, Tuple from parsimonious import Grammar, NodeVisitor, ParseError from parsimonious.nodes import Node @@ -148,5 +147,5 @@ ESCAPE_TABLE = str.maketrans({'"': BACKSLASH + '"', BACKSLASH: BACKSLASH + BACKS def dump_value(value: str) -> str: - """backslash-escapes \ and " from a string""" + """Escapes backslashes and " from a string""" return value.translate(ESCAPE_TABLE) diff --git a/jubeatools/formats/jubeat_analyser/dump_tools.py b/jubeatools/formats/jubeat_analyser/dump_tools.py index 7a5a00c..cddca67 100644 --- a/jubeatools/formats/jubeat_analyser/dump_tools.py +++ b/jubeatools/formats/jubeat_analyser/dump_tools.py @@ -1,6 +1,6 @@ """Collection of tools realted to dumping to jubeat analyser formats""" + from abc import ABC, abstractmethod -from copy import deepcopy from dataclasses import dataclass, field from decimal import Decimal from fractions import Fraction @@ -19,7 +19,7 @@ from typing import ( Union, ) -from more_itertools import collapse, intersperse, mark_ends, windowed +from more_itertools import windowed from sortedcontainers import SortedDict, SortedKeyList from jubeatools.formats.filetypes import ChartFile diff --git a/jubeatools/formats/jubeat_analyser/load_tools.py b/jubeatools/formats/jubeat_analyser/load_tools.py index ba1acae..fe468ce 100644 --- a/jubeatools/formats/jubeat_analyser/load_tools.py +++ b/jubeatools/formats/jubeat_analyser/load_tools.py @@ -1,4 +1,5 @@ """Collection of parsing tools that are common to all the jubeat analyser formats""" + import re import warnings from collections import Counter @@ -6,17 +7,7 @@ from copy import deepcopy from dataclasses import astuple, dataclass from decimal import Decimal from itertools import product, zip_longest -from typing import ( - AbstractSet, - Dict, - Iterator, - List, - Optional, - Sequence, - Set, - Tuple, - Union, -) +from typing import AbstractSet, Dict, List, Optional, Set, Tuple, Union import constraint from parsimonious import Grammar, NodeVisitor, ParseError diff --git a/jubeatools/formats/jubeat_analyser/memo/dump.py b/jubeatools/formats/jubeat_analyser/memo/dump.py index ae5167a..2aef9b7 100644 --- a/jubeatools/formats/jubeat_analyser/memo/dump.py +++ b/jubeatools/formats/jubeat_analyser/memo/dump.py @@ -1,19 +1,14 @@ from collections import ChainMap, defaultdict -from copy import deepcopy from dataclasses import dataclass, field -from decimal import Decimal from fractions import Fraction -from functools import partial from io import StringIO -from itertools import chain, zip_longest +from itertools import zip_longest from math import ceil -from pathlib import Path -from typing import Callable, Dict, Iterator, List, Optional, Set, Tuple, Union, cast +from typing import Dict, Iterator, List, Union from more_itertools import chunked, collapse, intersperse, mark_ends, windowed -from sortedcontainers import SortedKeyList -from jubeatools.formats.filetypes import ChartFile, JubeatFile +from jubeatools.formats.filetypes import ChartFile from jubeatools.song import ( BeatsTime, Chart, @@ -26,22 +21,17 @@ from jubeatools.song import ( ) from jubeatools.version import __version__ -from ..command import dump_command from ..dump_tools import ( - BEATS_TIME_TO_SYMBOL, - COMMAND_ORDER, DEFAULT_EXTRA_SYMBOLS, DIRECTION_TO_ARROW, DIRECTION_TO_LINE, NOTE_TO_CIRCLE_FREE_SYMBOL, JubeatAnalyserDumpedSection, LongNoteEnd, - SortedDefaultDict, create_sections_from_chart, - fraction_to_decimal, jubeat_analyser_file_dumper, ) -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import NOTE_SYMBOLS AnyNote = Union[TapNote, LongNote, LongNoteEnd] diff --git a/jubeatools/formats/jubeat_analyser/memo/load.py b/jubeatools/formats/jubeat_analyser/memo/load.py index 46ac1bb..f4ebfd1 100644 --- a/jubeatools/formats/jubeat_analyser/memo/load.py +++ b/jubeatools/formats/jubeat_analyser/memo/load.py @@ -1,16 +1,13 @@ -import warnings from collections import ChainMap from copy import deepcopy from dataclasses import astuple, dataclass from decimal import Decimal from functools import reduce -from itertools import chain, product, zip_longest +from itertools import product from pathlib import Path -from typing import Dict, Iterator, List, Mapping, Optional, Set, Tuple, Union +from typing import Dict, Iterator, List, Mapping, Set, Tuple, Union -import constraint from more_itertools import collapse, mark_ends -from parsimonious import Grammar, NodeVisitor, ParseError from jubeatools.song import ( BeatsTime, @@ -31,24 +28,18 @@ from ..files import load_files from ..load_tools import ( CIRCLE_FREE_TO_NOTE_SYMBOL, EMPTY_BEAT_SYMBOLS, - LONG_ARROWS, - LONG_DIRECTION, DoubleColumnChartLine, DoubleColumnFrame, JubeatAnalyserParser, UnfinishedLongNote, - decimal_to_beats, find_long_note_candidates, is_double_column_chart_line, is_empty_line, - is_simple_solution, - long_note_solution_heuristic, parse_double_column_chart_line, pick_correct_long_note_candidates, - split_double_byte_line, ) from ..symbol_definition import is_symbol_definition, parse_symbol_definition -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import CIRCLE_FREE_SYMBOLS class MemoFrame(DoubleColumnFrame): diff --git a/jubeatools/formats/jubeat_analyser/memo1/dump.py b/jubeatools/formats/jubeat_analyser/memo1/dump.py index cbee23f..da96201 100644 --- a/jubeatools/formats/jubeat_analyser/memo1/dump.py +++ b/jubeatools/formats/jubeat_analyser/memo1/dump.py @@ -1,19 +1,14 @@ -from collections import ChainMap, defaultdict -from copy import deepcopy +from collections import defaultdict from dataclasses import dataclass, field -from decimal import Decimal from fractions import Fraction -from functools import partial from io import StringIO -from itertools import chain, zip_longest +from itertools import zip_longest from math import ceil -from pathlib import Path -from typing import Dict, Iterator, List, Optional, Set, Tuple, Union +from typing import Dict, Iterator, List, Union -from more_itertools import chunked, collapse, intersperse, mark_ends, windowed -from sortedcontainers import SortedKeyList +from more_itertools import collapse, intersperse, mark_ends, windowed -from jubeatools.formats.filetypes import ChartFile, JubeatFile +from jubeatools.formats.filetypes import ChartFile from jubeatools.song import ( BeatsTime, Chart, @@ -27,22 +22,16 @@ from jubeatools.song import ( from jubeatools.utils import lcm from jubeatools.version import __version__ -from ..command import dump_command from ..dump_tools import ( - BEATS_TIME_TO_SYMBOL, - COMMAND_ORDER, - DEFAULT_EXTRA_SYMBOLS, DIRECTION_TO_ARROW, DIRECTION_TO_LINE, NOTE_TO_CIRCLE_FREE_SYMBOL, JubeatAnalyserDumpedSection, LongNoteEnd, - SortedDefaultDict, create_sections_from_chart, - fraction_to_decimal, jubeat_analyser_file_dumper, ) -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import NOTE_SYMBOLS AnyNote = Union[TapNote, LongNote, LongNoteEnd] diff --git a/jubeatools/formats/jubeat_analyser/memo1/load.py b/jubeatools/formats/jubeat_analyser/memo1/load.py index 4479d05..7f1d511 100644 --- a/jubeatools/formats/jubeat_analyser/memo1/load.py +++ b/jubeatools/formats/jubeat_analyser/memo1/load.py @@ -1,16 +1,12 @@ -import warnings -from collections import ChainMap from copy import deepcopy from dataclasses import astuple, dataclass from decimal import Decimal from functools import reduce -from itertools import chain, product, zip_longest +from itertools import product from pathlib import Path -from typing import Dict, Iterator, List, Mapping, Optional, Set, Tuple, Union +from typing import Dict, Iterator, List, Mapping, Set, Tuple, Union -import constraint -from more_itertools import collapse, mark_ends -from parsimonious import Grammar, NodeVisitor, ParseError +from more_itertools import mark_ends from jubeatools.song import ( BeatsTime, @@ -31,24 +27,17 @@ from ..files import load_files from ..load_tools import ( CIRCLE_FREE_TO_NOTE_SYMBOL, EMPTY_BEAT_SYMBOLS, - LONG_ARROWS, - LONG_DIRECTION, DoubleColumnChartLine, DoubleColumnFrame, JubeatAnalyserParser, UnfinishedLongNote, - decimal_to_beats, find_long_note_candidates, is_double_column_chart_line, is_empty_line, - is_simple_solution, - long_note_solution_heuristic, parse_double_column_chart_line, pick_correct_long_note_candidates, - split_double_byte_line, ) -from ..symbol_definition import is_symbol_definition, parse_symbol_definition -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import CIRCLE_FREE_SYMBOLS class Memo1Frame(DoubleColumnFrame): diff --git a/jubeatools/formats/jubeat_analyser/memo2/dump.py b/jubeatools/formats/jubeat_analyser/memo2/dump.py index 50c0113..460f12c 100644 --- a/jubeatools/formats/jubeat_analyser/memo2/dump.py +++ b/jubeatools/formats/jubeat_analyser/memo2/dump.py @@ -1,19 +1,15 @@ -from collections import ChainMap, defaultdict -from copy import deepcopy +from collections import defaultdict from dataclasses import dataclass, field from decimal import Decimal from fractions import Fraction -from functools import partial from io import StringIO from itertools import chain, zip_longest -from math import ceil -from pathlib import Path -from typing import Dict, Iterator, List, Optional, Set, Tuple, Union +from typing import Dict, Iterator, List, Optional, Union -from more_itertools import chunked, collapse, intersperse, mark_ends, windowed +from more_itertools import collapse, intersperse, mark_ends, windowed from sortedcontainers import SortedKeyList -from jubeatools.formats.filetypes import ChartFile, JubeatFile +from jubeatools.formats.filetypes import ChartFile from jubeatools.song import ( BeatsTime, BPMEvent, @@ -31,20 +27,15 @@ from jubeatools.version import __version__ from ..command import dump_command from ..dump_tools import ( - BEATS_TIME_TO_SYMBOL, - COMMAND_ORDER, - DEFAULT_EXTRA_SYMBOLS, DIFFICULTIES, DIRECTION_TO_ARROW, DIRECTION_TO_LINE, NOTE_TO_CIRCLE_FREE_SYMBOL, LongNoteEnd, SortedDefaultDict, - create_sections_from_chart, - fraction_to_decimal, jubeat_analyser_file_dumper, ) -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import NOTE_SYMBOLS AnyNote = Union[TapNote, LongNote, LongNoteEnd] diff --git a/jubeatools/formats/jubeat_analyser/memo2/load.py b/jubeatools/formats/jubeat_analyser/memo2/load.py index b6c6cc0..df7f24c 100644 --- a/jubeatools/formats/jubeat_analyser/memo2/load.py +++ b/jubeatools/formats/jubeat_analyser/memo2/load.py @@ -1,15 +1,10 @@ -import warnings -from collections import ChainMap -from copy import deepcopy from dataclasses import astuple, dataclass from decimal import Decimal from functools import reduce -from itertools import chain, product, zip_longest +from itertools import product, zip_longest from pathlib import Path from typing import Dict, Iterator, List, Mapping, Optional, Set, Tuple, Union -import constraint -from more_itertools import collapse, mark_ends from parsimonious import Grammar, NodeVisitor, ParseError from parsimonious.nodes import Node @@ -33,22 +28,13 @@ from ..files import load_files from ..load_tools import ( CIRCLE_FREE_TO_NOTE_SYMBOL, EMPTY_BEAT_SYMBOLS, - LONG_ARROWS, - LONG_DIRECTION, JubeatAnalyserParser, UnfinishedLongNote, - decimal_to_beats, find_long_note_candidates, - is_double_column_chart_line, is_empty_line, - is_simple_solution, - long_note_solution_heuristic, - parse_double_column_chart_line, pick_correct_long_note_candidates, - split_double_byte_line, ) -from ..symbol_definition import is_symbol_definition, parse_symbol_definition -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import CIRCLE_FREE_SYMBOLS @dataclass diff --git a/jubeatools/formats/jubeat_analyser/mono_column/dump.py b/jubeatools/formats/jubeat_analyser/mono_column/dump.py index 0ef471c..8d43058 100644 --- a/jubeatools/formats/jubeat_analyser/mono_column/dump.py +++ b/jubeatools/formats/jubeat_analyser/mono_column/dump.py @@ -1,18 +1,10 @@ -from collections import ChainMap, defaultdict from copy import deepcopy -from dataclasses import dataclass, field -from decimal import Decimal -from fractions import Fraction -from functools import partial from io import StringIO -from itertools import chain -from pathlib import Path -from typing import Dict, Iterator, List, Mapping, Optional, Tuple +from typing import Dict, Iterator, List -from more_itertools import collapse, intersperse, mark_ends, windowed -from sortedcontainers import SortedKeyList +from more_itertools import collapse, intersperse, mark_ends -from jubeatools.formats.filetypes import ChartFile, JubeatFile +from jubeatools.formats.filetypes import ChartFile from jubeatools.song import ( BeatsTime, Chart, @@ -27,16 +19,13 @@ from jubeatools.version import __version__ from ..dump_tools import ( BEATS_TIME_TO_SYMBOL, - COMMAND_ORDER, DEFAULT_EXTRA_SYMBOLS, DIRECTION_TO_ARROW, DIRECTION_TO_LINE, NOTE_TO_CIRCLE_FREE_SYMBOL, JubeatAnalyserDumpedSection, LongNoteEnd, - SortedDefaultDict, create_sections_from_chart, - fraction_to_decimal, jubeat_analyser_file_dumper, ) @@ -156,7 +145,7 @@ def _dump_mono_column_chart( # Actual output to file file = StringIO() file.write(f"// Converted using jubeatools {__version__}\n") - file.write(f"// https://github.com/Stepland/jubeatools\n\n") + file.write("// https://github.com/Stepland/jubeatools\n\n") for _, section in sections.items(): file.write(section.render(circle_free) + "\n") diff --git a/jubeatools/formats/jubeat_analyser/mono_column/load.py b/jubeatools/formats/jubeat_analyser/mono_column/load.py index 6e45515..f2d987a 100644 --- a/jubeatools/formats/jubeat_analyser/mono_column/load.py +++ b/jubeatools/formats/jubeat_analyser/mono_column/load.py @@ -1,22 +1,16 @@ -import re -import warnings -from collections import Counter from copy import deepcopy from dataclasses import astuple, dataclass from decimal import Decimal -from enum import Enum from functools import reduce from itertools import product from pathlib import Path from typing import Dict, Iterator, List, Set, Tuple, Union -import constraint from parsimonious import Grammar, NodeVisitor, ParseError from parsimonious.nodes import Node from jubeatools.song import ( BeatsTime, - BPMEvent, Chart, LongNote, Metadata, @@ -33,21 +27,16 @@ from ..command import is_command, parse_command from ..files import load_files from ..load_tools import ( CIRCLE_FREE_TO_BEATS_TIME, - LONG_ARROWS, - LONG_DIRECTION, JubeatAnalyserParser, UnfinishedLongNote, - decimal_to_beats, find_long_note_candidates, is_empty_line, is_separator, - is_simple_solution, - long_note_solution_heuristic, pick_correct_long_note_candidates, split_double_byte_line, ) from ..symbol_definition import is_symbol_definition, parse_symbol_definition -from ..symbols import CIRCLE_FREE_SYMBOLS, NOTE_SYMBOLS +from ..symbols import CIRCLE_FREE_SYMBOLS mono_column_chart_line_grammar = Grammar( r""" @@ -78,7 +67,9 @@ def is_mono_column_chart_line(line: str) -> bool: def parse_mono_column_chart_line(line: str) -> str: - return MonoColumnChartLineVisitor().visit(mono_column_chart_line_grammar.parse(line)) # type: ignore + return MonoColumnChartLineVisitor().visit( # type: ignore + mono_column_chart_line_grammar.parse(line) + ) @dataclass diff --git a/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py b/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py index 11b117a..5061aa6 100644 --- a/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py +++ b/jubeatools/formats/jubeat_analyser/tests/memo/test_memo.py @@ -1,20 +1,15 @@ -import tempfile from decimal import Decimal -from fractions import Fraction from pathlib import Path from typing import Set, Union from hypothesis import example, given -from hypothesis import note as hypothesis_note from hypothesis import strategies as st from jubeatools import song from jubeatools.formats.enum import Format -from jubeatools.formats.guess import guess_format -from jubeatools.formats.jubeat_analyser.memo.dump import _dump_memo_chart, dump_memo -from jubeatools.formats.jubeat_analyser.memo.load import MemoParser, load_memo +from jubeatools.formats.jubeat_analyser.memo.dump import _dump_memo_chart +from jubeatools.formats.jubeat_analyser.memo.load import MemoParser from jubeatools.testutils import strategies as jbst -from jubeatools.testutils.typing import DrawFunc from ..test_utils import load_and_dump_then_check, memo_compatible_song from . import example1, example2, example3 diff --git a/jubeatools/formats/jubeat_analyser/tests/memo1/test_memo1.py b/jubeatools/formats/jubeat_analyser/tests/memo1/test_memo1.py index f6177f6..29a64a0 100644 --- a/jubeatools/formats/jubeat_analyser/tests/memo1/test_memo1.py +++ b/jubeatools/formats/jubeat_analyser/tests/memo1/test_memo1.py @@ -7,7 +7,7 @@ from hypothesis import strategies as st from jubeatools import song from jubeatools.formats import Format -from jubeatools.formats.jubeat_analyser.memo1.dump import _dump_memo1_chart, dump_memo1 +from jubeatools.formats.jubeat_analyser.memo1.dump import _dump_memo1_chart from jubeatools.formats.jubeat_analyser.memo1.load import Memo1Parser from jubeatools.testutils.strategies import NoteOption from jubeatools.testutils.strategies import notes as notes_strat diff --git a/jubeatools/formats/jubeat_analyser/tests/memo2/test_memo2.py b/jubeatools/formats/jubeat_analyser/tests/memo2/test_memo2.py index d7ac0ef..6097236 100644 --- a/jubeatools/formats/jubeat_analyser/tests/memo2/test_memo2.py +++ b/jubeatools/formats/jubeat_analyser/tests/memo2/test_memo2.py @@ -14,7 +14,6 @@ from jubeatools.song import ( Chart, LongNote, Metadata, - NotePosition, SecondsTime, Song, TapNote, diff --git a/jubeatools/formats/jubeat_analyser/tests/test_examples.py b/jubeatools/formats/jubeat_analyser/tests/test_examples.py index af66d87..061a271 100644 --- a/jubeatools/formats/jubeat_analyser/tests/test_examples.py +++ b/jubeatools/formats/jubeat_analyser/tests/test_examples.py @@ -1,4 +1,3 @@ -from functools import wraps from importlib import resources import pytest @@ -14,4 +13,4 @@ def test_RorataJins_example() -> None: with resources.path(data, "RorataJin's example.txt") as p: format_ = guess_format(p) loader = LOADERS[format_] - song = loader(p) + _ = loader(p) diff --git a/jubeatools/formats/memon/memon.py b/jubeatools/formats/memon/memon.py index 71d77ef..8f3877f 100644 --- a/jubeatools/formats/memon/memon.py +++ b/jubeatools/formats/memon/memon.py @@ -1,7 +1,7 @@ from io import StringIO from itertools import chain from pathlib import Path -from typing import IO, Any, Dict, Iterable, List, Mapping, Tuple, Union +from typing import Any, Dict, List, Union import simplejson as json from marshmallow import ( @@ -9,12 +9,12 @@ from marshmallow import ( Schema, ValidationError, fields, - post_load, validate, validates_schema, ) +from multidict import MultiDict -from jubeatools.song import * +from jubeatools import song as jbt from jubeatools.utils import lcm # v0.x.x long note value : @@ -134,36 +134,38 @@ def _load_raw_memon(file: Path) -> Dict[str, Any]: return res -def _load_memon_note_v0(note: dict, resolution: int) -> Union[TapNote, LongNote]: - position = NotePosition.from_index(note["n"]) - time = beats_time_from_ticks(ticks=note["t"], resolution=resolution) +def _load_memon_note_v0( + note: dict, resolution: int +) -> Union[jbt.TapNote, jbt.LongNote]: + position = jbt.NotePosition.from_index(note["n"]) + time = jbt.beats_time_from_ticks(ticks=note["t"], resolution=resolution) if note["l"] > 0: - duration = beats_time_from_ticks(ticks=note["l"], resolution=resolution) - tail_tip = position + NotePosition(*P_VALUE_TO_X_Y_OFFSET[note["p"]]) - return LongNote(time, position, duration, tail_tip) + duration = jbt.beats_time_from_ticks(ticks=note["l"], resolution=resolution) + tail_tip = position + jbt.NotePosition(*P_VALUE_TO_X_Y_OFFSET[note["p"]]) + return jbt.LongNote(time, position, duration, tail_tip) else: - return TapNote(time, position) + return jbt.TapNote(time, position) -def load_memon_legacy(file: Path) -> Song: +def load_memon_legacy(file: Path) -> jbt.Song: raw_memon = _load_raw_memon(file) schema = Memon_legacy() memon = schema.load(raw_memon) - metadata = Metadata( + metadata = jbt.Metadata( title=memon["metadata"]["title"], artist=memon["metadata"]["artist"], audio=Path(memon["metadata"]["audio"]), cover=Path(memon["metadata"]["cover"]), ) - global_timing = Timing( - events=[BPMEvent(time=BeatsTime(0), BPM=memon["metadata"]["BPM"])], - beat_zero_offset=SecondsTime(-memon["metadata"]["offset"]), + global_timing = jbt.Timing( + events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=memon["metadata"]["BPM"])], + beat_zero_offset=jbt.SecondsTime(-memon["metadata"]["offset"]), ) - charts: MultiDict[Chart] = MultiDict() + charts: MultiDict[jbt.Chart] = MultiDict() for memon_chart in memon["data"]: charts.add( memon_chart["dif_name"], - Chart( + jbt.Chart( level=memon_chart["level"], notes=[ _load_memon_note_v0(note, memon_chart["resolution"]) @@ -172,28 +174,28 @@ def load_memon_legacy(file: Path) -> Song: ), ) - return Song(metadata=metadata, charts=charts, global_timing=global_timing) + return jbt.Song(metadata=metadata, charts=charts, global_timing=global_timing) -def load_memon_0_1_0(file: Path) -> Song: +def load_memon_0_1_0(file: Path) -> jbt.Song: raw_memon = _load_raw_memon(file) schema = Memon_0_1_0() memon = schema.load(raw_memon) - metadata = Metadata( + metadata = jbt.Metadata( title=memon["metadata"]["title"], artist=memon["metadata"]["artist"], audio=Path(memon["metadata"]["audio"]), cover=Path(memon["metadata"]["cover"]), ) - global_timing = Timing( - events=[BPMEvent(time=BeatsTime(0), BPM=memon["metadata"]["BPM"])], - beat_zero_offset=SecondsTime(-memon["metadata"]["offset"]), + global_timing = jbt.Timing( + events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=memon["metadata"]["BPM"])], + beat_zero_offset=jbt.SecondsTime(-memon["metadata"]["offset"]), ) - charts: MultiDict[Chart] = MultiDict() + charts: MultiDict[jbt.Chart] = MultiDict() for difficulty, memon_chart in memon["data"].items(): charts.add( difficulty, - Chart( + jbt.Chart( level=memon_chart["level"], notes=[ _load_memon_note_v0(note, memon_chart["resolution"]) @@ -202,10 +204,10 @@ def load_memon_0_1_0(file: Path) -> Song: ), ) - return Song(metadata=metadata, charts=charts, global_timing=global_timing) + return jbt.Song(metadata=metadata, charts=charts, global_timing=global_timing) -def load_memon_0_2_0(file: Path) -> Song: +def load_memon_0_2_0(file: Path) -> jbt.Song: raw_memon = _load_raw_memon(file) schema = Memon_0_2_0() memon = schema.load(raw_memon) @@ -213,24 +215,24 @@ def load_memon_0_2_0(file: Path) -> Song: if "preview" in memon["metadata"]: start = memon["metadata"]["preview"]["position"] length = memon["metadata"]["preview"]["length"] - preview = Preview(start, length) + preview = jbt.Preview(start, length) - metadata = Metadata( + metadata = jbt.Metadata( title=memon["metadata"]["title"], artist=memon["metadata"]["artist"], audio=Path(memon["metadata"]["audio"]), cover=Path(memon["metadata"]["cover"]), preview=preview, ) - global_timing = Timing( - events=[BPMEvent(time=BeatsTime(0), BPM=memon["metadata"]["BPM"])], - beat_zero_offset=SecondsTime(-memon["metadata"]["offset"]), + global_timing = jbt.Timing( + events=[jbt.BPMEvent(time=jbt.BeatsTime(0), BPM=memon["metadata"]["BPM"])], + beat_zero_offset=jbt.SecondsTime(-memon["metadata"]["offset"]), ) - charts: MultiDict[Chart] = MultiDict() + charts: MultiDict[jbt.Chart] = MultiDict() for difficulty, memon_chart in memon["data"].items(): charts.add( difficulty, - Chart( + jbt.Chart( level=memon_chart["level"], notes=[ _load_memon_note_v0(note, memon_chart["resolution"]) @@ -239,10 +241,10 @@ def load_memon_0_2_0(file: Path) -> Song: ), ) - return Song(metadata=metadata, charts=charts, global_timing=global_timing) + return jbt.Song(metadata=metadata, charts=charts, global_timing=global_timing) -def _long_note_tail_value_v0(note: LongNote) -> int: +def _long_note_tail_value_v0(note: jbt.LongNote) -> int: dx = note.tail_tip.x - note.position.x dy = note.tail_tip.y - note.position.y try: @@ -253,7 +255,7 @@ def _long_note_tail_value_v0(note: LongNote) -> int: ) from None -def _get_timing(song: Song) -> Timing: +def _get_timing(song: jbt.Song) -> jbt.Timing: if song.global_timing is not None: return song.global_timing else: @@ -262,7 +264,7 @@ def _get_timing(song: Song) -> Timing: ) -def _raise_if_unfit_for_v0(song: Song, version: str) -> None: +def _raise_if_unfit_for_v0(song: jbt.Song, version: str) -> None: """Raises an exception if the Song object is ill-formed or contains information that cannot be represented in a memon v0.x.y file (includes legacy)""" @@ -311,21 +313,21 @@ def _dump_to_json(memon: dict) -> bytes: return memon_fp.getvalue().encode("utf-8") -def _compute_resolution(notes: List[Union[TapNote, LongNote]]) -> int: +def _compute_resolution(notes: List[Union[jbt.TapNote, jbt.LongNote]]) -> int: return lcm( *chain( iter(note.time.denominator for note in notes), iter( note.duration.denominator for note in notes - if isinstance(note, LongNote) + if isinstance(note, jbt.LongNote) ), ) ) def _dump_memon_note_v0( - note: Union[TapNote, LongNote], resolution: int + note: Union[jbt.TapNote, jbt.LongNote], resolution: int ) -> Dict[str, int]: """converts a note into the {n, t, l, p} form""" memon_note = { @@ -334,7 +336,7 @@ def _dump_memon_note_v0( "l": 0, "p": 0, } - if isinstance(note, LongNote): + if isinstance(note, jbt.LongNote): memon_note["l"] = note.duration.numerator * ( resolution // note.duration.denominator ) @@ -343,7 +345,7 @@ def _dump_memon_note_v0( return memon_note -def dump_memon_legacy(song: Song, path: Path, **kwargs: dict) -> Dict[Path, bytes]: +def dump_memon_legacy(song: jbt.Song, path: Path, **kwargs: dict) -> Dict[Path, bytes]: _raise_if_unfit_for_v0(song, "legacy") timing = _get_timing(song) @@ -383,7 +385,7 @@ def dump_memon_legacy(song: Song, path: Path, **kwargs: dict) -> Dict[Path, byte return {filepath: _dump_to_json(memon)} -def dump_memon_0_1_0(song: Song, path: Path, **kwargs: dict) -> Dict[Path, bytes]: +def dump_memon_0_1_0(song: jbt.Song, path: Path, **kwargs: dict) -> Dict[Path, bytes]: _raise_if_unfit_for_v0(song, "v0.1.0") timing = _get_timing(song) @@ -419,7 +421,7 @@ def dump_memon_0_1_0(song: Song, path: Path, **kwargs: dict) -> Dict[Path, bytes return {filepath: _dump_to_json(memon)} -def dump_memon_0_2_0(song: Song, path: Path, **kwargs: dict) -> Dict[Path, bytes]: +def dump_memon_0_2_0(song: jbt.Song, path: Path, **kwargs: dict) -> Dict[Path, bytes]: _raise_if_unfit_for_v0(song, "v0.2.0") timing = _get_timing(song) diff --git a/jubeatools/formats/memon/test_memon.py b/jubeatools/formats/memon/test_memon.py index ee3ceae..0e61ee3 100644 --- a/jubeatools/formats/memon/test_memon.py +++ b/jubeatools/formats/memon/test_memon.py @@ -1,6 +1,5 @@ import tempfile from pathlib import Path -from typing import Callable import hypothesis.strategies as st from hypothesis import given diff --git a/jubeatools/song.py b/jubeatools/song.py index ff29d1b..59ae26a 100644 --- a/jubeatools/song.py +++ b/jubeatools/song.py @@ -6,9 +6,9 @@ Every output format is created from a Song instance Most timing-related info is stored as beat fractions, otherwise a decimal number of seconds is used """ + from __future__ import annotations -from collections import UserList, namedtuple from dataclasses import astuple, dataclass, field from decimal import Decimal from fractions import Fraction diff --git a/jubeatools/testutils/strategies.py b/jubeatools/testutils/strategies.py index 6e20290..d89e1c7 100644 --- a/jubeatools/testutils/strategies.py +++ b/jubeatools/testutils/strategies.py @@ -1,11 +1,12 @@ """ Hypothesis strategies to generate notes and charts """ + from decimal import Decimal -from enum import Enum, Flag, auto +from enum import Flag, auto from itertools import product from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, Set, TypeVar, Union +from typing import Any, Dict, Optional, Set, Union import hypothesis.strategies as st from multidict import MultiDict diff --git a/jubeatools/utils.py b/jubeatools/utils.py index 7ed459d..85e9b6b 100644 --- a/jubeatools/utils.py +++ b/jubeatools/utils.py @@ -1,7 +1,7 @@ import unicodedata from functools import reduce from math import gcd -from typing import Callable, Iterable, Optional, TypeVar +from typing import Callable, Optional, TypeVar def single_lcm(a: int, b: int) -> int: diff --git a/poetry.lock b/poetry.lock index 84dc9ac..a10c0c0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -28,6 +28,31 @@ docs = ["furo", "sphinx", "zope.interface"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +[[package]] +name = "autoflake" +version = "1.4" +description = "Removes unused imports and unused variables" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +pyflakes = ">=1.1.0" + +[[package]] +name = "autoimport" +version = "0.7.0" +description = "Autoimport missing python libraries." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +autoflake = "*" +Click = "*" +pyprojroot = "*" +sh = "*" + [[package]] name = "black" version = "21.4b2" @@ -65,6 +90,19 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "flake8" +version = "3.9.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" + [[package]] name = "hypothesis" version = "6.10.1" @@ -129,6 +167,14 @@ docs = ["sphinx (==3.4.3)", "sphinx-issues (==1.2.0)", "alabaster (==0.7.12)", " lint = ["mypy (==0.812)", "flake8 (==3.9.0)", "flake8-bugbear (==21.3.2)", "pre-commit (>=2.4,<3.0)"] tests = ["pytest", "pytz", "simplejson"] +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "more-itertools" version = "8.7.0" @@ -230,6 +276,22 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pycodestyle" +version = "2.7.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.3.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + [[package]] name = "pyparsing" version = "2.4.7" @@ -238,6 +300,14 @@ category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "pyprojroot" +version = "0.2.0" +description = "Find project root paths and return relative project files" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "pytest" version = "6.2.3" @@ -290,6 +360,14 @@ python-versions = "*" [package.extras] dev = ["pytest"] +[[package]] +name = "sh" +version = "1.14.1" +description = "Python subprocess replacement" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "simplejson" version = "3.17.2" @@ -341,7 +419,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "2f391539f8bf0d106956ec4f499fc1c98a2305e94cbd2682d913885d78d7a4dd" +content-hash = "4065f5a647b6cfb73d1aeceaae1fee4f94ecccdaa807aa134d3c307c6ecfbbd9" [metadata.files] appdirs = [ @@ -356,6 +434,13 @@ attrs = [ {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] +autoflake = [ + {file = "autoflake-1.4.tar.gz", hash = "sha256:61a353012cff6ab94ca062823d1fb2f692c4acda51c76ff83a8d77915fba51ea"}, +] +autoimport = [ + {file = "autoimport-0.7.0-py3-none-any.whl", hash = "sha256:d98ae9100d7dfbac7183336c69d2a87bfbeebcdeb59253760f1e2a9db27fc970"}, + {file = "autoimport-0.7.0.tar.gz", hash = "sha256:1c15c013e9029ec252fa8a1895570c34ff5676eabbdd85ae9514024e22ffee34"}, +] black = [ {file = "black-21.4b2-py3-none-any.whl", hash = "sha256:bff7067d8bc25eb21dcfdbc8c72f2baafd9ec6de4663241a52fb904b304d391f"}, {file = "black-21.4b2.tar.gz", hash = "sha256:fc9bcf3b482b05c1f35f6a882c079dc01b9c7795827532f4cc43c0ec88067bbc"}, @@ -368,6 +453,10 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] +flake8 = [ + {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, + {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, +] hypothesis = [ {file = "hypothesis-6.10.1-py3-none-any.whl", hash = "sha256:1d65f58d82d1cbd35b6441bda3bb11cb1adc879d6b2af191aea388fa412171b1"}, {file = "hypothesis-6.10.1.tar.gz", hash = "sha256:586b6c46e90878c2546743afbed348bca51e1f30e3461fa701fad58c2c47c650"}, @@ -384,6 +473,10 @@ marshmallow = [ {file = "marshmallow-3.11.1-py2.py3-none-any.whl", hash = "sha256:0dd42891a5ef288217ed6410917f3c6048f585f8692075a0052c24f9bfff9dfd"}, {file = "marshmallow-3.11.1.tar.gz", hash = "sha256:16e99cb7f630c0ef4d7d364ed0109ac194268dde123966076ab3dafb9ae3906b"}, ] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] more-itertools = [ {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"}, {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"}, @@ -478,10 +571,22 @@ py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] +pycodestyle = [ + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, +] +pyflakes = [ + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, +] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] +pyprojroot = [ + {file = "pyprojroot-0.2.0-py3-none-any.whl", hash = "sha256:741e8b4878a0d6bb6b06ec09aa05797130289e2127aa595b8f1cbadce697909f"}, + {file = "pyprojroot-0.2.0.tar.gz", hash = "sha256:a79900dc52ee097bfd8d917a3d45e2b98494c47a57ba3a71bf609d7a156732e8"}, +] pytest = [ {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, @@ -535,6 +640,10 @@ regex = [ rope = [ {file = "rope-0.17.0.tar.gz", hash = "sha256:658ad6705f43dcf3d6df379da9486529cf30e02d9ea14c5682aa80eb33b649e1"}, ] +sh = [ + {file = "sh-1.14.1-py2.py3-none-any.whl", hash = "sha256:75e86a836f47de095d4531718fe8489e6f7446c75ddfa5596f632727b919ffae"}, + {file = "sh-1.14.1.tar.gz", hash = "sha256:39aa9af22f6558a0c5d132881cf43e34828ca03e4ae11114852ca6a55c7c1d8e"}, +] simplejson = [ {file = "simplejson-3.17.2-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:2d3eab2c3fe52007d703a26f71cf649a8c771fcdd949a3ae73041ba6797cfcf8"}, {file = "simplejson-3.17.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:813846738277729d7db71b82176204abc7fdae2f566e2d9fcf874f9b6472e3e6"}, diff --git a/pyproject.toml b/pyproject.toml index 8b51a1b..ce75734 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,8 @@ hypothesis = "^6.10.1" mypy = "^0.812" isort = "^4.3.21" toml = "^0.10.2" +flake8 = "^3.9.1" +autoimport = "^0.7.0" [tool.poetry.scripts] jubeatools = 'jubeatools.cli:convert' diff --git a/utils/check_code.sh b/utils/check_code.sh new file mode 100755 index 0000000..e84b441 --- /dev/null +++ b/utils/check_code.sh @@ -0,0 +1,2 @@ +flake8 +mypy jubeatools \ No newline at end of file diff --git a/utils/format_code.sh b/utils/format_code.sh index c0601fa..7081492 100755 --- a/utils/format_code.sh +++ b/utils/format_code.sh @@ -1,2 +1,15 @@ +# find all files with unused imports, then hand them off to autoimport +flake8 \ + --isolated \ + --select=F401 \ + --format='%(path)s' \ + --exclude=__init__.py \ +| sort \ +| uniq \ +| xargs autoimport + +# auto-sort imports in all files isort -y + +# format code black jubeatools \ No newline at end of file