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 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
+ ![cpf-dv for Python](https://br-utils.vercel.app/img/cover_cpf-dv.jpg)
33
+
34
+ [![PyPI Version](https://img.shields.io/pypi/v/cpf-dv)](https://pypi.org/project/cpf-dv)
35
+ [![PyPI Downloads](https://img.shields.io/pypi/dm/cpf-dv)](https://pypi.org/project/cpf-dv)
36
+ [![Python Version](https://img.shields.io/pypi/pyversions/cpf-dv)](https://www.python.org/)
37
+ [![Test Status](https://img.shields.io/github/actions/workflow/status/LacusSolutions/br-utils-py/ci.yml?label=ci/cd)](https://github.com/LacusSolutions/br-utils-py/actions)
38
+ [![Last Update Date](https://img.shields.io/github/last-commit/LacusSolutions/br-utils-py)](https://github.com/LacusSolutions/br-utils-py)
39
+ [![Project License](https://img.shields.io/github/license/LacusSolutions/br-utils-py)](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
+ | ![Python 3.10](https://img.shields.io/badge/Python-3.10-3776AB?logo=python&logoColor=white) | ![Python 3.11](https://img.shields.io/badge/Python-3.11-3776AB?logo=python&logoColor=white) | ![Python 3.12](https://img.shields.io/badge/Python-3.12-3776AB?logo=python&logoColor=white) | ![Python 3.13](https://img.shields.io/badge/Python-3.13-3776AB?logo=python&logoColor=white) | ![Python 3.14](https://img.shields.io/badge/Python-3.14-3776AB?logo=python&logoColor=white) |
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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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