Skip to content

lei: compact() upper-cases before validation, so is_valid() accepts non-conforming lower case values #509

Description

@fabio-rovai

For most identifiers stdnum handles, case and separators are presentation, so compacting before validation is clearly right. For the LEI I think there is a narrow case where it has a consequence worth documenting, and I wanted to raise it here before writing about it anywhere else.

ISO 17442-1:2020 defines the LEI as 20 characters drawn from upper case A-Z and 0-9. The character repertoire is normative rather than a rendering convention, so a lower case value is not an alternative presentation of an LEI, it is a value that does not conform to the standard.

stdnum.lei follows the library-wide convention of compact() then validate, and compact() calls .upper(), so:

>>> from stdnum import lei
>>> lei.is_valid('5493000mn7xn3bbkce67')
True
>>> lei.is_valid(' 506700GE1G29325QX363')
True
>>> lei.validate('5493000mn7xn3bbkce67')
'5493000MN7XN3BBKCE67'

For the use stdnum is designed around, validating something a person typed or a form submitted, that is the behaviour you want. Where it bites is the opposite direction, auditing values a register has already published, because there the deviation is the thing being measured. I ran into this while checking LEI values republished by public registers: lower case LEI values do occur in published US bank register data, and a compacting validator counts every one of them as conforming, so the defect count comes back as zero. I had the same bug in my own code, which is how I noticed.

I am not proposing a behaviour change. compact() before validate() is the documented contract across the whole library and changing it would break a great deal of downstream code for no good reason.

Two smaller things that might be worth doing:

  1. The docstring for compact() in stdnum/lei.py says it "strips the number of any valid separators and removes surrounding white space", but the implementation is clean(number, ' -').strip().upper(). The upper-casing is the part with normative consequences for this particular identifier and it is not currently mentioned.

  2. A sentence in the module docstring noting that is_valid() accepts lower case and separator-formatted input, so callers auditing published data rather than validating input should compare against the raw value. Happy to send a PR for either or both if that would be useful.

For transparency, I have written this distinction up as part of a small ISO 17442 conformance package at https://github.com/fabio-rovai/iso17442, which cites stdnum as correct for its intended use. If you think I have mischaracterised anything there I would rather fix it than leave it standing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions