mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-26 06:42:32 +00:00
docs: add docstrings to all new/modified functions to meet 80% coverage
- lib/instruments.py: module docstring, class docstring, method docstrings for all public methods and private validators - lib/tunings.py: docstrings for _build_*, _valid_instrument_ids, _default_profile_id_for_instrument helper functions - lib/routers/instruments.py and lib/routers/stats.py: endpoint docstrings
This commit is contained in:
parent
3fb69d8595
commit
edeaf4db46
@ -20,6 +20,7 @@ _PROFILE_PATHWAYS = ("songs", "practice", "learn", "studio")
|
||||
|
||||
|
||||
def _validate_str(value, field_name, max_len=128):
|
||||
"""Validate a required non-empty string field, returning stripped value."""
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
raise ValueError(f"{field_name} must be a non-empty string")
|
||||
if len(value) > max_len:
|
||||
@ -28,6 +29,7 @@ def _validate_str(value, field_name, max_len=128):
|
||||
|
||||
|
||||
def _validate_optional_str(value, field_name, max_len=256):
|
||||
"""Validate an optional string field; None passes through, non-empty string required otherwise."""
|
||||
if value is None:
|
||||
return None
|
||||
if not isinstance(value, str):
|
||||
@ -38,6 +40,7 @@ def _validate_optional_str(value, field_name, max_len=256):
|
||||
|
||||
|
||||
def _validate_float_range(value, field_name, lo, hi):
|
||||
"""Validate a numeric value falls within [lo, hi], rejecting bools."""
|
||||
if not isinstance(value, (int, float)) or isinstance(value, bool):
|
||||
raise ValueError(f"{field_name} must be a number")
|
||||
v = float(value)
|
||||
@ -47,6 +50,7 @@ def _validate_float_range(value, field_name, lo, hi):
|
||||
|
||||
|
||||
def _validate_midi_list(lst, field_name, expected_len):
|
||||
"""Validate a list of MIDI note numbers (ints in 0-127) of the given length."""
|
||||
if not isinstance(lst, list):
|
||||
raise ValueError(f"{field_name} must be a list")
|
||||
if len(lst) != expected_len:
|
||||
@ -63,6 +67,7 @@ def _validate_midi_list(lst, field_name, expected_len):
|
||||
|
||||
|
||||
def _validate_offset_list(lst, field_name, expected_len):
|
||||
"""Validate a list of semitone offsets (ints in -12..12) of the given length."""
|
||||
if not isinstance(lst, list):
|
||||
raise ValueError(f"{field_name} must be a list of offsets")
|
||||
if len(lst) != expected_len:
|
||||
@ -242,17 +247,25 @@ class InstrumentRegistry:
|
||||
log.info("registered instrument %r (%s)", inst_id, label)
|
||||
|
||||
def unregister(self, instrument_id: str):
|
||||
"""Remove an instrument definition from the registry."""
|
||||
if instrument_id in self._instruments:
|
||||
del self._instruments[instrument_id]
|
||||
log.info("unregistered instrument %r", instrument_id)
|
||||
|
||||
def get(self, instrument_id: str) -> dict | None:
|
||||
"""Return the definition for a single instrument, or None."""
|
||||
return self._instruments.get(instrument_id)
|
||||
|
||||
def get_all(self) -> list[dict]:
|
||||
"""Return all registered instrument definitions."""
|
||||
return list(self._instruments.values())
|
||||
|
||||
def compute_tuning_midis(self, instrument_id: str, string_count: int, tuning_name: str) -> list[int] | None:
|
||||
"""Return absolute MIDI notes for a named tuning on a stringed instrument.
|
||||
|
||||
Computed by adding the tuning's semitone offsets to the instrument's
|
||||
standard open-string MIDI notes for the given string count.
|
||||
"""
|
||||
inst = self._instruments.get(instrument_id)
|
||||
if not inst or inst["kind"] != "stringed":
|
||||
return None
|
||||
@ -264,18 +277,21 @@ class InstrumentRegistry:
|
||||
return [s + o for s, o in zip(std, offsets)]
|
||||
|
||||
def get_tuning_names(self, instrument_id: str, string_count: int) -> list[str]:
|
||||
"""Return all tuning preset names for a stringed instrument + string count."""
|
||||
inst = self._instruments.get(instrument_id)
|
||||
if not inst or inst["kind"] != "stringed":
|
||||
return []
|
||||
return list((inst["tunings"].get(str(string_count)) or {}).keys())
|
||||
|
||||
def get_standard_midis(self, instrument_id: str, string_count: int) -> list[int] | None:
|
||||
"""Return standard open-string MIDI notes for a stringed instrument + string count."""
|
||||
inst = self._instruments.get(instrument_id)
|
||||
if not inst or inst["kind"] != "stringed":
|
||||
return None
|
||||
return inst["standard_tunings"].get(str(string_count))
|
||||
|
||||
def get_default_role(self, instrument_id: str) -> str | None:
|
||||
"""Return the id of the default role for an instrument, or None."""
|
||||
inst = self._instruments.get(instrument_id)
|
||||
if not inst:
|
||||
return None
|
||||
@ -287,6 +303,7 @@ class InstrumentRegistry:
|
||||
return None
|
||||
|
||||
def find_role_by_arrangement(self, instrument_id: str, arr_name: str, arr_flags: dict = None) -> str | None:
|
||||
"""Find the role id matching an arrangement name or path flags for a specific instrument."""
|
||||
inst = self._instruments.get(instrument_id)
|
||||
if not inst:
|
||||
return None
|
||||
@ -301,6 +318,7 @@ class InstrumentRegistry:
|
||||
return None
|
||||
|
||||
def instrument_id_for_arrangement(self, arr_name: str, arr_flags: dict = None) -> str | None:
|
||||
"""Return the instrument id whose roles match a given arrangement name or flags."""
|
||||
name_lower = (arr_name or "").strip().lower()
|
||||
for inst in self._instruments.values():
|
||||
for role in inst["roles"]:
|
||||
|
||||
@ -12,5 +12,6 @@ router = APIRouter()
|
||||
|
||||
@router.get("/api/instruments")
|
||||
def get_instruments():
|
||||
"""Return all instrument definitions registered via instrument plugins."""
|
||||
reg = getattr(appstate, "instrument_registry", None)
|
||||
return reg.get_all() if reg else []
|
||||
|
||||
@ -243,8 +243,7 @@ def api_stats_best():
|
||||
|
||||
@router.get("/api/stats/best-by-arrangement")
|
||||
def api_stats_best_by_arrangement():
|
||||
"""{filename: {arrangement_index: best_accuracy}} per-arrangement accuracy,
|
||||
for per-role badging and hover overlays on the library grid."""
|
||||
"""Return {filename: {arrangement_index: best_accuracy}} for per-role badging."""
|
||||
return appstate.meta_db.arrangement_accuracy_map()
|
||||
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ def set_instrument_registry(reg):
|
||||
|
||||
|
||||
def _build_standard_midis(registry=None):
|
||||
"""Build {instrument_key: [midis]} from the registry, falling back to hardcoded STANDARD_OPEN_MIDIS."""
|
||||
reg = registry or _instrument_registry
|
||||
result = {}
|
||||
if reg:
|
||||
@ -43,6 +44,7 @@ def _build_standard_midis(registry=None):
|
||||
|
||||
|
||||
def _build_preset_midis(registry=None):
|
||||
"""Build {instrument_key: {tuning_name: [midis]}} from the registry, falling back to TUNING_PRESET_MIDIS."""
|
||||
reg = registry or _instrument_registry
|
||||
result = {}
|
||||
if reg:
|
||||
@ -66,6 +68,7 @@ def _build_preset_midis(registry=None):
|
||||
|
||||
|
||||
def _build_profile_defaults(registry=None):
|
||||
"""Build {profile_id: profile_dict} from registered instruments, falling back to PROFILE_DEFAULTS."""
|
||||
reg = registry or _instrument_registry
|
||||
result = {}
|
||||
if reg:
|
||||
@ -97,11 +100,13 @@ def _build_profile_defaults(registry=None):
|
||||
|
||||
|
||||
def _build_profile_ids(registry=None):
|
||||
"""Return a tuple of valid instrument profile ids, derived from the registry."""
|
||||
profiles = _build_profile_defaults(registry)
|
||||
return tuple(profiles.keys())
|
||||
|
||||
|
||||
def _valid_instrument_ids(registry=None):
|
||||
"""Return the set of valid instrument IDs from the registry, or a guitar/bass fallback."""
|
||||
reg = registry or _instrument_registry
|
||||
if reg:
|
||||
ids = {inst["id"] for inst in reg.get_all()}
|
||||
@ -111,6 +116,7 @@ def _valid_instrument_ids(registry=None):
|
||||
|
||||
|
||||
def _default_profile_id_for_instrument(instrument_id, registry=None):
|
||||
"""Return the profile id for an instrument's default role, e.g. 'guitar-lead'."""
|
||||
profiles = _build_profile_defaults(registry)
|
||||
for pid, profile in profiles.items():
|
||||
if profile["instrument"] == instrument_id and profile.get("default_role"):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user