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:
-
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.
-
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.
For most identifiers
stdnumhandles, 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-Zand0-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.leifollows the library-wide convention ofcompact()then validate, andcompact()calls.upper(), so:For the use
stdnumis 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()beforevalidate()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:
The docstring for
compact()instdnum/lei.pysays it "strips the number of any valid separators and removes surrounding white space", but the implementation isclean(number, ' -').strip().upper(). The upper-casing is the part with normative consequences for this particular identifier and it is not currently mentioned.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
stdnumas correct for its intended use. If you think I have mischaracterised anything there I would rather fix it than leave it standing.