Spain validator plugin for International URNs
Project description
International URNs - Spain Plugin
A comprehensive plugin for International URNs that provides validators and generators for Spanish identity documents and administrative codes.
Requirements
- Python 3.11 or higher
- international-urns 1.0.0rc4 or higher (for extraction support)
- Pydantic 2.0+ (optional, for Pydantic integration)
- Faker 18.0+ (optional, for Faker integration)
Installation
# Basic installation
pip install international-urns-es
# With Pydantic integration
pip install international-urns-es[pydantic]
# With Faker integration
pip install international-urns-es[faker]
# With all optional dependencies
pip install international-urns-es[all]
Supported Documents
This plugin validates, generates, and extracts metadata from the following Spanish document types:
- DNI - Documento Nacional de Identidad (National Identity Document)
- NIE - Número de Identidad de Extranjero (Foreigner Identity Number)
- NIF - Número de Identificación Fiscal (Tax Identification Number)
- CIF - Código de Identificación Fiscal (Company Tax Code)
- DIR3 - Directorio Común de unidades y oficinas (Common Directory of Administrative Units)
- NSS - Número de la Seguridad Social (Social Security Number)
- License Plates - Matrículas de vehículos (Vehicle registration plates)
Features
- Validation: Check if URNs are correctly formatted and valid according to Spanish regulations
- Generation: Create random valid URNs for testing purposes
- Metadata Extraction: Extract rich metadata from URNs including document purpose, historical context, organization types, administration levels, and more
- Official Algorithms: All validators use official algorithms documented in Spanish government regulations
Quick Start
Validation
import international_urns as iurns
# Validate a DNI
dni_validator = iurns.get_validator('es', 'dni')
result = dni_validator('urn:es:dni:12345678Z')
print(result) # urn:es:dni:12345678Z
# Validate a NIE
nie_validator = iurns.get_validator('es', 'nie')
result = nie_validator('urn:es:nie:X1234567L')
print(result) # urn:es:nie:X1234567L
# Validate a license plate
plate_validator = iurns.get_validator('es', 'plate')
result = plate_validator('urn:es:plate:1234BBC')
print(result) # urn:es:plate:1234BBC
Generation
import international_urns as iurns
# Generate a random valid DNI
dni_generator = iurns.get_generator('es', 'dni')
dni_urn = dni_generator()
print(dni_urn) # urn:es:dni:43335678K (random)
# Generate a random valid CIF
cif_generator = iurns.get_generator('es', 'cif')
cif_urn = cif_generator()
print(cif_urn) # urn:es:cif:B12345670 (random)
# Direct import also works
from international_urns_es import generate_dni, generate_nie
dni_urn = generate_dni()
nie_urn = generate_nie()
Metadata Extraction
Extract rich metadata from URNs:
import international_urns as iurns
# Extract metadata from any URN
metadata = iurns.extract_urn('urn:es:cif:B01234566')
print(metadata['country_code']) # Output: 'es'
print(metadata['document_type']) # Output: 'cif'
print(metadata['organization_type_name']) # Output: 'Sociedad de Responsabilidad Limitada'
print(metadata['organization_category']) # Output: 'legal_entity'
print(metadata['provincial_code']) # Output: '01'
# Extract from DIR3
dir3_metadata = iurns.extract_urn('urn:es:dir3:A01002844')
print(dir3_metadata['administration_level_name']) # Output: 'Administración General del Estado'
# Extract from license plate
plate_metadata = iurns.extract_urn('urn:es:plate:M1234AB')
print(plate_metadata['plate_format']) # Output: 'old'
print(plate_metadata['province_name']) # Output: 'Madrid'
Each document type provides specific metadata fields. See the Document Types Reference below for details on what metadata each extractor provides.
Document Types Reference
DNI (Documento Nacional de Identidad)
The DNI is the national identity document for Spanish citizens.
Format: 8 digits + 1 check letter
Examples:
12345678Z00000000T99999999R
URN Format:
urn:es:dni:12345678Z
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'dni')
# Valid DNI
try:
result = validator('urn:es:dni:12345678Z')
print(f"Valid: {result}")
except ValueError as e:
print(f"Invalid: {e}")
Validation Rules:
- Must be exactly 8 digits followed by 1 letter
- Check letter is calculated using modulo 23 algorithm
- Case-insensitive for URN scheme, country, and document type
- Value preserves original case
Extracted Metadata:
metadata = iurns.extract_urn('urn:es:dni:12345678Z')
# Returns:
# {
# 'country_code': 'es',
# 'document_type': 'dni',
# 'document_value': '12345678Z',
# 'number': '12345678',
# 'check_letter': 'Z',
# 'is_valid_format': True
# }
NIE (Número de Identidad de Extranjero)
The NIE is the identification number for foreign nationals residing in Spain.
Format: Letter (X, Y, or Z) + 7 digits + 1 check letter
Examples:
X1234567LY0000000ZZ9999999R
URN Format:
urn:es:nie:X1234567L
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'nie')
result = validator('urn:es:nie:X1234567L')
Validation Rules:
- Prefix must be X, Y, or Z
- Followed by exactly 7 digits
- Check letter calculated using same algorithm as DNI (with prefix conversion: X→0, Y→1, Z→2)
Extracted Metadata:
metadata = iurns.extract_urn('urn:es:nie:X1234567L')
# Returns:
# {
# 'country_code': 'es',
# 'document_type': 'nie',
# 'document_value': 'X1234567L',
# 'prefix': 'X',
# 'number': '1234567',
# 'check_letter': 'L',
# 'generation': 'Original NIE series, issued until July 15, 2008 (X = 0 for check letter calculation)',
# 'document_purpose': 'Identification number for foreign nationals in Spain',
# 'format_history': 'Format changed from 8 to 7 digits by Orden INT/2058/2008',
# 'is_valid_format': True
# }
NIF (Número de Identificación Fiscal)
The NIF is the tax identification number for individuals in Spain. It accepts both DNI and NIE formats.
Format: DNI format (8 digits + letter) or NIE format (letter + 7 digits + letter)
Examples:
12345678Z(DNI format)X1234567L(NIE format)
URN Format:
urn:es:nif:12345678Z
urn:es:nif:X1234567L
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'nif')
# DNI format
result1 = validator('urn:es:nif:12345678Z')
# NIE format
result2 = validator('urn:es:nif:X1234567L')
Validation Rules:
- Accepts both DNI and NIE formats
- Validates check letter for both formats
Extracted Metadata:
# For DNI format
metadata = iurns.extract_urn('urn:es:nif:12345678Z')
# Returns: {'format_type': 'dni', 'number': '12345678', 'check_letter': 'Z', ...}
# For NIE format
metadata = iurns.extract_urn('urn:es:nif:X1234567L')
# Returns: {'format_type': 'nie', 'prefix': 'X', 'number': '1234567',
# 'generation': '...', ...}
CIF (Código de Identificación Fiscal)
The CIF is the tax identification code for Spanish companies and organizations.
Format: 1 organization letter + 7 digits + 1 check character (letter or digit)
Organization Type Letters:
- A - Sociedades Anónimas (Public Limited Companies)
- B - Sociedades de Responsabilidad Limitada (Private Limited Companies)
- C - Sociedades Colectivas (General Partnerships)
- D - Sociedades Comanditarias (Limited Partnerships)
- E - Comunidades de Bienes (Communities of Property)
- F - Sociedades Cooperativas (Cooperatives)
- G - Asociaciones (Associations)
- H - Comunidades de Propietarios (Homeowner Associations)
- J - Sociedades Civiles (Civil Partnerships)
- N - Entidades Extranjeras (Foreign Entities)
- P - Corporaciones Locales (Local Corporations)
- Q - Organismos Autónomos (Autonomous Bodies)
- R - Congregaciones e Instituciones Religiosas (Religious Organizations)
- S - Órganos de la Administración del Estado (State Administration Bodies)
- U - Uniones Temporales de Empresas (Temporary Business Associations)
- V - Otros tipos no definidos (Other Undefined Types)
- W - Establecimientos permanentes de entidades no residentes (Permanent Establishments)
Examples:
A12345674(SA - Sociedad Anónima, check digit is number)B01234566(SL - Sociedad Limitada, check digit is number)N1234567J(Foreign entity - check digit is letter)
URN Format:
urn:es:cif:A12345674
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'cif')
result = validator('urn:es:cif:A12345674')
Validation Rules:
- Organization types N, P, Q, R, S, W must have a letter as check digit
- Organization types A, B, E, H must have a number as check digit
- Other types can have either
- Check digit calculated using weighted sum algorithm
Extracted Metadata:
metadata = iurns.extract_urn('urn:es:cif:B01234566')
# Returns:
# {
# 'country_code': 'es',
# 'document_type': 'cif',
# 'document_value': 'B01234566',
# 'organization_type_code': 'B',
# 'organization_type_name': 'Sociedad de Responsabilidad Limitada',
# 'organization_category': 'legal_entity', # or 'public_entity', 'religious'
# 'number': '0123456',
# 'check_character': '6',
# 'check_format': 'digit', # or 'letter'
# 'provincial_code': '01',
# 'provincial_name': 'Álava',
# 'is_valid_format': True
# }
DIR3 (Directorio Común)
DIR3 codes identify administrative units and offices in the Spanish Public Administration.
Format: Exactly 9 alphanumeric characters
- Single-letter prefix (E, A, L, U, I, J, O) + 8 digits
- Double-letter prefix (EA, GE, EC) + 7 digits
Administration Level Codes:
Single-letter prefixes:
- E - Administración del Estado (State Administration)
- A - Administración Autonómica (Autonomous Community Administration)
- L - Administración Local (Local Administration)
- U - Universidades (Universities)
- I - Otras Instituciones (Other Institutions)
- J - Administración de Justicia (Justice Administration)
- O - Oficinas (Offices)
Double-letter prefixes:
- EA - Administración del Estado no RCP (State Administration, non-RCP)
- GE - Unidad de Gestión Económica-Presupuestaria (Economic-Budgetary Management Unit)
- EC - Entidades Colaboradoras (Collaborating Entities)
Examples:
E00010201(State administration unit)A13000001(Autonomous community - Madrid)L01234567(Local administration - Municipality)U00112345(University)EA0043247(State administration - non-RCP)GE0001234(Economic management unit)EC0005678(Collaborating entity)
URN Format:
urn:es:dir3:E00010201
urn:es:dir3:A13000001
urn:es:dir3:EA0043247
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'dir3')
result = validator('urn:es:dir3:E00010201')
Validation Rules:
- Must be exactly 9 characters total
- First character(s) must be a valid administration level code
- Remaining characters must be digits (8 for single-letter, 7 for double-letter prefixes)
- Based on Ley 39/2015 and National Interoperability Scheme
Extracted Metadata:
# State Administration
metadata = iurns.extract_urn('urn:es:dir3:E00010201')
# Returns:
# {
# 'country_code': 'es',
# 'document_type': 'dir3',
# 'document_value': 'E00010201',
# 'administration_level_code': 'E',
# 'administration_level_name': 'Administración del Estado',
# 'unit_code': '00010201',
# 'is_valid_format': True
# }
# Autonomous Community (includes community info)
metadata = iurns.extract_urn('urn:es:dir3:A13000001')
# Returns:
# {
# 'administration_level_code': 'A',
# 'administration_level_name': 'Administración Autonómica',
# 'unit_code': '13000001',
# 'autonomous_community_code': '13',
# 'autonomous_community_name': 'Comunidad de Madrid',
# 'is_valid_format': True
# }
# University (includes SIIU code)
metadata = iurns.extract_urn('urn:es:dir3:U00112345')
# Returns:
# {
# 'administration_level_code': 'U',
# 'administration_level_name': 'Universidades',
# 'unit_code': '00112345',
# 'university_siiu_code': '001',
# 'is_valid_format': True
# }
NSS (Número de la Seguridad Social)
The NSS is the Social Security Number used in Spain.
Format: 12 digits, optionally formatted with slashes
Structure:
- First 2 digits: Province code (01-52 or special codes 66-99)
- Next 8 digits: Sequential number
- Last 2 digits: Check digits (calculated using modulo 97)
Examples:
281234567840(without slashes)28/12345678/40(with slashes)
URN Format:
urn:es:nss:281234567840
urn:es:nss:28/12345678/40
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'nss')
# Without slashes
result1 = validator('urn:es:nss:281234567840')
# With slashes
result2 = validator('urn:es:nss:28/12345678/40')
Validation Rules:
- Must be exactly 12 digits
- Province code must be 01-52 (Spanish provinces) or 66-99 (special codes)
- Check digits validated using modulo 97 algorithm
- Accepts format with or without slashes
Extracted Metadata:
metadata = iurns.extract_urn('urn:es:nss:281234567840')
# Returns:
# {
# 'country_code': 'es',
# 'document_type': 'nss',
# 'document_value': '281234567840',
# 'province_code': '28',
# 'province_name': 'Madrid',
# 'sequential_number': '12345678',
# 'check_digits': '40',
# 'format': 'continuous', # or 'slashes'
# 'is_special_code': False,
# 'is_valid_format': True
# }
License Plates (Matrículas)
Spanish vehicle license plates. Supports current, historical, and special formats.
Current Format (since 2000): 4 digits + 3 consonants (no vowels)
Old Format (1971-2000): 1-2 province letters + 4 digits + 1-2 letters
Special Formats: Diplomatic (CD), Consular (CC), Foreign (E), etc.
Examples:
Current format:
1234BBC0000ZZZ9999DFG
Old format:
M1234AB(Madrid)B5678XY(Barcelona)PM9012CD(Palma de Mallorca)
Special format:
CD1234(Diplomatic)CC12345(Consular)E12345(Foreign)
URN Format:
urn:es:plate:1234BBC
urn:es:plate:M1234AB
urn:es:matricula:1234BBC
Usage:
import international_urns as iurns
validator = iurns.get_validator('es', 'plate')
# Current format
result1 = validator('urn:es:plate:1234BBC')
# Old format
result2 = validator('urn:es:plate:M1234AB')
# With spaces or hyphens (automatically normalized)
result3 = validator('urn:es:plate:1234 BBC')
result4 = validator('urn:es:plate:M-1234-AB')
Validation Rules:
Current format:
- Must be 4 digits + 3 consonants
- Allowed consonants: B, C, D, F, G, H, J, K, L, M, N, P, R, S, T, V, W, X, Y, Z
- No vowels (A, E, I, O, U), Ñ, or Q allowed
Old format:
- Province code must be valid (M, B, MA, PM, etc.)
- Followed by 4 digits
- Ending with 1-2 letters
Special format:
- Recognized prefixes: CD, CC, E, ET, CMD, DGP, MF, MMA, PMM, CNP
- Followed by 4-5 digits
Extracted Metadata:
# Current format
metadata = iurns.extract_urn('urn:es:plate:1234BBC')
# Returns: {'plate_format': 'current', 'digits': '1234', 'letters': 'BBC', ...}
# Old format
metadata = iurns.extract_urn('urn:es:plate:M1234AB')
# Returns: {'plate_format': 'old', 'province_code': 'M',
# 'province_name': 'Madrid', 'digits': '1234', 'letters': 'AB', ...}
# Special format
metadata = iurns.extract_urn('urn:es:plate:CD1234')
# Returns: {'plate_format': 'special', 'special_type': 'CD',
# 'special_type_description': 'Cuerpo Diplomático (Diplomatic Corps)', ...}
Pydantic Integration
All validators work seamlessly with Pydantic v2 using AfterValidator annotations.
Option 1: Pre-defined Type Aliases (Recommended)
The plugin provides convenient pre-defined type aliases in the integrations.pydantic module:
from pydantic import BaseModel
from international_urns_es.integrations.pydantic import DNI_URN, CIF_URN, PLATE_URN
class Person(BaseModel):
name: str
dni: DNI_URN
class Company(BaseModel):
name: str
cif: CIF_URN
vehicles: list[PLATE_URN]
# Usage
person = Person(name="John Doe", dni="urn:es:dni:12345678Z")
company = Company(
name="Example SL",
cif="urn:es:cif:B01234566",
vehicles=["urn:es:plate:1234BBC", "urn:es:plate:5678DFG"]
)
Available type aliases: DNI_URN, NIE_URN, NIF_URN, CIF_URN, DIR3_URN, NSS_URN, PLATE_URN, MATRICULA_URN
Option 2: Using the Registry Interface
You can also create type aliases directly using the registry:
from typing import Annotated
import international_urns as iurns
from pydantic import BaseModel
from pydantic.functional_validators import AfterValidator, BeforeValidator
# Create type aliases using validators directly from the registry
DNI_URN = Annotated[str, AfterValidator(iurns.get_validator("es", "dni"))]
CIF_URN = Annotated[str, AfterValidator(iurns.get_validator("es", "cif"))]
# Type alias with both BeforeValidator and AfterValidator
DNI_URN_NORMALIZED = Annotated[
str,
BeforeValidator(str.strip), # Built-in normalization
AfterValidator(iurns.get_validator("es", "dni")),
]
# Use in models
class Person(BaseModel):
name: str
dni_urn: DNI_URN
class Company(BaseModel):
name: str
cif_urn: CIF_URN
class Employee(BaseModel):
name: str
dni_urn: DNI_URN_NORMALIZED # Strips whitespace before validation
# Usage
person = Person(name="John Doe", dni_urn="urn:es:dni:12345678Z")
company = Company(name="Example SL", cif_urn="urn:es:cif:B01234566") # SL uses B prefix
employee = Employee(name="Jane Doe", dni_urn=" urn:es:dni:12345678Z ") # Auto-strips
Advanced Pydantic Examples
Multiple validators in one model:
# Define additional validators using registry
PLATE_URN = Annotated[str, AfterValidator(iurns.get_validator("es", "plate"))]
class SpanishEntity(BaseModel):
name: str
dni_urn: DNI_URN | None = None
cif_urn: CIF_URN | None = None
plate_urn: PLATE_URN | None = None
# Person with DNI and vehicle
person = SpanishEntity(
name="John Doe",
dni_urn="urn:es:dni:12345678Z",
plate_urn="urn:es:plate:1234BBC"
)
List validation:
class CompanyFleet(BaseModel):
company_name: str
cif_urn: CIF_URN
vehicles: list[PLATE_URN]
# SL companies use B prefix
fleet = CompanyFleet(
company_name="Transport SL",
cif_urn="urn:es:cif:B01234566",
vehicles=["urn:es:plate:1234BBC", "urn:es:plate:5678DFG"]
)
Faker Integration
The plugin provides a Faker provider for generating random valid Spanish URN data for testing purposes.
from faker import Faker
from international_urns_es.integrations.faker import SpanishURNProvider
# Create a Faker instance and add the provider
fake = Faker()
fake.add_provider(SpanishURNProvider)
# Generate random Spanish URNs
dni = fake.dni_urn()
print(dni) # urn:es:dni:43335678K (random)
nie = fake.nie_urn()
print(nie) # urn:es:nie:Y1234567M (random)
cif = fake.cif_urn()
print(cif) # urn:es:cif:B12345670 (random)
plate = fake.plate_urn()
print(plate) # urn:es:plate:5678DFG (random)
Available Faker Methods
The SpanishURNProvider provides the following methods:
dni_urn()- Generate a random DNI URNnie_urn()- Generate a random NIE URNnif_urn()- Generate a random NIF URN (DNI or NIE format)cif_urn()- Generate a random CIF URNdir3_urn()- Generate a random DIR3 URNnss_urn()- Generate a random NSS URNplate_urn()- Generate a random license plate URNmatricula_urn()- Generate a random license plate URN (alternative name)
Integration with Pydantic and Faker
Combine both integrations for powerful test data generation:
from faker import Faker
from pydantic import BaseModel
from international_urns_es.integrations.faker import SpanishURNProvider
from international_urns_es.integrations.pydantic import DNI_URN, CIF_URN
# Setup Faker
fake = Faker()
fake.add_provider(SpanishURNProvider)
# Define Pydantic model
class Person(BaseModel):
name: str
dni: DNI_URN
class Company(BaseModel):
name: str
cif: CIF_URN
# Generate random valid test data
person = Person(name=fake.name(), dni=fake.dni_urn())
company = Company(name=fake.company(), cif=fake.cif_urn())
print(person) # name='John Doe' dni='urn:es:dni:43335678K'
print(company) # name='Example SL' cif='urn:es:cif:B12345670'
Development
Setup
# Clone the repository
git clone https://gitlab.com/Kencho1/international-urns-es.git
cd international-urns-es
# Create virtual environment with uv
uv venv
# Install dependencies with dev extras
uv pip install -e ".[dev]"
Running Tests
# Run all tests
pytest
# Run with coverage
pytest --cov=international_urns_es --cov-report=html
# Run specific test file
pytest tests/test_dni.py
# Run with verbose output
pytest -v
Code Quality
# Run ruff linter
ruff check .
# Run ruff formatter
ruff format .
# Run mypy type checker
mypy international_urns_es
Contributing
Contributions are welcome! Please ensure:
- All tests pass
- Code passes ruff and mypy checks
- New validators include comprehensive tests
- Documentation is updated
License
MIT License
Links
References
Official Documentation
- DNI - Spanish National ID
- NIE - Foreigner ID
- Seguridad Social - Social Security
- DIR3 - Administrative Directory
- DGT - Vehicle Plates
Algorithm References
- DNI/NIE check letter calculation uses modulo 23
- CIF check digit uses weighted sum algorithm
- NSS check digits use modulo 97
- License plate formats follow Spanish regulations from different eras
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file international_urns_es-1.1.0.tar.gz.
File metadata
- Download URL: international_urns_es-1.1.0.tar.gz
- Upload date:
- Size: 26.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fe0e7e12deb5d9c23f9b66fc2214ca05911f07069f2ad33e274f09b21cee01c
|
|
| MD5 |
397d93b9d2bbc65ed9760fd8c61be263
|
|
| BLAKE2b-256 |
861a737b6d1d2ae4d42818d3025634f7bbff8c94c3767c44001e5846643234b5
|
File details
Details for the file international_urns_es-1.1.0-py3-none-any.whl.
File metadata
- Download URL: international_urns_es-1.1.0-py3-none-any.whl
- Upload date:
- Size: 44.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.1
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b18485b8c5c022dc3e24073c85532a199ab5cfdc10b4ddf8a10c36bf1bd7597c
|
|
| MD5 |
ac61087e1f717824c82f45bbafff4d42
|
|
| BLAKE2b-256 |
e3b548fc20cc88c95f280c1a0aa09d39b10a1c8b1b2da88360a3fcf2864b4b41
|