Skip to main content

Parse, validate and extract data from Egyptian National ID numbers

Project description

egypt-nid

PyPI version CI Coverage Typing Python 3.8+ License: MIT

egypt-nid is the definitive Python library for parsing, validating, and extracting structured data from Egyptian National ID numbers (الرقم القومي).


Features

  • ✅ Full 14-digit structural validation (century, birth date, governorate, Luhn checksum)
  • 🔒 Strict mode (raises typed exceptions) and lenient mode (collects warnings)
  • 📊 Bulk parsing, filtering, and aggregate statistics
  • 🌍 Governorate names in English and Arabic with locale support
  • 🎭 Masking for safe display
  • 🖥️ CLI tool (egypt-nid)
  • 🔌 Framework-ready (Django, FastAPI, Pandas — no hard dependencies)
  • 📦 py.typed – PEP 561 compliant, fully typed
  • ⚡ LRU-cached repeated parses

Installation

pip install egypt-nid

Quick Start

from egypt_nid import parse

nid = parse("30105150123456")

print(nid.birth_date)          # datetime.date(2001, 5, 15)
print(nid.age)                 # 23 (depends on current date)
print(nid.gender)              # 'male'
print(nid.governorate_name)    # 'Cairo'
print(nid.governorate_name_ar) # 'القاهرة'
print(nid.is_adult())          # True
print(nid.born_outside_egypt)  # False
print(nid.masked())            # '3** ** ** **** 3456'

Error Handling

Strict mode (default)

from egypt_nid import parse
from egypt_nid.exceptions import (
    LengthError, NonNumericError, CenturyError,
    BirthDateError, GovernorateError, ChecksumError,
)

try:
    nid = parse("bad_input")
except LengthError as e:
    print(f"Wrong length: {e}")
except ChecksumError as e:
    print(f"Typo detected: {e}")
except EgyptNIDError as e:
    print(f"Validation failed: {e}")

Lenient mode

from egypt_nid import parse_lenient

nid, result = parse_lenient("30105150123457")  # bad checksum
if not result.valid:
    for field, warning in result.warnings.items():
        print(f"  [{field}] {warning}")
# nid is still returned if only non-critical checks fail

Serialisation

nid = parse("30105150123456")

# Dictionary
d = nid.to_dict()
d = nid.to_dict(locale="ar", include_birth_date=False)

# JSON
print(nid.to_json())
print(nid.to_json(indent=None))  # compact

Bulk Operations

from egypt_nid import parse_bulk, compute_stats
from egypt_nid import filter_by_gender, filter_by_age_range, filter_adults

ids = ["30105150123456", "29008200201241", ...]

parsed = parse_bulk(ids, skip_errors=True)

males   = filter_by_gender(parsed, "male")
adults  = filter_adults(parsed)
seniors = filter_by_age_range(parsed, 60, 100)

stats = compute_stats(parsed)
print(stats.average_age)             # 34.7
print(stats.gender_distribution)     # {'male': 120, 'female': 80}
print(stats.governorate_distribution) # {'01': 50, '21': 30, ...}

CLI Usage

# Pretty output
egypt-nid 30105150123456

# JSON output
egypt-nid --json 30105150123456

# Lenient mode
egypt-nid --lenient 30105150123457

# Bulk processing from file (one NID per line)
egypt-nid --bulk ids.txt
egypt-nid --bulk ids.txt --json

# Version
egypt-nid --version

Framework Integration

Django

from django.core.exceptions import ValidationError
from egypt_nid import parse
from egypt_nid.exceptions import EgyptNIDError

def validate_egyptian_nid(value):
    try:
        parse(value)
    except EgyptNIDError as exc:
        raise ValidationError(str(exc)) from exc

FastAPI

from fastapi import HTTPException
from egypt_nid import parse, NationalID
from egypt_nid.exceptions import EgyptNIDError

def get_nid(nid_str: str) -> NationalID:
    try:
        return parse(nid_str)
    except EgyptNIDError as exc:
        raise HTTPException(status_code=422, detail=str(exc))

Pandas

import pandas as pd
from egypt_nid import parse
from egypt_nid.exceptions import EgyptNIDError

def safe_parse(val):
    try:
        return parse(val).gender
    except EgyptNIDError:
        return None

df["gender"] = df["nid"].apply(safe_parse)

Custom Governorates

from egypt_nid import parse

custom = {
    "01": ("New Cairo", "القاهرة الجديدة"),
    "88": ("Foreign", "خارج الجمهورية"),
}

nid = parse("30105150123456", custom_governorates=custom)

Governorate Versioning

# Default is "v1"; future versions will be added without breaking changes
nid = parse("30105150123456", version="v1")

Performance Notes

  • Repeated parses of the same NID string are served from an LRU cache (4096 entries).
  • parse_bulk processes a list in a single pass.
  • All derived attributes (age, gender) are computed lazily.
  • For datasets > 100 k rows, consider running in a thread pool executor.

Contributing

Please read CONTRIBUTING.md and open a pull request. All contributions require:

  1. Tests (pytest + Hypothesis)
  2. Type annotations
  3. Google-style docstrings
  4. Passing ruff, black, and mypy --strict

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

egypt_nid-0.1.1.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

egypt_nid-0.1.1-py3-none-any.whl (22.9 kB view details)

Uploaded Python 3

File details

Details for the file egypt_nid-0.1.1.tar.gz.

File metadata

  • Download URL: egypt_nid-0.1.1.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for egypt_nid-0.1.1.tar.gz
Algorithm Hash digest
SHA256 e19f7d0897c6a0bee93e3552bd0fdb0024d4557867d02fc6d03af21fad337632
MD5 33aa34eac25480bba7d8107509e9feeb
BLAKE2b-256 d20787306ad3126cb12bdb6384541813a789e185ddbd22e3d80ee6090dc86d99

See more details on using hashes here.

File details

Details for the file egypt_nid-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: egypt_nid-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 22.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for egypt_nid-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d63515e6198e4d79f475e6203e04544bf37cad1997e999ec31e98cfc349231f5
MD5 814d0568996e9097fe4708f7fe61582e
BLAKE2b-256 6da53dfe0ab6eff98b4c3c1be003f998bfac26864d9ace92e8fb539dc922d19b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page