diff --git a/src/validators/finance.py b/src/validators/finance.py index 9df5a970..2f5e71e6 100644 --- a/src/validators/finance.py +++ b/src/validators/finance.py @@ -32,21 +32,27 @@ def _cusip_checksum(cusip: str): def _isin_checksum(value: str): - check, val = 0, None + if not (value[:2].isalpha() and value[-1].isdigit()): + return False - for idx in range(12): - c = value[idx] - if c >= "0" and c <= "9" and idx > 1: - val = ord(c) - ord("0") - elif c >= "A" and c <= "Z": - val = 10 + ord(c) - ord("A") - elif c >= "a" and c <= "z": - val = 10 + ord(c) - ord("a") + # Expand letters to numbers (A=10, ..., Z=35), then run the Luhn algorithm. + digits = "" + for char in value: + if char.isdigit(): + digits += char + elif char.isalpha(): + digits += str(10 + ord(char.upper()) - ord("A")) else: return False + check = 0 + for idx, digit in enumerate(reversed(digits)): + val = int(digit) if idx & 1: - val += val + val *= 2 + if val > 9: + val -= 9 + check += val return (check % 10) == 0 @@ -82,8 +88,8 @@ def isin(value: str): [1]: https://en.wikipedia.org/wiki/International_Securities_Identification_Number Examples: - >>> isin('037833DP2') - ValidationError(func=isin, args={'value': '037833DP2'}) + >>> isin('US0378331005') + True >>> isin('037833DP3') ValidationError(func=isin, args={'value': '037833DP3'}) diff --git a/tests/test_finance.py b/tests/test_finance.py index a40fd333..90cae2be 100644 --- a/tests/test_finance.py +++ b/tests/test_finance.py @@ -24,13 +24,13 @@ def test_returns_failed_validation_on_invalid_cusip(value: str): # ==> ISIN <== # -@pytest.mark.parametrize("value", ["US0004026250", "JP000K0VF054", "US0378331005"]) +@pytest.mark.parametrize("value", ["US0004026250", "JP000K0VF055", "US0378331005"]) def test_returns_true_on_valid_isin(value: str): """Test returns true on valid isin.""" assert isin(value) -@pytest.mark.parametrize("value", ["010378331005", "XCVF", "00^^^1234", "A000009"]) +@pytest.mark.parametrize("value", ["010378331005", "XCVF", "00^^^1234", "A000009", "US0378331004"]) def test_returns_failed_validation_on_invalid_isin(value: str): """Test returns failed validation on invalid isin.""" assert isinstance(isin(value), ValidationError)