Initial commit

This commit is contained in:
Ash Garcia 2022-07-17 22:32:44 -07:00
commit 1b18db3e77
5 changed files with 437 additions and 0 deletions

0
__init__.py Normal file
View file

69
analyze.py Normal file
View 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
View 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
View 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
View 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()