<table>
<tr>
<td>

### ⛔ ⛔ ⛔ DEPRECATED PACKAGE ⛔ ⛔ ⛔

**This package (`cpf-cd`) is no longer maintained and will not receive further updates.**

For consistency reasons across **BR Utils** initiative, this project was renamed to **[`cpf-dv`](https://pypi.org/project/cpf-dv/)**.

```bash
pip uninstall cpf-cd
pip install cpf-dv
```

And since v1 of `cpf-dv` follows the same API of `cpf-cd`, updating the imports should be enough:

```diff
- from cpf_cd import CpfCheckDigits
+ from cpf_dv import CpfCheckDigits
```

Visit the [cpf-dv repository](https://github.com/LacusSolutions/br-utils-py/tree/main/packages/cpf-dv) for additional information and support.

</td>
</tr>
</table>

---

![cpf-cd for Python](https://br-utils.vercel.app/img/cover_cpf-cd.jpg)

[![PyPI Version](https://img.shields.io/pypi/v/cpf-cd)](https://pypi.org/project/cpf-cd)
[![PyPI Downloads](https://img.shields.io/pypi/dm/cpf-cd)](https://pypi.org/project/cpf-cd)
[![Python Version](https://img.shields.io/pypi/pyversions/cpf-cd)](https://www.python.org/)
[![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)
[![Last Update Date](https://img.shields.io/github/last-commit/LacusSolutions/br-utils-py)](https://github.com/LacusSolutions/br-utils-py)
[![Project License](https://img.shields.io/github/license/LacusSolutions/br-utils-py)](https://github.com/LacusSolutions/br-utils-py/blob/main/LICENSE)

Utility class to calculate check digits on CPF (Brazilian individual taxpayer ID).

## Python Support

| ![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) |
|--- | --- | --- | --- | --- |
| Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ | Passing ✔ |

## Installation

```bash
$ pip install cpf-cd
```

## Import

```python
from cpf_cd import CpfCheckDigits
```

## Usage

### Basic Usage

```python
# Calculate check digits from a 9-digit CPF base
check_digits = CpfCheckDigits("054496519")

print(check_digits.first_digit)    # returns 1
print(check_digits.second_digit)   # returns 0
print(check_digits.to_string())    # returns '05449651910'
print(check_digits.to_list())      # returns [0, 5, 4, 4, 9, 6, 5, 1, 9, 1, 0]
```

### Input Formats

The `CpfCheckDigits` class accepts multiple input formats:

#### String Input

```python
# Plain string (non-numeric characters are automatically stripped)
check_digits = CpfCheckDigits("054496519")
check_digits = CpfCheckDigits("054.496.519-10")  # formatting is ignored
check_digits = CpfCheckDigits("054496519")        # 9 digits
check_digits = CpfCheckDigits("05449651910")     # 11 digits (only first 9 are used)
```

#### List of Strings

```python
# List of single-character strings
check_digits = CpfCheckDigits(["0", "5", "4", "4", "9", "6", "5", "1", "9"])

# List with multi-digit strings (automatically flattened)
check_digits = CpfCheckDigits(["054496519"])      # flattens to individual digits
check_digits = CpfCheckDigits(["054", "496", "519"])  # also flattens
```

#### List of Integers

```python
# List of single-digit integers
check_digits = CpfCheckDigits([1, 2, 3, 4, 5, 6, 7, 8, 9])

# List with multi-digit integers (automatically flattened)
check_digits = CpfCheckDigits([123456789])         # flattens to individual digits
check_digits = CpfCheckDigits([123, 456, 789])     # also flattens

### Properties

#### `first_digit: int`

Returns the first check digit (10th digit of the CPF).

```python
check_digits = CpfCheckDigits("054496519")
print(check_digits.first_digit)  # returns 1
```

#### `second_digit: int`

Returns the second check digit (11th digit of the CPF).

```python
check_digits = CpfCheckDigits("054496519")
print(check_digits.second_digit)  # returns 0
```

### Methods

#### `to_list() -> list[int]`

Returns the complete CPF as a list of integers (9 base digits + 2 check digits).

```python
check_digits = CpfCheckDigits("054496519")
print(check_digits.to_list())  # returns [0, 5, 4, 4, 9, 6, 5, 1, 9, 1, 0]
```

#### `to_string() -> str`

Returns the complete CPF as a string (9 base digits + 2 check digits).

```python
check_digits = CpfCheckDigits("054496519")
print(check_digits.to_string())  # returns '05449651910'
```

### Examples

```python
from cpf_cd import CpfCheckDigits

# Calculate check digits for a CPF base
base = "054496519"
check_digits = CpfCheckDigits(base)

# Get individual check digits
first = check_digits.first_digit    # 1
second = check_digits.second_digit   # 0

# Get complete CPF
complete = check_digits.to_string()  # '05449651910'

# Work with formatted input
formatted = CpfCheckDigits("054.496.519-10")
print(formatted.to_string())  # '05449651910'

# Work with list input
list_input = CpfCheckDigits([0, 5, 4, 4, 9, 6, 5, 1, 9])
print(list_input.to_string())  # '05449651910'
```

## Error Handling

The package raises specific exceptions for different error scenarios:

### `CpfCheckDigitsInputTypeError`

Raised when the input type is not supported (must be `str`, `list[str]`, or `list[int]`).

```python
from cpf_cd import CpfCheckDigits, CpfCheckDigitsInputTypeError

try:
    CpfCheckDigits(12345678901)  # int not allowed
except CpfCheckDigitsInputTypeError as e:
    print(e)  # CPF input must be of type str, list[str] or list[int]. Got int.
```

### `CpfCheckDigitsInputLengthError`

Raised when the input does not contain 9 to 11 digits.

```python
from cpf_cd import CpfCheckDigits, CpfCheckDigitsInputLengthError

try:
    CpfCheckDigits("12345678")  # only 8 digits
except CpfCheckDigitsInputLengthError as e:
    print(e)  # CPF input "12345678" does not contain 9 to 11 digits. Got 8 in "12345678".
```

### `CpfCheckDigitsInputNotValidError`

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.

```python
from cpf_cd import CpfCheckDigits, CpfCheckDigitsInputNotValidError

try:
    CpfCheckDigits(["999", "999", "999"])
except CpfCheckDigitsInputNotValidError as e:
    print(e)  # CPF input ['999', '999', '999'] is invalid. Repeated digits are not considered valid.
```

### Catch any error from the package

All errors extend from a common error instance `CpfCheckDigitsError`, so you can use this type to handle any error thrown by the module.

```python
from cpf_cd import CpfCheckDigitsError

try:
  # some risky code run
except CpfCheckDigitsError as e:
  # do something
```

## Features

- ✅ **Multiple Input Formats**: Accepts strings, lists of strings, or lists of integers
- ✅ **Format Agnostic**: Automatically strips non-numeric characters from string input
- ✅ **Auto-Expansion**: Automatically expands multi-digit numbers in lists to individual digits
- ✅ **Lazy Evaluation**: Check digits are calculated only when accessed (via properties)
- ✅ **Type Safety**: Built with Python 3.10+ type hints
- ✅ **Zero Dependencies**: No external dependencies required
- ✅ **Comprehensive Error Handling**: Specific exceptions for different error scenarios

## API Reference

### CpfCheckDigits Class

#### Constructor

```python
CpfCheckDigits(cpf_digits: str | list[str] | list[int]) -> CpfCheckDigits
```

Creates a new `CpfCheckDigits` instance from the provided CPF base digits.

**Parameters:**
- `cpf_digits` (str | list[str] | list[int]): The CPF base digits (9-11 digits). Can be:
  - A string with 9-11 digits (formatting characters are ignored)
  - A list of strings (each string can be a single digit or multi-digit number)
  - A list of integers (each integer can be a single digit or multi-digit number)

**Raises:**
- `CpfCheckDigitsInputTypeError`: If the input type is not supported
- `CpfCheckDigitsInputLengthError`: If the input does not contain 9-11 digits

**Returns:**
- `CpfCheckDigits`: A new instance ready to calculate check digits

#### Properties

##### `first_digit: int`

The first check digit (10th digit of the CPF). Calculated lazily on first access.

##### `second_digit: int`

The second check digit (11th digit of the CPF). Calculated lazily on first access.

#### Methods

##### `to_list() -> list[int]`

Returns the complete CPF as a list of 11 integers (9 base digits + 2 check digits).

##### `to_string() -> str`

Returns the complete CPF as a string of 11 digits (9 base digits + 2 check digits).

## Calculation Algorithm

The package calculates CPF check digits using the official Brazilian algorithm:

1. **First Check Digit (10th position)**:
   - Uses digits 1-9 of the CPF base
   - Applies weights: 10, 9, 8, 7, 6, 5, 4, 3, 2 (from left to right)
   - Calculates: `sum(digit × weight) % 11`
   - Result: `0` if remainder > 9, otherwise `11 - remainder`

2. **Second Check Digit (11th position)**:
   - Uses digits 1-9 + first check digit
   - Applies weights: 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 (from left to right)
   - Calculates: `sum(digit × weight) % 11`
   - Result: `0` if remainder > 9, otherwise `11 - remainder`

## Dependencies

- **Python**: >= 3.10

No external dependencies required.

## Contribution & Support

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:

- ⭐ Starring the repository
- 🤝 Contributing to the codebase
- 💡 [Suggesting new features](https://github.com/LacusSolutions/br-utils-py/issues)
- 🐛 [Reporting bugs](https://github.com/LacusSolutions/br-utils-py/issues)

## License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/LacusSolutions/br-utils-py/blob/main/LICENSE) file for details.

## Changelog

See [CHANGELOG](https://github.com/LacusSolutions/br-utils-py/blob/main/packages/cpf-cd/CHANGELOG.md) for a list of changes and version history.

---

Made with ❤️ by [Lacus Solutions](https://github.com/LacusSolutions)
