Repository navigation
Releases: identique/idnumbers
Release list
Release 1.21.0
Validity changes
- CAN (#635):
SocialInsuranceNumber.validate()rejects
prefixes 0 and 8 under the standard SIN rules, excluding the CESP-specific exception for primary-caregiver SINs
beginning with 0.checksum()remains an arithmetic-only Luhn check.
Added
- LUX entity TIN (#272): add
LUX.EntityTIN, the 11-digit
national identifier that is used as a TIN-like number for non-natural persons, with the weighted mod 11 check digit
from the OECD Luxembourg TIN sheet; numbers whose remainder is 1 are rejected because it is never allocated.LUX.TIN
groupsindividual(LUX.NationalID) andentity(LUX.EntityTIN).LUX.NationalIDis unchanged. - DEU tax number (#130): add
DEU.TaxNumber(alias
DEU.Steuernummer), the Steuernummer in the 13-digit ELSTER format. It checks the state number, the district rules
and the state-specific check digit (11er-Verfahren, 2er-Verfahren or the modified 11er-Verfahren of
Rhineland-Palatinate, with the Berlin-A and Berlin-B weights) from the ELSTER document "Prüfung der Steuer- und
Steueridentifikationsnummer", andparse()returns the state, tax office, district and distinguishing number. The
format printed on tax notices is not accepted.DEU.NationalIDis unchanged.
Other fixes
- API docs deployment (#644): the
apidocworkflow queues its
deploys in a concurrency group (apidoc-devfor pushes tomain,apidoc-<version>for a release) and rebases and
retries a rejectedgh-pagespush, so back-to-back pushes tomainno longer fail the docs deploy and a push to
mainnever cancels a release's docs deploy. - FRA INSEE (#637):
INSEE.parse()(and
FRA.NationalID.parse()) returnsNonefor a number with a wrong control key, whichvalidate()already rejected.
Valid numbers parse as before. - NOR (#637):
NationalID.parse()returnsNonefor a number
with wrong check digits, whichvalidate()already rejected.validate()results andidnumbers.parse_id_info()
are unchanged. - Parity corpus (#636): vendor the Node port's parity corpus
at port commit782680d, which adds CRI, DOM, ECU, EGY, GTM, RUS and SAU (85 countries, 20,672 vectors), and record
Python's validity for them, so a validity change in those seven countries now failstests/parity/test_corpus.py.
Python's recorded validity for the other 78 countries is unchanged.
Release 1.20.0
Validity changes
- BRA (#617): RG checksum validation treats
Xas value 10,
rejecting it when the weighted sum has remainder 0 modulo 11 and accepting it when the remainder is 1. Numeric
check-digit behavior is unchanged; remainder 0 still accepts0. - UKR (#616): taxpayer ID numbers with encoded birth dates
after the local current date now returnFalsefromvalidate()andNonefromparse(). Today's date remains
accepted, and checksum computation and parsed fields for past dates are unchanged.
Changed
- Coverage badge (#600): the README shows the line and branch
coverage from a shields.io endpoint JSON that CI publishes to thebadgesbranch on pushes tomain, using
GITHUB_TOKENwith no repository secrets or external service. Pull request CI and the 97% floor are unchanged. - Coverage badge hardening (#611): CI tests pin the workflow
triggers topushandpull_request, the coverage checkout does not persist credentials, and badge publication
skips stale main commits after checking the remote head. The endpoint JSON, coverage metric and floor are unchanged. - Formatting (#599): the Python sources are formatted with
ruff format(ruff 0.16.10, 120 columns; Markdown is left alone) and CI checks them withruff format --check.
.git-blame-ignore-revslists the reformat commit, sogit blameskips it. Code and behaviour are unchanged. This
also settles the quote style of the README example tests
(#561).
Other fixes
- Checksums (#619): every public
checksum()returns
FalseorNonefor non-string input instead of raising or computing a value. HKG and TUR computed checksums
now returnOptional[str]. All string-input behavior, including malformed strings, is unchanged. - Formatting (#618):
normalize_id()andformat_id()
uppercase ASCII letters only, leaving non-ASCII letters (including lowercase Greek letters) unchanged instead of
folding lookalikes or expanding ligatures into ASCII. Helper outputs change; country validation is unchanged. - LKA (#615): old NIC suffixes
V/v/X/xhistorically
indicated voter/nonvoter status, not citizenship.OldNationalID.parse()keeps thecitizenshipkey but returns
Noneinstead of citizen/resident status, with typeOptional[Citizenship]. Validation, checksum, conversion and
other parsed fields are unchanged.
Documentation
- Contributor checklists (#608): explicitly name both ruff
lint and formatting checks. CI contract tests tolerate extra formatter exclusions and reordered jobs while
retaining readable failures for missing checks. Clarify checksum-group coverage, line wrapping and the first
trusted release wording; CI policy and library behaviour are unchanged. - Contributor guide (#598): the line-length rule in
AGENTS.mdnow says that every line of a reflowed paragraph counts as changed, and that only URLs are exempt: other
long string literals and citations are wrapped. Code and behaviour are unchanged. - Country template (#595): the checksum group item names the
check character(s) and covers achecksum()that returns a derived value (the tax file number's remainder): the group
stays over the check character, and the class needs the named exemption inTestComputedChecksumGroups. The BGR
13-digit test comment now names digits 9 to 12. Code and behaviour are unchanged. - Input masks (#620): clarify that masks describe preferred
shapes, not fixed letter prefixes, ID validity or every accepted separator variant. Tests check accepted mask
representatives directly against each class's regexp as well as its validator. Metadata and helper outputs are
unchanged. - Release procedure (#603):
RELEASE.mddates the closure
of #327 (2026-10-12) and says that releases since 1.16.0 have
run the updated workflow runtimes, with 1.18.0 the first trusted publication. The procedure is unchanged.
Release 1.19.0
1.19.0 (2026-10-12)
Documentation
- Contributor guide (#581): the 120-column rule in
AGENTS.mdnow applies to new and changed lines, and older long lines are not swept. A URL may also stay whole as a
single string literal, such as an entry ofMETADATA.links. Code and behavior are unchanged. - Failure reasons (#578): the country template's checklist
now asks for achecksumgroup inMETADATA.regexpwheneverchecksum()returns the check digit or character, so
failure_reason()can reportchecksum_mismatch; aboolchecksum()needs no group. The BGR Unified
Identification Code note indocs/FAILURE_REASONS.mdnow says that a 13-digit code whose first nine digits fail the
9-digit check but whose 13th digit is correct staysvalidation_failed, because the group covers only the 13th
digit. Validity and diagnostics are unchanged.
Release 1.18.0
1.18.0 (2026-10-12)
Changed
- Failure reasons (#506):
METADATA.regexpof 14
computed-checksum classes (AUS Medicare Number and Tax File Number, BGR Unified Identification Code, CHL National
Unique Role Number, COL Unique Personal ID, GRC Tax Identity Number, HKG Hong Kong Identity Card Number, IRN National
ID Number, ISR Identity Number, JPN My Number, LVA Personal Code, TUR National ID Number, UKR Legal Entity ID Number
and Taxpayer ID Number) now names the check character(s) with achecksumgroup, sofailure_reason()reports
checksum_mismatchinstead ofvalidation_failedfor a definite check-character mismatch. The unified
validate()andparse_id_info()report it too for the nine classes that are their country's primary ID (CHL, COL,
HKG, IRN, ISR, JPN, LVA, TUR and the UKR Taxpayer ID Number); they select only the primary ID, so the other five
(AUS Medicare Number and Tax File Number, BGR Unified Identification Code, GRC Tax Identity Number and UKR Legal
Entity ID Number) are reached throughfailure_reason(id_class, number). Accepted and rejected inputs, parsed
results andchecksum()values are unchanged. The Australian tax file number'schecksum()returns a remainder
rather than a check digit, so a remainder equal to the last digit keeps the generic reason.
Other fixes
- Parity sync script (#564): construct failed-rollback reports
before bytecode cleanup and test that a failed restore afterKeyboardInterruptorSystemExitreports both errors
and the tracked paths with exit code 2; successful rollback still propagates the original interrupt. - Registry tests (#566): exercise every slow key lookup path
in the concurrency helper and check the portable non-iterable alias error fragment without changing registry behavior. - Release tests (#559): cover rejection of a bare
mismatched-version dist-info entry and an absolute package path in wheels; release guard behavior is unchanged.
Documentation
- Contributor guide (#551): AGENTS.md documents the
120-column line-length convention and its exceptions (whole URLs on their own line, Markdown tables and generated
guides); the 1.17.0 DOM entry now uses python-stdnum's own wording. - Contributor guide (#561): specify that the re-export
exemption applies tofrom .util import ..., notfrom . import util; only the rule's wording changes. - Display format (#557): clarify that capital-letter field
names are outside{...}andDusually denotes a day, with widths and meanings defined per class; formats and
validity are unchanged. - EGY national ID number (#555): distinguish the Node
sister project's reported Luhn failure from its unproven weighted mod-11 hypothesis in the docstring; the research
note is not an official source, and validity is unchanged. - Mask vocabulary (#570): explain Sweden's retained
+
exception to slot-count matching, with a synthetic layout example; preferred input-mask patterns still use literal
-, and formatting behavior is unchanged. - Unified API tests (#568): document the hostile
representation helpers with class docstrings; test behavior is unchanged.
Release 1.17.0
Other fixes
- Metadata follow-up (#540): independently pin metadata
discovery counts, approved display formats and official names in tests; report JSON scans of modules without ID
classes as clean CLI usage errors. Clarify length-only formatting, refresh Cyprus government/OECD source links
and use an official Swedish test personal number in formatting tests. Country validation behaviour is unchanged. - Packaging (#529): the release check now also rejects a
wheel entry that is a bare file namedidnumbersoridnumbers-<version>.dist-inforather than a path inside that
directory; the published wheel is unchanged. - Parity sync script (#533): when
--from-portfails and
restoring the tracked files also fails, both errors are reported and the possibly half-updated files are named;
tests now coverdrop_bytecode()and keep the non-git port case from finding an enclosing repository. - Unified API (#538):
validate(),validate_many()and
parse_id_info()no longer raise when a country'srepr()returns astrsubclass whose length or slicing raises;
the README describes theerror_messagefallbacks.
Documentation
- CRI cédula de identidad (#515): correct the example's
provenance to the Node port and add tests for compact, fully hyphenated, mixed and malformed hyphen layouts;
validity and parsed results are unchanged. - DOM cédula de identidad y electoral (#519): state the
exact size of python-stdnum's list of issued numbers that fail Luhn (578 entries: 576 with 11 digits, 2 with 10
digits) and quote its own description of them; validity is unchanged. - ECU cédula de identidad (#521): drop the unsourced RUC
remark, describe the Gob.EC issue on the third digit 6 as reported validation failures, and cite python-stdnum's
actual hyphenated example171430710-3; validity is unchanged. - EGY national ID number (#523): attribute the Luhn and
mod-11 check-digit claims to third-party sources and cite the Node port's research note; the failure-reason guide
now calls its date witnesses impossible-date witnesses, since not every one has a checksum; validity is unchanged. - GTM, SAU (#527): define the
display_format
placeholder letters in the class docstrings, and explain indocs/nationalid/METADATA.mdthat letters other than
#(and, in a few classes,L) name per-class fields; formats are unchanged. - Narrow the AGENTS.md re-export rule to the single-dot relative imports that
tests/test_explicit_reexports.py
checks, and unit-test the checker (#531). - Say in the
register()docstring that a non-iterablealiasesraisesTypeError, and make the registry
concurrency tests fail when the registration lock is missing
(#535). - Unified API (#543): render builtin
TypeErrornames as
literals in result-class hash documentation, avoiding unresolved API documentation links; behaviour is unchanged.
Release 1.16.0
Added
-
CRI cédula de identidad (#329): add the 9-digit Costa Rica
identity card numberP-TTTT-AAAA(province 1-9, tomo, asiento), with optional hyphens and no check digit;
parse()returns the province, its name, the tomo and the asiento. python-stdnum's 10-digit0P-TTTT-AAAAform is
not accepted. -
DOM cédula de identidad y electoral (#330): add the
11-digit Dominican Republic identity and electoral card numberNNN-NNNNNNN-Cwith optional hyphens and a Luhn
check digit;parse()returns the series, the sequence and the check digit. python-stdnum's list of issued numbers
that fail Luhn is not accepted (#517). -
ECU cédula de identidad (#331): add the 10-digit Ecuador
identity card number: province 01-24 or 30 (registered abroad), a third digit 0-6 (4 or 5 for province 30) and a
mod-10 check digit, with an optional hyphen before the check digit;parse()returns the province, the sequence
number and the check digit. -
EGY national ID number (#332): add the 14-digit Egypt
national numberCYYMMDDGGSSSSV(Executive Regulations of the Civil Status Law, Art. 3): century 2 or 3 and a real
birth date are validated; the governorate code, the serial and the final digit are not (no official code table or
check-digit algorithm is published).parse()returns the birth date, the governorate code, the serial and the
final digit. -
GTM Código Único de Identificación / DPI (#333): add
structure-only validation of compact 13-digit CUI numbers (department 01-22, municipality 01-99).parse()
preserves the eight-digit correlative, unverified check digit, department and municipality as strings. No
checksum or per-department municipality table is enforced; whitespace and separators are rejected. -
RUS internal passport (#334): add structure-only
validation of a four-digit series and six-digit number, rejecting all-zero fields by maintainer policy.
Accept only compact ASCII digits,SSSS NNNNNNor printedSS SS NNNNNNwith single ASCII spaces;
parse()preserves series and number as strings. No checksum or issuance/authenticity check is performed. -
SAU national ID / Iqama (#335): add structure-only
validation of the 10-digit Saudi Arabia national ID and resident identity (Iqama) number: the first digit is 1
(citizen) or 2 (resident), per the NPHIES Patient profile.parse()returns the citizenship. No check digit is
verified, because the Luhn rule is unconfirmed by any authoritative source; only compact ASCII digits are
accepted.
Changed
- Release workflows (#468): update the version-bump and
documentation actions to Node.js 24 runtimes and pin Ubuntu 24.04, preserving the bot PR and docs deployment
contracts and the existing release authentication routes. - Metadata (#501): the official names of COL (NUIP), CYP
(ΑΦΤ) and IRN (شماره ملی) are corrected against official sources.display_formatnow uses[...]for an optional
part and{...}for exactly one of the listed characters, and keeps parentheses as printed characters (BGR, HKG,
IRL, AUS Medicare and FIN).docs/nationalid/METADATA.mddocuments both, and thatmasksare the preferred layouts
the class accepts, not necessarily every layout it accepts. - Packaging (#484): the wheel no longer installs the
repository's top-leveltoolspackage or the<prefix>/version/VERSIONdata file; it now ships only the
idnumberspackage. The source distribution is unchanged, and the library API is unaffected. The release check
rejects a wheel with any other top-level entry.
Other fixes
- Tests (#503): treat an empty Hypothesis storage override
as unset, keeping ancillary caches in temporary storage while preserving the original environment value. - Registry (#496):
register()no longer drops aliases
passed as a one-shot iterable such as a generator, andaliasesis now typedIterable[str];resolve_country(),
and soget_country()andget_validator(), no longer raises for astrsubclass that overridesisascii()or
upper()or for an object that fakes its__class__;register()rejects a blank name and gives a missing
alpha-2 code its own error message. - Scan tool (#501):
tools.scan_ids.collect_ids()lists only
the ID classes defined in itspackage_nameargument, which it used to ignore, and raisesValueErrorwhen the
package defines none. The defaultidnumbers.nationalidoutput is unchanged. - Unified API (#499):
validate()andparse_id_info()no
longer raise when the country'srepr()raises or when the exception of a validator or parser can't be turned into
text, and the unsupported-country message shortens a long country value to 80 characters. TheValidationResult
andParseSuccessdocs say that a result with parsed data isn't hashable.
Documentation
Release 1.15.0
Added
-
Add generated country metadata, failure-reason and input-format guides, executed and strictly typed documentation
examples, and contributor/country workflow templates
(#328). -
Add primary-only
normalize_id(),format_id()andget_input_mask()with frozenInputMaskresults.
Metadata-driven formatting checks length, not validity; input patterns use ASCII uppercase letters/digits.
Swedish formatting preserves a significant+in both 10-digit and 12-digit forms; preferred mask patterns
remain literal minus layouts. Greece's primary metadata example now uses already-accepted Latin letters;
Greek validation and formatting remain unchanged (#321). -
Add
failure_reason(id_class, id_number)for primary and secondary ID types. Unified validation and parsing now
report best-effort length, format, checksum and embedded-calendar-date reasons; inconclusive checks and exceptions
retainVALIDATION_FAILED. Date tracing is isolated between nested calls, threads and async tasks, and country
validity, parser and checksum behavior is unchanged (#322). -
Add
idnumbers.parse_id_info(country, id_number)for a country's primaryNationalID, returning the frozen
ParseSuccess/ParseFailureunionParseIdInfoResult. Theokdiscriminator supports strict type narrowing;
success includes a fresh dictionary of the country parser's information. Lookup and validation failures match
validate(), while valid IDs without extractable information giveNOT_PARSABLE. Country parsers are unchanged
(#319). -
Add a country registry,
idnumbers.registry, which is re-exported fromidnumbers.get_validator('tw'),
get_country('TWN')andresolve_country('tw')find theNationalIDclass, aCountryEntry(alpha-3 and alpha-2
code, name,NationalIDand all ID types) or the alpha-3 code of a country by its alpha-2 or alpha-3 code in any
letter case, and giveNonefor anything unknown instead of raising.list_supported_countries()lists the 78
countries, andregister()adds a custom country. The names are the English names of Unicode CLDR 48.2.3. AUS and
GRC keep their currentNationalID, and a country module is imported on the first lookup of its country
(#318). -
Add
idnumbers.validate(country, id_number), which validates an ID number of any supported country and returns a
frozenValidationResultwithis_valid, the alpha-3country_code, theid_number, theextracted_infoof a
valid ID number (theparse()result,Nonewhen the ID type isn't parsable), areasonand anerror_message. It
never raises, and a result is truthy when it is valid. An unknown country gives the reasonunsupported_country,
other failures a best-effort diagnostic reason.validate_many()validates(country, id_number)pairs, and
FailureReasonlists the reasons, which later releases may extend. The country classes are unchanged
(#317). -
Describe every ID class with seven more
METADATAkeys:country_name,id_type,official_name(Nonefor an
ID with no local-language name),display_format,example(a synthetic number the class validates),
checksum_algorithm(Nonewithout a check digit) andmasks, the layouts the class accepts.METADATAis now an
IdMetadata, a typedSimpleNamespacesubclass inidnumbers.nationalid.metadata, so attribute access,vars(),
copy()andgetattr()work as before. A class made byalias_of, such asAUS.NationalID, now has the name,
module and docstring of its target instead ofAliasType, which changes itsrepr.CAN.SocialInsuranceNumber
gets thedeprecatedkey the other classes already had, andtools.scan_idslists the classes through the country
registry (#320).
Changed
-
Ship the PEP 561
py.typedmarker, so type checkers use the package's annotations, and correct the annotations
that disagreed with the runtime values: theCHECK_DIGITalias, the untypednormalizeandchecksumhelpers,
the COL and KAZchecksummethods that returnFalsefor malformed input, the AUS TFN and KWT civil-number
checksummethods, which can return 10, and the LTUextract_year_base_genderresult, which is neverNone
(#325). -
Move the build metadata from
setup.pyto a PEP 621pyproject.toml. The license is now the SPDX expressionMIT
instead of the deprecated license classifier (#465), and
building from source needs setuptools 77 or later, which pip and uv install automatically
(#325). -
CI type-checks the package with strict mypy for Python 3.9 and checks the built distributions with
twine check --strict(#325). -
Re-export the ID classes explicitly (
import X as X) from the country modules such asAUS, so strict type
checkers (mypy--strict, which disallows implicit re-exports) accept
from idnumbers.nationalid.AUS import MedicareNumberandAUS.MedicareNumber; nothing changes at runtime
(#326). -
Prepare isolated, guarded PyPI publication and opt-in Trusted Publishing with attestations; retain the working
default token route and existing secrets. External publisher setup and the first verified trusted release remain
pending maintainer action. Document release ordering and 1.14.0-to-1.15.0 migration
(#327). -
Add dev-only Hypothesis checks of arbitrary Unicode inputs for every registered ID type, metadata method
contracts, out-of-range lengths and checksum mutations, with explicit synthetic witnesses for checksum limits
(#324). -
CI lints with ruff, enforces a 97% line-and-branch coverage floor, runs the README Python examples and
informational benchmarks, keeps GitHub Actions updated with Dependabot, and reports a singleci-summaryresult
(#326). Formatting withruff formatand a coverage badge
await a decision in #485. -
Vendor the Node port's parity corpus (78 countries, 17,960 vectors) at a pinned port commit and record Python's own
validity for it, so a validity change on any shared vector fails a test and gets a port note;
scripts/sync_parity_corpus.pyrefreshes both (#323).
Release 1.14.0
Documentation
-
Add an optional uv contributor quickstart with editable installation and full-suite or single-country tests
(#473). -
Check published PyPI packages with uv on Python 3.9 and 3.14 on demand, and document the post-release check
(#472). -
Add a runnable uv script and isolated wheel-install CI checks for the script and README one-liner
(#471). -
Document project, virtual-environment, one-off and standalone-script usage with uv in the README
(#470).
Release 1.13.0
1.13.0 (2026-10-07)
Validity changes
-
ALB identity number (#344): decoded birth dates after today
are rejected, using the date rather than a fixed decade cap. The existing historical year range is unchanged,
pending confirmation in #446. -
ARG DNI (#280): 7-digit numbers (below 10 million), such as
5.123.456and5123456, are now accepted.METADATA.min_lengthis 7. -
AUT entity UID (#345): require the
Uprefix;
other letters are rejected byvalidate()and the input guard inchecksum(), even with a correct
check digit. The checksum arithmetic and supported separators are unchanged. -
BRA CPF (#284): numbers made of one repeated digit,
000.000.000-00to999.999.999-99, are now rejected. They pass the check digits, but the federal civil-registry
system SIRC lists repeated digits as a reason for an invalid CPF.checksum()is unchanged. -
CHN resident ID (#347): accept ASCII lowercase
xas the
final check character; parsed and calculated check characters remain canonicalXor integers. Checksum arithmetic,
calendar validation and region policy are unchanged; region/issuance checks remain deferred to
#451. -
CYP tax number (#348): prefix
12is rejected;
checksum()remains unchanged. -
DEU tax ID (IdNr) (#289): the digit rules now follow the
ELSTER specification. A leading0is rejected, which includes ELSTER test numbers such as02476291358. Among the
first ten digits, exactly one digit must appear twice or three times, so39825979193(four 9s) and36794081522
(no repeated digit) are rejected. A check digit equal to the two digits before it is no longer rejected, so
38056471999is now valid. -
DNK CPR number (#290): the century now comes from the
seventh digit and the two-digit year, as the CPR office's table defines it, instead ofyy > 50.parse()reads
0101451234as 1945-01-01 instead of 2045-01-01, and 1858-1899 births are recognised.2902000000(29 February 1900)
is now rejected and2902004000(29 February 2000) is valid. A birth date in the future is rejected. -
ESP DNI (#349):
validate()andchecksum()now accept
ASCII lowercase check letters and strip surrounding whitespace. Checksum arithmetic and the eight-ASCII-digit
plus ASCII-letter payload format are unchanged; internal whitespace and separators remain invalid. -
FRA NIR (#291): birth department
96is now accepted. It
was used for Tunisia before 1964, like 91-95 for Algeria and Morocco, so145089612304582is now valid. -
IDN NIK (#293): encoded birth days are now validated as
01-31for men and41-71for women. Impossible dates are rejected, while 29 February with year00is
accepted because it exists in 2000 even though it does not exist in 1900; NIK does not identify the century. -
IRL PPS number (#294): the optional second suffix character
is now limited toA,B,H,Wor a space; other letters are rejected even when the check character is correct.
The whitespace restriction was already fixed in #369.
HistoricalT/Xsuffix handling remains out of scope and is tracked in
#433. -
IRN national ID (#295): codes made of one repeated digit,
0000000000to9999999999, are now rejected, including supported hyphenated formats.checksum()is unchanged. -
ISR identity number (#350): reject
000000000even though
it passes Luhn.checksum()is unchanged; the exact nine-digit format remains required. Shorter-number padding
policy is deferred to #455. -
KAZ BIN (#297): registration months outside
01-12are
now rejected, including checksum-correct750061381659(month00).checksum()is unchanged. -
LKA NIC (#299): both old and new formats now reject encoded
birth-day values outside001-366for men and501-866for women, even with a correct checksum.
checksum()and within-range date decoding are unchanged; leap-year-specific decoding remains out of scope in
#437. -
LTU personal code (#300): century decoding now accepts
50002290013(29 February 2000) and rejects70002290015(29 February 2100).checksum()is unchanged.
First-digit policy for0and9remains out of scope in #337. -
LVA personal code (#301): legacy codes now require a valid
calendar date and century digit0/1/2; unsupported prefixes00and40-99are rejected even with a correct
check digit. Date-free modern prefixes32-39remain accepted per current primary sources.checksum()is
unchanged; the modern checksum requirement remains out of scope in
#440. -
MAC identity number (#351): parentheses around the final
digit must be paired; unbalanced forms are rejected. Compact eight-digit and printed parenthesized forms remain
accepted, with unchanged parse payloads and eight-significant-digit length metadata. -
MEX CURP (#352):
validate()andparse()reject
the 81 original inconvenient name prefixes in RENAPO's catalogue. Their second-letterXreplacements remain
accepted when otherwise valid; standalonechecksum()is unchanged. -
NLD BSN (#303): compact nine-digit numbers such as
123456782are now accepted, while the existing fully dotted layout1234.56.782remains accepted. All-zero
numbers remain rejected in both formats; the 11-proof andchecksum()are unchanged. -
NZL (#306): the expanded NHI checksum now uses modulus
23 and check-letter indices 1-23, per HISO 10046:2024; the legacy numeric checksum is unchanged. Driver licence
prefixes now require two ASCII letters, preserving lowercase acceptance and the trailing-digit blacklist. IRD
numbers must be within the current inclusive 10,000,000-200,000,000 range and pass the existing checksum. -
PRT (#353): add
CitizenCardfor compact 12-character
document numbers with the full-card checksum.CivilIDNumberandNationalIDremain BI/NIC-only. NIF now accepts
prefixes74,75,77,78and79; its checksum arithmetic and other prefix rules are unchanged. -
SWE (#311): personnummer now accepts 10- and 12-digit
compact forms and optional-/+separators; explicit four-digit years determine the century. Only the two
century digits are excluded from the checksum. Add separateCoordinationNumbersupport for samordningsnummer,
with encoded birth day +60 and decoded dates inparse().NationalIDremains personnummer-only; existing
10-digit-/+decoding and checksum-calculator behavior are unchanged. -
UKR EDRPOU (#312): a second-pass remainder of
10now
maps to check digit0, so57334830and43089360are accepted. Existing weight selection is unchanged;
the separate selection question remains tracked in #342.
Other fixes
- ALB identity number (#344): the missing metadata comma is
restored, separatingNumri i Identitetit të ShtetasitandNISHinto the intended six names. - BGD national ID (#346):
parse()returnsyyyyas
the declared four-character string, preserving leading zeros. The national ID metadata lengths are corrected to 17;
old national ID metadata remains 13. - BIH (#354): correct the JMBG class country label to
Bosnia and Herzegovina; validation and parsing are unchanged. - CHN resident ID (#419): add regression coverage for
October birth dates, already fixed in #275;
validity is unchanged. - FRA NIR (#291):
parse()now reports gender code7, the
temporary number for a man, as male. Codes3and4are still reported as female, because no source says which sex
they stand for (#429). - GRC (#354): correct new identity-card length metadata to
eight significant characters. Old-card metadata remains seven; option...
Release 1.12.0
The first release since 1.11.0. It fixes validity bugs that were found by comparing the library with its Node.js port
and with python-stdnum. Every fix cites its source in the class docstring or in METADATA.links. Several fixes change
which IDs are accepted, so read the first two sections before you upgrade.
Behaviour changes
validate()never raises any more. It returnsFalsefor non-strinput, for empty input and for malformed input,
andparse()returnsNonefor the same inputs. Before, most types raisedAssertionError,ValueError,
IndexErrororTypeError(#371).- An integer is no longer converted to a string. ALB, ARE, BHR, CHN, EST, ITA, KWT, LKA (old), LTU, POL and VNM used to
turn non-strinput into text withrepr(), soLTU.NationalID.validate(37605030299)wasTrue. It is nowFalse,
like every other type. Convert integers withstr()first (#371). - The whole input must match, with ASCII digits only. A trailing newline, and digits such as Arabic-Indic
١٢٣or
full-width123, used to slip through the patterns and are now rejected
(#369). The Irish PPS number allows only a space after the number,
not other whitespace. A tab or newline there used to raise and now givesFalse
(#369). checksum()has a documented contract for malformed input. Checksums that return a digit or letter returnNone, and
checksums that return aboolreturnFalse, instead of raising or returning a meaningless value:- BGR, BRA (CPF and RG), IRN and JPN:
NoneorFalsefor input that does not match the format. For example,
JPN.MyNumber.checksum('00000000178')was'3'and is nowNone
(#371). - ZAF:
Nonefor input that does not match the ID format (#394). - TWN:
Noneinstead ofFalse(#396,
#414). - CHL:
Noneinstead of raising or returning a meaningless character
(#410). - ZWE:
Falseinstead of raisingAttributeError(#415). - Callers that used
checksum()to compute a check digit from a prefix that does not match the ID format are
affected.
- BGR, BRA (CPF and RG), IRN and JPN:
checksum()results that were wrong are corrected: TWN returns0instead of10
(#396), BGR EGN returns0instead of10
(#375), andutil.ean13_digitnow weights the even positions by 3
as EAN-13 does (#377). CHEBusinessIDgains achecksum()method
(#377).parse()returnsNonefor IDs that are valid but have no complete birth date or no check digit to report: BEL
numbers with an unknown month or day (#373), and CZE and SVK
9-digit birth numbers (#384).ParseResultis unchanged. BEL
numbers withyy50 now parse to 1950, not 2050 (#373).- Some types now accept or reject whole groups of IDs. The largest shifts are GEO (11 digits instead of 9,
#400), ZWE (a new check letter,
#398), CHL 7-digit RUTs
(#382), CHE AHV numbers
(#377) and TWN
(#396). See "Validity changes".
Validity changes
Each bullet says what is accepted now and what is rejected now. Vectors are taken from the pull requests, and most of
them are also tests.
- BEL national registration number (#373):
- The post-2000 check digit was computed with an operator-precedence bug. Real numbers born from 2000, such as
01010100126, were rejected, and anyyybelow 50 with check number29was accepted (00010100129, now
rejected). - The century is inferred from the check digit, as the Rijksregister instruction IT000 requires. 1900-1949 numbers
such as47010100190are now valid. A future 20xx birth year is rejected. - For
yy50, validity is unchanged, butparse()now returns 1950 instead of 2050 (50010100156was read as
2050-01-01 and is now 1950-01-01). - A month
00or a day00(an incomplete birth date, for example85003003376) is now valid andparse()returns
None. A day that does not exist in its month, and a month above 12, stay invalid.
- The post-2000 check digit was computed with an operator-precedence bug. Real numbers born from 2000, such as
- BEL entity VAT (#373): a 10-digit number must start with
0or
1. A 9-digit number may start with any digit. - BGR EGN (#375): a weighted-sum remainder of 10 gives check
digit 0. About 1 in 11 real EGNs, such as8507300050and0001011000, were rejected before. - BGR 13-digit EIK/BULSTAT (#375): the check now weights digits 9
to 12, and the first nine digits must be a valid 9-digit EIK.0114806291686is valid and0114806291688is not.
9-digit codes are unchanged. - CHE AHV/AVS number (#377):
- The check digit uses the EAN-13 weights 1 and 3. Genuine numbers such as
756.9217.0769.85are now accepted and
wrong ones such as756.0000.5678.17are rejected. - The dot in the pattern is escaped, so
756.1234.5678-97isFalse.
- The check digit uses the EAN-13 weights 1 and 3. Genuine numbers such as
- CHE UID (
BusinessID) (#377): the modulus 11 check digit is
verified.CHE-116.281.710is valid andCHE-116.281.715is not. - CHL RUT (#382,
#410):- The weights run from the right, so 7-digit RUTs are checked correctly.
1.111.111-4is valid,1.111.111-3is not.
8-digit RUTs are unchanged. - A lowercase check character is accepted:
10.000.013-kis valid.
- The weights run from the right, so 7-digit RUTs are checked correctly.
- CZE and SVK birth number (#384):
- Check digit 0 is accepted when the first nine digits leave remainder 10:
5401031230is valid. - 9-digit numbers are accepted for births up to 1953. They need a valid date with
yy53 or below, for example
530101123. - A 10-digit number with
yy00 to 53 is read as 2000 to 2053 and a birth date in the future is invalid.5001010003
is no longer read as 1950.METADATA.min_lengthis now 9.
- Check digit 0 is accepted when the first nine digits leave remainder 10:
- ITA fiscal code (#386):
- Omocodia codes are now valid.
RSSMRA8LM01H501Wused to makevalidate()raise. parse()maps the letters back to digits (area_codeisH500instead ofH50L) and picks the century: 20yy
unless that date is in the future, otherwise 19yy.RSSMRA40M01H501Bis born in 1940. Validity does not change for
IDs without omocodia letters.
- Omocodia codes are now valid.
- KWT civil number (#388): a number with a correct check digit
but an impossible birth date (200022900006,200013200008,200001000009) used to raise.validate()is now
Falseandparse()isNone. - NOR national ID (#389):
- An impossible date (31 April, 30 February, day or month
00,00000000000) givesFalseinstead of raising. - D-numbers (day plus 40, for example
41019912351) and H-numbers (month plus 40, for example01419912340) are now
valid andparse()returns the real birth date. A number with both offsets, and FH-numbers, stay invalid.
- An impossible date (31 April, 30 February, day or month
- ROU personal numerical code (#391): the first digit 6 (women
born 2000 to 2099) no longer raises, and6050315121230is valid. The first digit 2 now means 1900 to 1999 and 4
means 1800 to 1899, so4000229011237(29 February 1800) is invalid. - SGP NRIC/FIN (#392): M-series FINs add a start constant of 3 to
the weighted sum.M1234567KandM3960741Nare now valid, andM1234567Nis not. The S, T, F and G series are
unchanged. - ZAF national ID (#394,
#275): non-numeric input such asabcraisedValueErrorand now
givesFalse. IDs with month 10 are accepted too, see the CHN and ZAF month pattern entry below. - TWN national ID (#396): a weighted sum that is a multiple of 10
gives check digit 0. About 1 in 10 genuine IDs, such asM162773050andA120229780, were rejected before. - ZWE national ID (#398): the check letter is the whole number
modulo 23, as the cited source says, and not the digit sum.502001148W50and082095850T42are now valid.
751919620S86is now invalid, and751919620J86is the valid one. About 22 in 23 of the IDs that passed the old
digit-sum rule are now rejected. - GEO personal number (#400): the number has 11 digits, and may
start with 0.01001011234is valid. Every 9-digit string, which was valid before, is now invalid. - BRA RG and SWE personal identity number ([#402](ht...