Initial commit
This commit is contained in:
commit
1b18db3e77
5 changed files with 437 additions and 0 deletions
0
__init__.py
Normal file
0
__init__.py
Normal file
69
analyze.py
Normal file
69
analyze.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
import argparse
|
||||
from collections import Counter, defaultdict
|
||||
import shelve
|
||||
from typing import Counter, Dict, List, Optional
|
||||
|
||||
import simfile
|
||||
|
||||
from smoketest import SimfileError
|
||||
|
||||
|
||||
def analyze(
|
||||
simfile_errors: Dict[str, List[SimfileError]],
|
||||
*,
|
||||
traceback_substring: str,
|
||||
verbose: bool,
|
||||
number: Optional[int]
|
||||
) -> None:
|
||||
error_counter = Counter()
|
||||
error_occurrences = defaultdict(list)
|
||||
for filename, errors in simfile_errors.items():
|
||||
for error in errors:
|
||||
line = error.traceback.splitlines()[-1]
|
||||
if not traceback_substring or traceback_substring in line:
|
||||
error_counter[line] += 1
|
||||
error_occurrences[line].append((filename, error.context))
|
||||
for line, count in error_counter.most_common(number):
|
||||
print(f'=== {line} ({count} occurrences) ===')
|
||||
if verbose:
|
||||
for filename, context in error_occurrences[line]:
|
||||
print(f' * "{filename}" (during {repr(context)})')
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument(
|
||||
'-s', '--shelf',
|
||||
help='shelf file to use',
|
||||
default=f'shelf-{simfile.__version__}',
|
||||
)
|
||||
argparser.add_argument(
|
||||
'-t', '--traceback-substring',
|
||||
help='filter tracebacks by substring',
|
||||
)
|
||||
argparser.add_argument(
|
||||
'-n', '--number',
|
||||
type=int,
|
||||
default=10,
|
||||
help='number of most common errors to print',
|
||||
)
|
||||
argparser.add_argument(
|
||||
'-v', '--verbose',
|
||||
help='enable verbose output',
|
||||
default=False,
|
||||
action='store_true',
|
||||
)
|
||||
args = argparser.parse_args()
|
||||
|
||||
with shelve.open(args.shelf) as simfile_errors:
|
||||
analyze(
|
||||
simfile_errors,
|
||||
traceback_substring=args.traceback_substring,
|
||||
verbose=args.verbose,
|
||||
number=args.number,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
215
poetry.lock
generated
Normal file
215
poetry.lock
generated
Normal file
|
@ -0,0 +1,215 @@
|
|||
[[package]]
|
||||
name = "appdirs"
|
||||
version = "1.4.4"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "22.6.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
|
||||
typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""}
|
||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.5"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "fs"
|
||||
version = "2.4.16"
|
||||
description = "Python's filesystem abstraction layer"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
appdirs = ">=1.4.3,<1.5.0"
|
||||
six = ">=1.10,<2.0"
|
||||
|
||||
[package.extras]
|
||||
scandir = ["scandir (>=1.5,<2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "4.12.0"
|
||||
description = "Read metadata from Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
|
||||
zipp = ">=0.5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"]
|
||||
perf = ["ipython"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "msdparser"
|
||||
version = "2.0.0b5"
|
||||
description = "Simple MSD parser (rhythm game format)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.9.0"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
|
||||
[[package]]
|
||||
name = "peewee"
|
||||
version = "3.15.1"
|
||||
description = "a little orm"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.5.2"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
|
||||
test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "2.13.0"
|
||||
description = "Python helper for Semantic Versioning (http://semver.org/)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "simfile"
|
||||
version = "2.1.0b3"
|
||||
description = "Modern simfile library for Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
fs = ">=2.4.15,<2.5.0"
|
||||
msdparser = ">=2.0.0b4,<2.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "typed-ast"
|
||||
version = "1.5.4"
|
||||
description = "a fork of Python 2 and 3 ast modules with type comment support"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.3.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.7+"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.8.1"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"]
|
||||
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "4fdaaca625a1242b7a0012eb7261349b6f25459688543e12b9d4c37eed4f8e3a"
|
||||
|
||||
[metadata.files]
|
||||
appdirs = []
|
||||
black = []
|
||||
click = []
|
||||
colorama = []
|
||||
fs = []
|
||||
importlib-metadata = []
|
||||
msdparser = []
|
||||
mypy-extensions = []
|
||||
pathspec = []
|
||||
peewee = []
|
||||
platformdirs = []
|
||||
semver = []
|
||||
simfile = []
|
||||
six = []
|
||||
tomli = []
|
||||
typed-ast = []
|
||||
typing-extensions = []
|
||||
zipp = []
|
19
pyproject.toml
Normal file
19
pyproject.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[tool.poetry]
|
||||
name = "simfile-smoketest"
|
||||
version = "0.1.0"
|
||||
description = "Smoketest for the `simfile` library"
|
||||
authors = ["Ash Garcia <github@garcia.sh>"]
|
||||
license = "MIT"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
simfile = {version = "^2.1.0b3", allow-prereleases = true}
|
||||
peewee = "^3.15.1"
|
||||
semver = "^2.13.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^22.6.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
134
smoketest.py
Normal file
134
smoketest.py
Normal file
|
@ -0,0 +1,134 @@
|
|||
import argparse
|
||||
import os
|
||||
import pprint
|
||||
import shelve
|
||||
import traceback
|
||||
from typing import Counter, Dict, List, Optional
|
||||
|
||||
import simfile
|
||||
from simfile.notes import NoteData
|
||||
from simfile.notes.group import group_notes
|
||||
from simfile.notes.timed import time_notes
|
||||
from simfile.ssc import SSCChart
|
||||
from simfile.timing.engine import TimingData
|
||||
from simfile.timing.displaybpm import displaybpm
|
||||
|
||||
|
||||
class SimfileError:
|
||||
context: str
|
||||
traceback: Optional[str]
|
||||
|
||||
def __init__(self, context: str):
|
||||
self.context = context
|
||||
self.traceback = traceback.format_exc()
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.context}: {self.traceback}"
|
||||
|
||||
|
||||
def check_simfile(filename: str) -> List[SimfileError]:
|
||||
errors = []
|
||||
try:
|
||||
sim = simfile.open(filename)
|
||||
except Exception:
|
||||
errors.append(SimfileError("parse"))
|
||||
return errors
|
||||
|
||||
timing_data = None
|
||||
try:
|
||||
timing_data = TimingData.from_simfile(sim)
|
||||
except Exception:
|
||||
errors.append(SimfileError("timing"))
|
||||
|
||||
try:
|
||||
displaybpm(sim)
|
||||
except Exception:
|
||||
errors.append(SimfileError("displaybpm"))
|
||||
|
||||
for c, chart in enumerate(sim.charts):
|
||||
try:
|
||||
note_data = NoteData.from_chart(chart)
|
||||
except Exception:
|
||||
errors.append(SimfileError(f"chart {c} note_data"))
|
||||
try:
|
||||
for _ in group_notes(note_data, join_heads_to_tails=True):
|
||||
pass
|
||||
except Exception:
|
||||
errors.append(SimfileError(f"chart {c} group_notes"))
|
||||
|
||||
if isinstance(chart, SSCChart):
|
||||
try:
|
||||
displaybpm(sim, chart)
|
||||
except Exception:
|
||||
errors.append(SimfileError(f"chart {c} displaybpm"))
|
||||
|
||||
# skip the remaining chart tests if timing data parsing failed
|
||||
if timing_data is None:
|
||||
continue
|
||||
|
||||
if isinstance(chart, SSCChart):
|
||||
try:
|
||||
timing_data = TimingData.from_simfile(sim, chart)
|
||||
except Exception:
|
||||
errors.append(SimfileError(f"chart {c} timing"))
|
||||
|
||||
try:
|
||||
for _ in time_notes(note_data, timing_data):
|
||||
pass
|
||||
except Exception:
|
||||
errors.append(SimfileError(f"chart {c} timed_notes"))
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def scan_directory(path: str, simfile_errors: Dict[str, List[SimfileError]]):
|
||||
stats = Counter()
|
||||
for entry in os.scandir(path):
|
||||
if simfile_errors.get(entry.path) == []:
|
||||
stats["skipped"] += 1
|
||||
continue
|
||||
if entry.is_dir():
|
||||
stats.update(scan_directory(entry.path, simfile_errors))
|
||||
elif any(entry.name.endswith(ext) for ext in (".sm", ".ssc")):
|
||||
errors = check_simfile(entry.path)
|
||||
stats["checked"] += 1
|
||||
simfile_errors[entry.path] = errors
|
||||
if errors:
|
||||
stats["error"] += 1
|
||||
pprint.pprint({"path": entry.path, "errors": errors})
|
||||
else:
|
||||
stats["success"] += 1
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def dir_path(string):
|
||||
if os.path.isdir(string):
|
||||
return string
|
||||
else:
|
||||
raise ValueError("dir_path: not a directory")
|
||||
|
||||
|
||||
def main():
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument(
|
||||
"directory",
|
||||
help="directory of simfiles to scan",
|
||||
type=dir_path,
|
||||
)
|
||||
argparser.add_argument(
|
||||
"-s",
|
||||
"--shelf",
|
||||
help="shelf file to use",
|
||||
default=f"shelf-{simfile.__version__}",
|
||||
)
|
||||
args = argparser.parse_args()
|
||||
|
||||
with shelve.open(args.shelf) as simfile_errors:
|
||||
stats = scan_directory(args.directory, simfile_errors)
|
||||
for k, v in stats.items():
|
||||
print(k, v)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue