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 @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- `is_valid_legal_process` retornava erro ou aceitava dígitos não ASCII para números de processo malformados; agora retorna `False`, e `format_legal_process` retorna `None` nesses casos [#793](https://github.com/brazilian-utils/python/issues/793)
- Cálculo do dígito verificador em `is_valid_legal_process` e `generate_legal_process`, que rejeitava números de processo reais e gerava números inválidos [#785](https://github.com/brazilian-utils/python/pull/785)

## [2.5.0] - 2026-06-30
Expand Down
31 changes: 30 additions & 1 deletion brutils/legal_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def format_legal_process(legal_process_id: str) -> str | None:
None
"""

if legal_process_id.isdigit() and len(legal_process_id) == 20:
if _is_twenty_digits(legal_process_id):
capture_fields = r"(\d{7})(\d{2})(\d{4})(\d)(\d{2})(\d{4})"
include_chars = r"\1-\2.\3.\4.\5.\6"

Expand All @@ -69,6 +69,29 @@ def format_legal_process(legal_process_id: str) -> str | None:
############


def _is_twenty_digits(legal_process_id: str) -> bool:
"""
Checks whether the string has exactly 20 ASCII digits.

`str.isdigit` is not enough: it also accepts characters such as "²" and
"٣", which `int` either rejects or reads as a different digit.

Args:
legal_process_id (str): The string to be checked.

Returns:
bool: True if the string has exactly 20 ASCII digits, False otherwise.

Example:
>>> _is_twenty_digits("68476506120233030000")
True
>>> _is_twenty_digits("6847650612023303000٣")
False
"""

return re.fullmatch(r"\d{20}", legal_process_id, flags=re.ASCII) is not None


def is_valid(legal_process_id: str) -> bool:
"""
Check if a legal process ID is valid.
Expand All @@ -92,7 +115,13 @@ def is_valid(legal_process_id: str) -> bool:
False
"""

if not isinstance(legal_process_id, str):
return False

clean_legal_process_id = remove_symbols(legal_process_id)
if not _is_twenty_digits(clean_legal_process_id):
return False

DD = clean_legal_process_id[7:9]
J = clean_legal_process_id[13:14]
TR = clean_legal_process_id[14:16]
Expand Down
19 changes: 19 additions & 0 deletions tests/test_legal_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@ def test_is_valid(self):
self.assertIs(is_valid("455323423QQWEQWSsasd&*(()"), False)
self.assertIsInstance(is_valid("455323423QQWEQWSsasd&*(()"), bool)

def test_is_valid_returns_false_for_malformed_input(self):
# 5046512-94.2016.4.04.7000 is a real, valid legal process ID
self.assertIs(is_valid("50465129420164047000"), True)
# Truncated: the court (TR) or origin (OOOO) fields are empty
self.assertIs(is_valid("50465129420164"), False)
self.assertIs(is_valid("5046512942016404"), False)
# Letters where digits are expected
self.assertIs(is_valid("ABCDEFG9420164047000"), False)
# Characters that str.isdigit accepts but are not ASCII digits
self.assertIs(is_valid("²0465129420164047000"), False)
self.assertIs(is_valid("5046512942016404700٠"), False)
# Not a string
self.assertIs(is_valid(50465129420164047000), False)
self.assertIs(is_valid(None), False)

def test_format_legal_process_rejects_non_ascii_digits(self):
self.assertIsNone(format_legal_process("5046512942016404700٠"))
self.assertIsNone(format_legal_process("²" * 20))


if __name__ == "__main__":
main()
Loading