cpf-dv 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cpf_dv/__init__.py +19 -0
- cpf_dv/cpf_check_digits.py +154 -0
- cpf_dv/exceptions.py +71 -0
- cpf_dv-1.0.0.dist-info/METADATA +327 -0
- cpf_dv-1.0.0.dist-info/RECORD +8 -0
- cpf_dv-1.0.0.dist-info/WHEEL +5 -0
- cpf_dv-1.0.0.dist-info/licenses/LICENSE +9 -0
- cpf_dv-1.0.0.dist-info/top_level.txt +1 -0
cpf_dv/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .cpf_check_digits import CpfCheckDigits
|
|
2
|
+
from .exceptions import (
|
|
3
|
+
CpfCheckDigitsCalculationError,
|
|
4
|
+
CpfCheckDigitsError,
|
|
5
|
+
CpfCheckDigitsInputLengthError,
|
|
6
|
+
CpfCheckDigitsInputNotValidError,
|
|
7
|
+
CpfCheckDigitsInputTypeError,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"CpfCheckDigits",
|
|
12
|
+
"CpfCheckDigitsCalculationError",
|
|
13
|
+
"CpfCheckDigitsError",
|
|
14
|
+
"CpfCheckDigitsInputLengthError",
|
|
15
|
+
"CpfCheckDigitsInputNotValidError",
|
|
16
|
+
"CpfCheckDigitsInputTypeError",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from .exceptions import (
|
|
4
|
+
CpfCheckDigitsCalculationError,
|
|
5
|
+
CpfCheckDigitsInputLengthError,
|
|
6
|
+
CpfCheckDigitsInputNotValidError,
|
|
7
|
+
CpfCheckDigitsInputTypeError,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
CPF_MIN_LENGTH = 9
|
|
11
|
+
CPF_MAX_LENGTH = 11
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CpfCheckDigits:
|
|
15
|
+
"""Class to calculate CPF check digits."""
|
|
16
|
+
|
|
17
|
+
__slots__ = ("_cpf_digits", "_first_digit", "_second_digit")
|
|
18
|
+
|
|
19
|
+
def __init__(self, cpf_input: str | list[str] | list[int]) -> None:
|
|
20
|
+
original_input = cpf_input
|
|
21
|
+
|
|
22
|
+
if not isinstance(cpf_input, (str, list)):
|
|
23
|
+
raise CpfCheckDigitsInputTypeError(original_input)
|
|
24
|
+
|
|
25
|
+
if isinstance(cpf_input, str):
|
|
26
|
+
cpf_input = self._handle_string_input(cpf_input)
|
|
27
|
+
else:
|
|
28
|
+
cpf_input = self._handle_list_input(cpf_input, original_input)
|
|
29
|
+
|
|
30
|
+
self._validate_length(cpf_input, original_input)
|
|
31
|
+
self._validate_non_repeated_digits(cpf_input, original_input)
|
|
32
|
+
|
|
33
|
+
self._cpf_digits = cpf_input[:CPF_MIN_LENGTH]
|
|
34
|
+
self._first_digit: int | None = None
|
|
35
|
+
self._second_digit: int | None = None
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def first_digit(self) -> int:
|
|
39
|
+
"""Calculates and returns the first check digit.As it's immutable, it caches the calculation result."""
|
|
40
|
+
if self._first_digit is None:
|
|
41
|
+
base_digits_sequence = self._cpf_digits.copy()
|
|
42
|
+
self._first_digit = self._calculate(base_digits_sequence)
|
|
43
|
+
|
|
44
|
+
return self._first_digit
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def second_digit(self) -> int:
|
|
48
|
+
"""Calculates and returns the second check digit.As it's immutable, it caches the calculation result. And, as it depends on the first check digit, it's also calculated."""
|
|
49
|
+
if self._second_digit is None:
|
|
50
|
+
base_digits_sequence = [*self._cpf_digits, self.first_digit]
|
|
51
|
+
self._second_digit = self._calculate(base_digits_sequence)
|
|
52
|
+
|
|
53
|
+
return self._second_digit
|
|
54
|
+
|
|
55
|
+
def to_list(self) -> list[int]:
|
|
56
|
+
"""Returns the complete CPF as a list of 11 integers (9 base digits + 2 check digits)."""
|
|
57
|
+
return [*self._cpf_digits, self.first_digit, self.second_digit]
|
|
58
|
+
|
|
59
|
+
def to_string(self) -> str:
|
|
60
|
+
"""Returns the complete CPF as a string of 11 digits (9 base digits + 2 check digits)."""
|
|
61
|
+
return "".join(str(digit) for digit in self.to_list())
|
|
62
|
+
|
|
63
|
+
def _handle_string_input(self, cpf_string: str) -> list[int]:
|
|
64
|
+
"""When CPF is provided as a string, it is sanitized, validated and converted to a list of integers."""
|
|
65
|
+
digits_only_string = re.sub(r"[^0-9]", "", cpf_string)
|
|
66
|
+
|
|
67
|
+
return [int(digit_string) for digit_string in digits_only_string]
|
|
68
|
+
|
|
69
|
+
def _handle_list_input(
|
|
70
|
+
self,
|
|
71
|
+
cpf_list: list[str] | list[int],
|
|
72
|
+
original_input: list,
|
|
73
|
+
) -> list[int]:
|
|
74
|
+
"""When CPF is provided as a list of strings or integers, it is sanitized, validated and converted to a list of integers for further processing."""
|
|
75
|
+
if all(isinstance(digit, str) for digit in cpf_list):
|
|
76
|
+
return self._handle_string_list_input(cpf_list)
|
|
77
|
+
|
|
78
|
+
if all(isinstance(digit, int) for digit in cpf_list):
|
|
79
|
+
return self._flatten_digits(cpf_list)
|
|
80
|
+
|
|
81
|
+
raise CpfCheckDigitsInputTypeError(original_input)
|
|
82
|
+
|
|
83
|
+
def _handle_string_list_input(self, cpf_string_list: list[str]) -> list[int]:
|
|
84
|
+
"""When CPF is provided as a list of strings, it is sanitized, validated and converted to a list of integers for further processing."""
|
|
85
|
+
final_cpf_int_list = []
|
|
86
|
+
|
|
87
|
+
for list_item in cpf_string_list:
|
|
88
|
+
cpf_int_list = self._handle_string_input(list_item)
|
|
89
|
+
final_cpf_int_list.extend(cpf_int_list)
|
|
90
|
+
|
|
91
|
+
return final_cpf_int_list
|
|
92
|
+
|
|
93
|
+
def _flatten_digits(self, int_list: list[int]) -> list[int]:
|
|
94
|
+
"""Breaks down multiple digits within the array into individual ones. Negative numbers are converted to their absolute value."""
|
|
95
|
+
final_cpf_int_list = []
|
|
96
|
+
|
|
97
|
+
for number in int_list:
|
|
98
|
+
abs_number = abs(number)
|
|
99
|
+
final_cpf_int_list.extend(
|
|
100
|
+
[int(digit_string) for digit_string in str(abs_number)]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return final_cpf_int_list
|
|
104
|
+
|
|
105
|
+
def _validate_length(
|
|
106
|
+
self,
|
|
107
|
+
cpf_int_list: list[int],
|
|
108
|
+
original_input: str | list[str] | list[int],
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Validates the length of the CPF digits."""
|
|
111
|
+
digits_count = len(cpf_int_list)
|
|
112
|
+
|
|
113
|
+
if digits_count < CPF_MIN_LENGTH or digits_count > CPF_MAX_LENGTH:
|
|
114
|
+
raise CpfCheckDigitsInputLengthError(
|
|
115
|
+
original_input,
|
|
116
|
+
"".join(str(digit) for digit in cpf_int_list),
|
|
117
|
+
CPF_MIN_LENGTH,
|
|
118
|
+
CPF_MAX_LENGTH,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def _validate_non_repeated_digits(
|
|
122
|
+
self,
|
|
123
|
+
cpf_int_list: list[int],
|
|
124
|
+
original_input: str | list[str] | list[int],
|
|
125
|
+
) -> None:
|
|
126
|
+
"""Validates that the CPF digits are not all the same."""
|
|
127
|
+
eligible_cpf_int_list = cpf_int_list[:CPF_MIN_LENGTH]
|
|
128
|
+
digits_set = set(eligible_cpf_int_list)
|
|
129
|
+
|
|
130
|
+
if len(digits_set) == 1:
|
|
131
|
+
raise CpfCheckDigitsInputNotValidError(
|
|
132
|
+
original_input,
|
|
133
|
+
"Repeated digits are not considered valid.",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def _calculate(self, cpf_sequence: list[int]) -> int:
|
|
137
|
+
"""Calculates the CPF check digits using the official Brazilian algorithm. For the first check digit, it uses the digits 1 through 9 of the CPF base. For the second one, it uses the digits 1 through 10 (with the first check digit)."""
|
|
138
|
+
min_length = CPF_MIN_LENGTH
|
|
139
|
+
max_length = CPF_MAX_LENGTH - 1
|
|
140
|
+
sequence_length = len(cpf_sequence)
|
|
141
|
+
|
|
142
|
+
if sequence_length < min_length or sequence_length > max_length:
|
|
143
|
+
raise CpfCheckDigitsCalculationError(cpf_sequence)
|
|
144
|
+
|
|
145
|
+
factor = sequence_length + 1
|
|
146
|
+
sum_result = 0
|
|
147
|
+
|
|
148
|
+
for num in cpf_sequence:
|
|
149
|
+
sum_result += num * factor
|
|
150
|
+
factor -= 1
|
|
151
|
+
|
|
152
|
+
remainder = 11 - (sum_result % 11)
|
|
153
|
+
|
|
154
|
+
return 0 if remainder > 9 else remainder
|
cpf_dv/exceptions.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
class CpfCheckDigitsError(Exception):
|
|
2
|
+
"""Base exception for all cpf-dv related errors."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CpfCheckDigitsInputTypeError(CpfCheckDigitsError):
|
|
6
|
+
"""Raised when the class input does not match the expected type."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, actual_input) -> None:
|
|
9
|
+
self.actual_input = actual_input
|
|
10
|
+
|
|
11
|
+
super().__init__(
|
|
12
|
+
f"CPF input must be of type str, list[str] or list[int]. Got {type(actual_input).__name__}."
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CpfCheckDigitsInputLengthError(CpfCheckDigitsError):
|
|
17
|
+
"""Raised when the class input does not contain the expected number of digits."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
actual_input: str | list[str] | list[int],
|
|
22
|
+
evaluated_input: str,
|
|
23
|
+
min_expected_length: int,
|
|
24
|
+
max_expected_length: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
self.actual_input = actual_input
|
|
27
|
+
self.evaluated_input = evaluated_input
|
|
28
|
+
self.min_expected_length = min_expected_length
|
|
29
|
+
self.max_expected_length = max_expected_length
|
|
30
|
+
|
|
31
|
+
if isinstance(actual_input, str):
|
|
32
|
+
fmt_actual_input = f'"{actual_input}"'
|
|
33
|
+
else:
|
|
34
|
+
fmt_actual_input = f"{actual_input}"
|
|
35
|
+
|
|
36
|
+
if actual_input == evaluated_input:
|
|
37
|
+
fmt_evaluated_input = f"{len(evaluated_input)}"
|
|
38
|
+
else:
|
|
39
|
+
fmt_evaluated_input = f'{len(evaluated_input)} in "{evaluated_input}"'
|
|
40
|
+
|
|
41
|
+
super().__init__(
|
|
42
|
+
f"CPF input {fmt_actual_input} does not contain "
|
|
43
|
+
f"{min_expected_length} to {max_expected_length} digits. "
|
|
44
|
+
f"Got {fmt_evaluated_input}."
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class CpfCheckDigitsInputNotValidError(CpfCheckDigitsError):
|
|
49
|
+
"""Raised when the class input is not valid (e.g., repeated digits)."""
|
|
50
|
+
|
|
51
|
+
def __init__(self, actual_input: str | list[str] | list[int], reason: str) -> None:
|
|
52
|
+
self.actual_input = actual_input
|
|
53
|
+
self.reason = reason
|
|
54
|
+
|
|
55
|
+
if isinstance(actual_input, str):
|
|
56
|
+
fmt_actual_input = f'"{actual_input}"'
|
|
57
|
+
else:
|
|
58
|
+
fmt_actual_input = f"{actual_input}"
|
|
59
|
+
|
|
60
|
+
super().__init__(f"CPF input {fmt_actual_input} is invalid. {reason}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CpfCheckDigitsCalculationError(CpfCheckDigitsError):
|
|
64
|
+
"""Raised when the calculation of the CPF check digits fails."""
|
|
65
|
+
|
|
66
|
+
def __init__(self, actual_input: list[int]) -> None:
|
|
67
|
+
self.actual_input = actual_input
|
|
68
|
+
|
|
69
|
+
super().__init__(
|
|
70
|
+
f"Failed to calculate CPF check digits for the sequence: {actual_input}."
|
|
71
|
+
)
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cpf-dv
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Utility resources to calculate check digits on CPF (Brazilian personal ID)
|
|
5
|
+
Author: Julio L. Muller
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://cnpj-utils.vercel.app/
|
|
8
|
+
Project-URL: Source, https://github.com/LacusSolutions/br-utils-py
|
|
9
|
+
Project-URL: Tracker, https://github.com/LacusSolutions/br-utils-py/issues
|
|
10
|
+
Keywords: cpf,verificar,verificador,verify,verification,check,check-digit,check-digits,pt-br,br
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Natural Language :: Portuguese (Brazilian)
|
|
15
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX
|
|
18
|
+
Classifier: Operating System :: Unix
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
26
|
+
Classifier: Topic :: Utilities
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
[](https://pypi.org/project/cpf-dv)
|
|
35
|
+
[](https://pypi.org/project/cpf-dv)
|
|
36
|
+
[](https://www.python.org/)
|
|
37
|
+
[](https://github.com/LacusSolutions/br-utils-py/actions)
|
|
38
|
+
[](https://github.com/LacusSolutions/br-utils-py)
|
|
39
|
+
[](https://github.com/LacusSolutions/br-utils-py/blob/main/LICENSE)
|
|
40
|
+
|
|
41
|
+
Utility class to calculate check digits on CPF (Brazilian individual taxpayer ID).
|
|
42
|
+
|
|
43
|
+
## Python Support
|
|
44
|
+
|
|
45
|
+
|  |  |  |  |  |
|
|
46
|
+
|--- | --- | --- | --- | --- |
|
|
47
|
+
| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ |
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
$ pip install cpf-dv
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Import
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from cpf_dv import CpfCheckDigits
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Basic Usage
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
# Calculate check digits from a 9-digit CPF base
|
|
67
|
+
check_digits = CpfCheckDigits("054496519")
|
|
68
|
+
|
|
69
|
+
print(check_digits.first_digit) # returns 1
|
|
70
|
+
print(check_digits.second_digit) # returns 0
|
|
71
|
+
print(check_digits.to_string()) # returns '05449651910'
|
|
72
|
+
print(check_digits.to_list()) # returns [0, 5, 4, 4, 9, 6, 5, 1, 9, 1, 0]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Input Formats
|
|
76
|
+
|
|
77
|
+
The `CpfCheckDigits` class accepts multiple input formats:
|
|
78
|
+
|
|
79
|
+
#### String Input
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# Plain string (non-numeric characters are automatically stripped)
|
|
83
|
+
check_digits = CpfCheckDigits("054496519")
|
|
84
|
+
check_digits = CpfCheckDigits("054.496.519-10") # formatting is ignored
|
|
85
|
+
check_digits = CpfCheckDigits("054496519") # 9 digits
|
|
86
|
+
check_digits = CpfCheckDigits("05449651910") # 11 digits (only first 9 are used)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### List of Strings
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# List of single-character strings
|
|
93
|
+
check_digits = CpfCheckDigits(["0", "5", "4", "4", "9", "6", "5", "1", "9"])
|
|
94
|
+
|
|
95
|
+
# List with multi-digit strings (automatically flattened)
|
|
96
|
+
check_digits = CpfCheckDigits(["054496519"]) # flattens to individual digits
|
|
97
|
+
check_digits = CpfCheckDigits(["054", "496", "519"]) # also flattens
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### List of Integers
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# List of single-digit integers
|
|
104
|
+
check_digits = CpfCheckDigits([1, 2, 3, 4, 5, 6, 7, 8, 9])
|
|
105
|
+
|
|
106
|
+
# List with multi-digit integers (automatically flattened)
|
|
107
|
+
check_digits = CpfCheckDigits([123456789]) # flattens to individual digits
|
|
108
|
+
check_digits = CpfCheckDigits([123, 456, 789]) # also flattens
|
|
109
|
+
|
|
110
|
+
### Properties
|
|
111
|
+
|
|
112
|
+
#### `first_digit: int`
|
|
113
|
+
|
|
114
|
+
Returns the first check digit (10th digit of the CPF).
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
check_digits = CpfCheckDigits("054496519")
|
|
118
|
+
print(check_digits.first_digit) # returns 1
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `second_digit: int`
|
|
122
|
+
|
|
123
|
+
Returns the second check digit (11th digit of the CPF).
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
check_digits = CpfCheckDigits("054496519")
|
|
127
|
+
print(check_digits.second_digit) # returns 0
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Methods
|
|
131
|
+
|
|
132
|
+
#### `to_list() -> list[int]`
|
|
133
|
+
|
|
134
|
+
Returns the complete CPF as a list of integers (9 base digits + 2 check digits).
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
check_digits = CpfCheckDigits("054496519")
|
|
138
|
+
print(check_digits.to_list()) # returns [0, 5, 4, 4, 9, 6, 5, 1, 9, 1, 0]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `to_string() -> str`
|
|
142
|
+
|
|
143
|
+
Returns the complete CPF as a string (9 base digits + 2 check digits).
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
check_digits = CpfCheckDigits("054496519")
|
|
147
|
+
print(check_digits.to_string()) # returns '05449651910'
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Examples
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from cpf_dv import CpfCheckDigits
|
|
154
|
+
|
|
155
|
+
# Calculate check digits for a CPF base
|
|
156
|
+
base = "054496519"
|
|
157
|
+
check_digits = CpfCheckDigits(base)
|
|
158
|
+
|
|
159
|
+
# Get individual check digits
|
|
160
|
+
first = check_digits.first_digit # 1
|
|
161
|
+
second = check_digits.second_digit # 0
|
|
162
|
+
|
|
163
|
+
# Get complete CPF
|
|
164
|
+
complete = check_digits.to_string() # '05449651910'
|
|
165
|
+
|
|
166
|
+
# Work with formatted input
|
|
167
|
+
formatted = CpfCheckDigits("054.496.519-10")
|
|
168
|
+
print(formatted.to_string()) # '05449651910'
|
|
169
|
+
|
|
170
|
+
# Work with list input
|
|
171
|
+
list_input = CpfCheckDigits([0, 5, 4, 4, 9, 6, 5, 1, 9])
|
|
172
|
+
print(list_input.to_string()) # '05449651910'
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Error Handling
|
|
176
|
+
|
|
177
|
+
The package raises specific exceptions for different error scenarios:
|
|
178
|
+
|
|
179
|
+
### `CpfCheckDigitsInputTypeError`
|
|
180
|
+
|
|
181
|
+
Raised when the input type is not supported (must be `str`, `list[str]`, or `list[int]`).
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from cpf_dv import CpfCheckDigits, CpfCheckDigitsInputTypeError
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
CpfCheckDigits(12345678901) # int not allowed
|
|
188
|
+
except CpfCheckDigitsInputTypeError as e:
|
|
189
|
+
print(e) # CPF input must be of type str, list[str] or list[int]. Got int.
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `CpfCheckDigitsInputLengthError`
|
|
193
|
+
|
|
194
|
+
Raised when the input does not contain 9 to 11 digits.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from cpf_dv import CpfCheckDigits, CpfCheckDigitsInputLengthError
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
CpfCheckDigits("12345678") # only 8 digits
|
|
201
|
+
except CpfCheckDigitsInputLengthError as e:
|
|
202
|
+
print(e) # CPF input "12345678" does not contain 9 to 11 digits. Got 8 in "12345678".
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### `CpfCheckDigitsInputNotValidError`
|
|
206
|
+
|
|
207
|
+
Raised when the input is forbidden for some restriction, like repeated digits like `111.111.111`, `222.222.222`, `333.333.333` and so on.
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from cpf_dv import CpfCheckDigits, CpfCheckDigitsInputNotValidError
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
CpfCheckDigits(["999", "999", "999"])
|
|
214
|
+
except CpfCheckDigitsInputNotValidError as e:
|
|
215
|
+
print(e) # CPF input ['999', '999', '999'] is invalid. Repeated digits are not considered valid.
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Catch any error from the package
|
|
219
|
+
|
|
220
|
+
All errors extend from a common error instance `CpfCheckDigitsError`, so you can use this type to handle any error thrown by the module.
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from cpf_dv import CpfCheckDigitsError
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
# some risky code run
|
|
227
|
+
except CpfCheckDigitsError as e:
|
|
228
|
+
# do something
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Features
|
|
232
|
+
|
|
233
|
+
- ✅ **Multiple Input Formats**: Accepts strings, lists of strings, or lists of integers
|
|
234
|
+
- ✅ **Format Agnostic**: Automatically strips non-numeric characters from string input
|
|
235
|
+
- ✅ **Auto-Expansion**: Automatically expands multi-digit numbers in lists to individual digits
|
|
236
|
+
- ✅ **Lazy Evaluation**: Check digits are calculated only when accessed (via properties)
|
|
237
|
+
- ✅ **Type Safety**: Built with Python 3.10+ type hints
|
|
238
|
+
- ✅ **Zero Dependencies**: No external dependencies required
|
|
239
|
+
- ✅ **Comprehensive Error Handling**: Specific exceptions for different error scenarios
|
|
240
|
+
|
|
241
|
+
## API Reference
|
|
242
|
+
|
|
243
|
+
### CpfCheckDigits Class
|
|
244
|
+
|
|
245
|
+
#### Constructor
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
CpfCheckDigits(cpf_digits: str | list[str] | list[int]) -> CpfCheckDigits
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Creates a new `CpfCheckDigits` instance from the provided CPF base digits.
|
|
252
|
+
|
|
253
|
+
**Parameters:**
|
|
254
|
+
- `cpf_digits` (str | list[str] | list[int]): The CPF base digits (9-11 digits). Can be:
|
|
255
|
+
- A string with 9-11 digits (formatting characters are ignored)
|
|
256
|
+
- A list of strings (each string can be a single digit or multi-digit number)
|
|
257
|
+
- A list of integers (each integer can be a single digit or multi-digit number)
|
|
258
|
+
|
|
259
|
+
**Raises:**
|
|
260
|
+
- `CpfCheckDigitsInputTypeError`: If the input type is not supported
|
|
261
|
+
- `CpfCheckDigitsInputLengthError`: If the input does not contain 9-11 digits
|
|
262
|
+
|
|
263
|
+
**Returns:**
|
|
264
|
+
- `CpfCheckDigits`: A new instance ready to calculate check digits
|
|
265
|
+
|
|
266
|
+
#### Properties
|
|
267
|
+
|
|
268
|
+
##### `first_digit: int`
|
|
269
|
+
|
|
270
|
+
The first check digit (10th digit of the CPF). Calculated lazily on first access.
|
|
271
|
+
|
|
272
|
+
##### `second_digit: int`
|
|
273
|
+
|
|
274
|
+
The second check digit (11th digit of the CPF). Calculated lazily on first access.
|
|
275
|
+
|
|
276
|
+
#### Methods
|
|
277
|
+
|
|
278
|
+
##### `to_list() -> list[int]`
|
|
279
|
+
|
|
280
|
+
Returns the complete CPF as a list of 11 integers (9 base digits + 2 check digits).
|
|
281
|
+
|
|
282
|
+
##### `to_string() -> str`
|
|
283
|
+
|
|
284
|
+
Returns the complete CPF as a string of 11 digits (9 base digits + 2 check digits).
|
|
285
|
+
|
|
286
|
+
## Calculation Algorithm
|
|
287
|
+
|
|
288
|
+
The package calculates CPF check digits using the official Brazilian algorithm:
|
|
289
|
+
|
|
290
|
+
1. **First Check Digit (10th position)**:
|
|
291
|
+
- Uses digits 1-9 of the CPF base
|
|
292
|
+
- Applies weights: 10, 9, 8, 7, 6, 5, 4, 3, 2 (from left to right)
|
|
293
|
+
- Calculates: `sum(digit × weight) % 11`
|
|
294
|
+
- Result: `0` if remainder > 9, otherwise `11 - remainder`
|
|
295
|
+
|
|
296
|
+
2. **Second Check Digit (11th position)**:
|
|
297
|
+
- Uses digits 1-9 + first check digit
|
|
298
|
+
- Applies weights: 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 (from left to right)
|
|
299
|
+
- Calculates: `sum(digit × weight) % 11`
|
|
300
|
+
- Result: `0` if remainder > 9, otherwise `11 - remainder`
|
|
301
|
+
|
|
302
|
+
## Dependencies
|
|
303
|
+
|
|
304
|
+
- **Python**: >= 3.10
|
|
305
|
+
|
|
306
|
+
No external dependencies required.
|
|
307
|
+
|
|
308
|
+
## Contribution & Support
|
|
309
|
+
|
|
310
|
+
We welcome contributions! Please see our [Contributing Guidelines](https://github.com/LacusSolutions/br-utils-py/blob/main/CONTRIBUTING.md) for details. But if you find this project helpful, please consider:
|
|
311
|
+
|
|
312
|
+
- ⭐ Starring the repository
|
|
313
|
+
- 🤝 Contributing to the codebase
|
|
314
|
+
- 💡 [Suggesting new features](https://github.com/LacusSolutions/br-utils-py/issues)
|
|
315
|
+
- 🐛 [Reporting bugs](https://github.com/LacusSolutions/br-utils-py/issues)
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
This project is licensed under the MIT License - see the [LICENSE](https://github.com/LacusSolutions/br-utils-py/blob/main/LICENSE) file for details.
|
|
320
|
+
|
|
321
|
+
## Changelog
|
|
322
|
+
|
|
323
|
+
See [CHANGELOG](https://github.com/LacusSolutions/br-utils-py/blob/main/packages/cpf-dv/CHANGELOG.md) for a list of changes and version history.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
Made with ❤️ by [Lacus Solutions](https://github.com/LacusSolutions)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
cpf_dv/__init__.py,sha256=q9yjXOcCXevAco_TgAXX6dhBWiZ4ofjVZTS7ksgiHZE,481
|
|
2
|
+
cpf_dv/cpf_check_digits.py,sha256=80x7n-Q_W_hZyxFBBKIN_lBOr1DeBAvWZ7WtTcAiiJM,5949
|
|
3
|
+
cpf_dv/exceptions.py,sha256=blguXSbznmVFgtB_g5HXI-4SbHQOEBRQ7H0XnHFPCBE,2455
|
|
4
|
+
cpf_dv-1.0.0.dist-info/licenses/LICENSE,sha256=6Q_pGT36dadTRTG16NLBYQGLRaiWtZ2DA3kwzd_32nk,1072
|
|
5
|
+
cpf_dv-1.0.0.dist-info/METADATA,sha256=bnkDonYEu8pLaOVhNC45Fw17alqywyJ9O3M7CQImgbo,10966
|
|
6
|
+
cpf_dv-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
7
|
+
cpf_dv-1.0.0.dist-info/top_level.txt,sha256=vgQZHkvUD0YCyUyQ-YhOYdH2flcdyCjRsK1w_TXiXmo,7
|
|
8
|
+
cpf_dv-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Julio L. Muller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cpf_dv
|