mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: default profile report containers to empty
This commit is contained in:
1 parent
2a56fccc85
commit
fdc7fc8326
2 files changed
+51
-5
No files matched your search
@@ -18,7 +18,7 @@ import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from dataclasses import asdict, dataclass
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
@@ -998,19 +998,19 @@ class EntryReport:
|
||||
class ProfileReport:
|
||||
name: str
|
||||
repo: str | None = None
|
||||
repos: list[str] = None
|
||||
repos: list[str] = field(default_factory=list)
|
||||
pinned_tag: str | None = None
|
||||
host: str | None = None
|
||||
pin: str | None = None
|
||||
pin_origin: str | None = None
|
||||
head: str | None = None
|
||||
entries: list[EntryReport] = None
|
||||
entries: list[EntryReport] = field(default_factory=list)
|
||||
skipped: str | None = None
|
||||
counts: dict[str, int] = None
|
||||
counts: dict[str, int] = field(default_factory=dict)
|
||||
# Declared repositories whose forge the tool cannot read. They are named
|
||||
# rather than dropped: a `source` on an unknown host used to fall back to
|
||||
# `upstream` in silence, and a divergence between the two went unseen.
|
||||
unread: list[str] = None
|
||||
unread: list[str] = field(default_factory=list)
|
||||
|
||||
def needs_review(self) -> int:
|
||||
counts = self.counts or {}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"""A dataclass field typed as a container defaults to an empty one.
|
||||
|
||||
ProfileReport declared `entries: list[EntryReport] = None`: the annotation
|
||||
promised a list, the public constructor gave None, and format_report raised
|
||||
TypeError on the first loop.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _is_dataclass(node: ast.ClassDef) -> bool:
|
||||
for deco in node.decorator_list:
|
||||
target = deco.func if isinstance(deco, ast.Call) else deco
|
||||
name = target.attr if isinstance(target, ast.Attribute) else getattr(target, "id", "")
|
||||
if name == "dataclass":
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ContainerFieldsAreNotNone(unittest.TestCase):
|
||||
def test_no_container_annotation_defaults_to_none(self):
|
||||
for path in sorted([*(REPO_ROOT / "scripts").rglob("*.py"), REPO_ROOT / "install.py"]):
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||
for node in ast.walk(tree):
|
||||
if not (isinstance(node, ast.ClassDef) and _is_dataclass(node)):
|
||||
continue
|
||||
for stmt in node.body:
|
||||
if not isinstance(stmt, ast.AnnAssign) or stmt.value is None:
|
||||
continue
|
||||
annotation = ast.unparse(stmt.annotation)
|
||||
is_none = isinstance(stmt.value, ast.Constant) and stmt.value.value is None
|
||||
if is_none and annotation.split("[")[0] in ("list", "dict", "set") \
|
||||
and "None" not in annotation:
|
||||
with self.subTest(module=path.name, cls=node.name,
|
||||
field=ast.unparse(stmt.target)):
|
||||
self.fail(f"{annotation} defaults to None")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user