from __future__ import annotations
from typing import Optional, TypedDict, Dict
import humps
class ArticulationOptions(TypedDict, total=False):
name: str
stroke: str
hindi: str
ipa: str
eng_trans: str
stroke_nickname: str
[docs]
class Articulation:
[docs]
def __init__(self, options: Optional[ArticulationOptions] = None) -> None:
opts = humps.decamelize(options or {})
# Parameter validation
self._validate_parameters(opts)
self.name: str = opts.get('name', 'pluck')
stroke = opts.get('stroke')
hindi = opts.get('hindi')
ipa = opts.get('ipa')
eng_trans = opts.get('eng_trans')
stroke_nickname = opts.get('stroke_nickname')
if stroke is not None:
self.stroke = stroke
if hindi is not None:
self.hindi = hindi
if ipa is not None:
self.ipa = ipa
if eng_trans is not None:
self.eng_trans = eng_trans
if stroke_nickname is not None:
self.stroke_nickname = stroke_nickname
if getattr(self, 'stroke', None) == 'd' and not getattr(self, 'stroke_nickname', None):
self.stroke_nickname = 'da'
elif getattr(self, 'stroke', None) == 'r' and not getattr(self, 'stroke_nickname', None):
self.stroke_nickname = 'ra'
def _validate_parameters(self, opts: dict) -> None:
"""Validate constructor parameters and provide helpful error messages."""
if not opts:
return
# Define allowed parameter names
allowed_keys = {'name', 'stroke', 'hindi', 'ipa', 'eng_trans', 'stroke_nickname'}
provided_keys = set(opts.keys())
invalid_keys = provided_keys - allowed_keys
# Check for invalid parameter names
if invalid_keys:
error_messages = []
for key in invalid_keys:
if key == 'english_trans' or key == 'english':
error_messages.append(f"Parameter '{key}' not supported. Did you mean 'eng_trans'?")
elif key == 'articulation_name':
error_messages.append(f"Parameter '{key}' not supported. Did you mean 'name'?")
else:
error_messages.append(f"Invalid parameter: '{key}'")
error_msg = "; ".join(error_messages)
error_msg += f". Allowed parameters: {sorted(allowed_keys)}"
raise ValueError(error_msg)
# Validate parameter types
# Note: stroke can be various types in some contexts, so we're less strict
string_params = ['name', 'hindi', 'ipa', 'eng_trans', 'stroke_nickname']
for param in string_params:
if param in opts and opts[param] is not None and not isinstance(opts[param], str):
raise TypeError(f"Parameter '{param}' must be a string, got {type(opts[param]).__name__}")
# Validate that name is not empty if provided
if 'name' in opts and opts['name'] == "":
raise ValueError("Parameter 'name' cannot be empty")
[docs]
@staticmethod
def from_json(obj: Dict) -> 'Articulation':
return Articulation(obj)
[docs]
def to_json(self) -> Dict:
out = {}
if hasattr(self, 'name'):
out['name'] = self.name
if hasattr(self, 'stroke'):
out['stroke'] = self.stroke
if hasattr(self, 'hindi'):
out['hindi'] = self.hindi
if hasattr(self, 'ipa'):
out['ipa'] = self.ipa
if hasattr(self, 'eng_trans'):
out['engTrans'] = self.eng_trans
if hasattr(self, 'stroke_nickname'):
out['strokeNickname'] = self.stroke_nickname
return out
def __eq__(self, other: object) -> bool:
if not isinstance(other, Articulation):
return False
return self.to_json() == other.to_json()