Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Utilitário `convert_name_to_uf`
- Utilitário `is_valid_cnh` [#651](https://github.com/brazilian-utils/brutils-python/pull/651)

## [2.3.0] - 2025-10-07

Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ False
- [is\_valid\_email](#is_valid_email)
- [Data](#date)
- [convert\_date\_to_text](#convert_date_to_text)
- [CNH](#cnh)
- [is\_valid\_cnh](#is_valid_cnh)
- [Placa de Carro](#placa-de-carro)
- [is\_valid\_license\_plate](#is_valid_license_plate)
- [format\_license\_plate](#format_license_plate)
Expand Down Expand Up @@ -666,6 +668,37 @@ None
"Primeiro de agosto de dois mil e vinte e quatro"
````

## CNH

### is_valid_cnh

Verifica se o número de registro de CNH (Carteira de Habilitação Nacional) brasileiro é válido.
Para que um número de CNH seja considerado válido, a entrada deve ser uma string contendo
exatamente 11 dígitos numéricos. Esta função não verifica se o número da CNH é real, apenas
valida os dígitos verificadores.

Argumentos:

- cnh (str): A string contendo o número de registro de CNH a ser verificado.

Retorno:

- bool: True se o número de registro da CNHN for válido (11 dígitos), False caso contrário.

Exemplo:

```python
>>> from brutils import is_valid_cnh
>>> is_valid_cnh("12345678901")
False
>>> is_valid_cnh("A2C45678901")
False
>>> is_valid_cnh("98765432100")
True
>>> is_valid_cnh("987654321-00")
True
```


## Placa de Carro

Expand Down
33 changes: 33 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ False
- [generate\_phone](#generate_phone)
- [Email](#email)
- [is\_valid\_email](#is_valid_email)
- [CNH](#cnh)
- [is\_valid\_cnh](#is_valid_cnh)
- [License Plate](#license-plate)
- [is\_valid\_license\_plate](#is_valid_license_plate)
- [format\_license\_plate](#format_license_plate)
Expand Down Expand Up @@ -659,6 +661,37 @@ False
False
```


## CNH

### is_valid_cnh

Checks if the registration number of a brazilian CNH (Carteira de Habilitação Nacional or Driver's License in En.) is valid.
To be considered valid, the input must be a string containing exactly 11 digits. This function does not verify if the registration number of the CNH is a real one, it only validates it's verification numbers.

Argumentos:

- cnh (str): A string containing the registration nunber of the CNH to be checked.

Retorno:

- bool: True if the CNH number is valid (11 digits), False otherwise.

Exemplo:

```python
>>> from brutils import is_valid_cnh
>>> is_valid_cnh("123456789")
False
>>> is_valid_cnh("A2C45678901")
False
>>> is_valid_cnh("98765432100")
True
>>> is_valid_cnh("987654321-00")
True
```


## License Plate

### is_valid_license_plate
Expand Down
5 changes: 5 additions & 0 deletions brutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
from brutils.cep import is_valid as is_valid_cep
from brutils.cep import remove_symbols as remove_symbols_cep

# CNH Imports
from brutils.cnh import is_valid_cnh as is_valid_cnh

# CNPJ Imports
from brutils.cnpj import format_cnpj
from brutils.cnpj import generate as generate_cnpj
Expand Down Expand Up @@ -95,6 +98,8 @@
"generate_cpf",
"is_valid_cpf",
"remove_symbols_cpf",
# CNH
"is_valid_cnh",
# Email
"is_valid_email",
# Legal Process
Expand Down
86 changes: 86 additions & 0 deletions brutils/cnh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
def is_valid_cnh(cnh: str) -> bool:
"""
Validates the registration number for the Brazilian CNH (Carteira Nacional de Habilitação) that was created in 2022.
Previous versions of the CNH are not supported in this version.
This function checks if the given CNH is valid based on the format and allowed characters,
verifying the verification digits.
Args:
cnh (str): CNH string (symbols will be ignored).
Returns:
bool: True if CNH has a valid format.
Examples:
>>> is_valid_cnh("12345678901")
False
>>> is_valid_cnh("A2C45678901")
False
>>> is_valid_cnh("98765432100")
True
>>> is_valid_cnh("987654321-00")
True
"""
cnh = "".join(
filter(str.isdigit, cnh)
) # clean the input and check for numbers only

if not cnh:
return False

if len(cnh) != 11:
return False

# Reject sequences as "00000000000", "11111111111", etc.
if cnh == cnh[0] * 11:
return False

# cast digits to list of integers
digits: list[int] = [int(ch) for ch in cnh]
first_verificator = digits[9]
second_verificator = digits[10]

if not _check_first_verificator(
digits, first_verificator
): # checking the 10th digit
return False

return _check_second_verificator(
digits, second_verificator, first_verificator
) # checking the 11th digit


def _check_first_verificator(digits: list[int], first_verificator: int) -> bool:
"""
Generates the first verification digit and uses it to verify the 10th digit of the CNH
"""

sum = 0
for i in range(9):
sum += digits[i] * (9 - i)

sum = sum % 11
result = 0 if sum > 9 else sum

return result == first_verificator


def _check_second_verificator(
digits: list[int], second_verificator: int, first_verificator: int
) -> bool:
"""
Generates the second verification and uses it to verify the 11th digit of the CNH
"""
sum = 0
for i in range(9):
sum += digits[i] * (i + 1)

result = sum % 11

if first_verificator > 9:
result = result + 9 if (result - 2) < 0 else result - 2

if result > 9:
result = 0

return result == second_verificator
12 changes: 12 additions & 0 deletions tests/test_cnh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from unittest import TestCase

from brutils.cnh import is_valid_cnh


class TestCNH(TestCase):
def test_is_valid_cnh(self):
self.assertFalse(is_valid_cnh("22222222222"))
self.assertFalse(is_valid_cnh("ABC70304734"))
self.assertFalse(is_valid_cnh("6619558737912"))
self.assertTrue(is_valid_cnh("097703047-34"))
self.assertTrue(is_valid_cnh("09770304734"))