1,880 bytes · the Python implementation · view raw
from .validation_lei_types import LeiCheck
def _invalid(reason: str) -> LeiCheck:
return LeiCheck(valid=False, normalised=None, lou_prefix=None, reason=reason)
def _is_digit(ch: str) -> bool:
return"0" <= ch <= "9"def _mod97(lei: str) -> int:
"""ISO 7064 MOD 97-10 over the whole LEI, letters read as A = 10 ... Z = 35. Python could hold the 40-digit expansion as one integer, but the remainder is carried one character at a time so this is the same algorithm as the Rust sibling, where it cannot. A letter contributes two digits and so multiplies the running remainder by 100. """
remainder = 0for ch in lei:
if _is_digit(ch):
remainder = (remainder * 10 + (ord(ch) - 48)) % 97else:
remainder = (remainder * 100 + (ord(ch) - 55)) % 97return remainder
def validate_lei(value: str) -> LeiCheck:
"""Check a Legal Entity Identifier's format and ISO 17442 check digits. Well formed is not issued: only GLEIF says whether an LEI is current. Validators answer rather than raise. """ifnot isinstance(value, str):
return _invalid("empty")
# Folded by hand: str.upper() is Unicode-aware and Rust's ASCII fold is not.
lei = "".join(chr(ord(ch) - 32) if"a" <= ch <= "z"else ch for ch in value if ch != " ")
ifnot lei:
return _invalid("empty")
if any(not (_is_digit(ch) or"A" <= ch <= "Z") for ch in lei):
return _invalid("bad-character")
if len(lei) != 20:
return _invalid("bad-length")
ifnot _is_digit(lei[18]) ornot _is_digit(lei[19]):
return _invalid("bad-format")
# The remainder must be 1, not 0: the check digits are chosen so that it is.if _mod97(lei) != 1:
return _invalid("bad-check-digit")
return LeiCheck(valid=True, normalised=lei, lou_prefix=lei[:4], reason=None)