spain national id is a string composed by 8 numbers plus a letter the letter in fact is not part of the id it acts as a validator checking you didn t do a mistake when entering it on a system or are giving a fake one https en wikipedia orgwikidocumentonacionaldeidentidadspainnumber isspainnationalid12345678z true isspainnationalid12345678z it is caseinsensitive true isspainnationalid12345678x false isspainnationalid12345678i false isspainnationalid12345678z some systems add a dash true isspainnationalid12345678 traceback most recent call last valueerror input must be a string of 8 numbers plus letter isspainnationalid123456709 traceback most recent call last valueerror input must be a string of 8 numbers plus letter isspainnationalid1234567z traceback most recent call last valueerror input must be a string of 8 numbers plus letter isspainnationalid1234z traceback most recent call last valueerror input must be a string of 8 numbers plus letter isspainnationalid1234zzzz traceback most recent call last valueerror input must be a string of 8 numbers plus letter isspainnationalid12345678 traceback most recent call last typeerror expected string as input found int spain national id is a string composed by 8 numbers plus a letter the letter in fact is not part of the id it acts as a validator checking you didn t do a mistake when entering it on a system or are giving a fake one https en wikipedia org wiki documento_nacional_de_identidad_ spain number is_spain_national_id 12345678z true is_spain_national_id 12345678z it is case insensitive true is_spain_national_id 12345678x false is_spain_national_id 12345678i false is_spain_national_id 12345678 z some systems add a dash true is_spain_national_id 12345678 traceback most recent call last valueerror input must be a string of 8 numbers plus letter is_spain_national_id 123456709 traceback most recent call last valueerror input must be a string of 8 numbers plus letter is_spain_national_id 1234567 z traceback most recent call last valueerror input must be a string of 8 numbers plus letter is_spain_national_id 1234z traceback most recent call last valueerror input must be a string of 8 numbers plus letter is_spain_national_id 1234zzzz traceback most recent call last valueerror input must be a string of 8 numbers plus letter is_spain_national_id 12345678 traceback most recent call last typeerror expected string as input found int
NUMBERS_PLUS_LETTER = "Input must be a string of 8 numbers plus letter"
LOOKUP_LETTERS = "TRWAGMYFPDXBNJZSQVHLCKE"


def is_spain_national_id(spanish_id: str) -> bool:
    

    if not isinstance(spanish_id, str):
        msg = f"Expected string as input, found {type(spanish_id).__name__}"
        raise TypeError(msg)

    spanish_id_clean = spanish_id.replace("-", "").upper()
    if len(spanish_id_clean) != 9:
        raise ValueError(NUMBERS_PLUS_LETTER)

    try:
        number = int(spanish_id_clean[0:8])
        letter = spanish_id_clean[8]
    except ValueError as ex:
        raise ValueError(NUMBERS_PLUS_LETTER) from ex

    if letter.isdigit():
        raise ValueError(NUMBERS_PLUS_LETTER)

    return letter == LOOKUP_LETTERS[number % 23]


if __name__ == "__main__":
    import doctest

    doctest.testmod()