Source code for jangle.lite
from __future__ import annotations
import re
from dataclasses import dataclass, field
from typing import Optional
from jangle.patterns import RULES, match_rule
from jangle.utils import StrReprCls, split_subtags
[docs]@dataclass(repr=False)
class Extension(StrReprCls):
singleton: str
texts: list[str]
[docs] @classmethod
def from_match(cls, match: re.Match) -> Extension:
return cls(
singleton=match.group("singleton"),
texts=split_subtags(match.group("ext_text")),
)
[docs] @classmethod
def from_str(cls, string: str) -> Extension:
return cls.from_match(match_rule("extension", string))
def __str__(self) -> str:
return "-".join([self.singleton, *self.texts])
[docs]@dataclass(repr=False)
class LangTag(StrReprCls):
lang: str
extlang: Optional[str] = None
script: Optional[str] = None
region: Optional[str] = None
variants: list[str] = field(default_factory=list)
extensions: list[Extension] = field(default_factory=list)
private: Optional[str] = None
[docs] @classmethod
def from_groups(cls, groups: dict[str, str]) -> LangTag:
langtag = cls(
lang=groups["iso_639"].lower(),
extlang=groups["extlang"].lower() or None,
script=groups["script"].title() or None,
region=groups["region"].upper() or None,
private=groups["private_subtag"].lower().removeprefix("x-")
or None,
)
if groups["variants"]:
langtag.variants = split_subtags(groups["variants"].lower())
langtag.extensions = list(
map(
Extension.from_match,
RULES["extension"].finditer(groups["extensions"].lower()),
)
)
return langtag
[docs] @classmethod
def from_match(cls, match: re.Match[str]) -> LangTag:
return cls.from_groups(match.groupdict(""))
[docs] @classmethod
def from_str(cls, string: str) -> LangTag:
return cls.from_match(match_rule("langtag", string))
def __str__(self) -> str:
subtags = [self.lang]
subtags.extend(
filter(
None,
[
self.extlang,
self.script,
self.region,
],
)
)
subtags.extend(self.variants)
subtags.extend(map(str, self.extensions))
if self.private:
subtags.extend(["x", self.private])
return "-".join(subtags)
def __contains__(self, val: LangTag | re.Match[str] | str) -> bool:
if isinstance(val, re.Match):
val = self.__class__.from_match(val)
elif isinstance(val, str):
val = self.__class__.from_str(val)
if val.lang != self.lang:
return False
for attr_name in ["script", "extlang", "region", "private"]:
attr = getattr(val, attr_name)
if attr and attr != getattr(self, attr_name):
return False
for variant in val.variants:
if variant not in self.variants:
return False
self_extensions = set(map(str, self.extensions))
for extension in val.extensions:
if str(extension) not in self_extensions:
return False
return True
[docs]@dataclass(repr=False)
class LanguageTag(StrReprCls):
langtag: Optional[LangTag]
private: Optional[str]
grandfathered: Optional[str]
[docs] @classmethod
def from_match(cls, match: re.Match) -> LanguageTag:
groups = match.groupdict("")
lite = cls(
langtag=None,
private=groups["private_tag"].lower().removeprefix("x-") or None,
grandfathered=groups["grandfathered"].lower() or None,
)
if groups["langtag"]:
lite.langtag = LangTag.from_groups(groups)
return lite
[docs] @classmethod
def from_str(cls, string: str) -> LanguageTag:
return cls.from_match(match_rule("Language-Tag", string))
def __str__(self) -> str:
if self.grandfathered:
return self.grandfathered
if self.private:
return "-".join(["x", self.private])
return str(self.langtag)