Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs #5

Merged
merged 15 commits into from
Aug 27, 2023
Merged
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
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ repos:
hooks:
- id: isort
args: [--profile=black]

- repo: local
hooks:
- id: generate_docs
name: Generate Docs (README.md)
entry: python scripts/generate_docs.py
language: python
pass_filenames: false
86 changes: 85 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,87 @@

# fluent_validator

Package to validate data in a fluent way
**Validate Your Data with Ease!**

[![GitHub stars](https://img.shields.io/github/stars/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/network)
[![GitHub issues](https://img.shields.io/github/issues/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/issues)
[![GitHub license](https://img.shields.io/github/license/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/blob/main/LICENSE)

## Overview

`fluent_validator` is a Python package that makes data validation a breeze! Say goodbye to complex, nested if statements and hello to a fluent and expressive way to validate your data. With `fluent_validator`, you can easily define and execute validation rules for your data in a clean and readable manner.

## Features

- **Fluent Syntax**: Define validation rules in a clean and fluent manner.
- **No Extra Dependencies**: `fluent_validator` is lightweight and doesn't require any additional packages.
- **Python 3.7+ Support**: It works seamlessly with Python versions 3.7, 3.8, 3.9, 3.10, and 3.11.
- **Extensive Validation Library**: Check out our extensive list of available validations to cover all your validation needs.

## Installation

You can install `fluent_validator` using pip:

```bash
pip install fluent_validator
````

## Usage

Here's a quick example of how to use `fluent_validator`:

```python
from fluent_validator import validate, validate_all

# Validate a single value
validate(10).not_is_none().greater_than(5).not_equal(40)

# Or validate multiple values
validate_all(10, 100).not_is_none().greater_than(5).not_equal(40)
```

## Available Validations

`fluent_validator` offers a wide range of validations to suit your needs.

Notably, all validations have a corresponding negative form. Simply prefix the method with `not_`.

For example, the negative of `is_none()` is `not_is_none()`.

### Check out the full list of available above.

| Validation | Description |
| --- | --- |
| `between(min_vl, max_vl)` | Check if the object is within the specified range. |
| `contains_at_least(value)` | Check if the object (assumed to be iterable) contains at least the specified number of elements. |
| `contains_at_most(value)` | Check if the object (assumed to be iterable) contains at most the specified number of elements. |
| `contains_exactly(value)` | Check if the object (assumed to be iterable) contains exactly the specified number of elements. |
| `equal(value)` | Check if the object is equal to the specified value. |
| `greater_or_equal_than(value)` | Check if the object is greater than or equal to the specified value. |
| `greater_than(value)` | Check if the object is greater than the specified value. |
| `has_unique_values()` | Check if the object (assumed to be iterable) contains unique values. Note: This function assumes that the object's elements are hashable. |
| `is_bool()` | Check if the object is a boolean. |
| `is_callable()` | Check if the object is callable (e.g., a function or method). |
| `is_false()` | Check if the object is a boolean and has a value of False. |
| `is_in()` | Check if the object is in a collection of values. |
| `is_instance()` | Check if the object is an instance of one or more specified types. |
| `is_iterable()` | Check if the object is iterable. |
| `is_none()` | Check if the object is None. |
| `is_number()` | Check if the object is a number (int or float). |
| `is_string()` | Check if the object is a string. |
| `is_true()` | Check if the object is a boolean and has a value of True. |
| `less_or_equal_than(value)` | Check if the object is less than or equal to the specified value. |
| `less_than(value)` | Check if the object is less than the specified value. |
| `max(value)` | Check if the object is less than or equal to the specified maximum value. |
| `min(value)` | Check if the object is greater than or equal to the specified minimum value. |

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support

If you encounter any issues or have questions about `fluent_validator`, please feel free to [open an issue](https://github.com/mariotaddeucci/fluent_validator/issues). We're here to help!

Happy Validating! 🚀
31 changes: 25 additions & 6 deletions fluent_validator/validators/type_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,39 @@


class TypeValidator(BaseValidator):
def _is_instance(self, *args) -> bool:
def _is_instance(self, *args):
"""
Check if the object is an instance of one or more specified types.
"""

return isinstance(self.obj, args)

def _is_callable(self) -> bool:
def _is_callable(self):
"""
Check if the object is callable (e.g., a function or method).
"""
return callable(self.obj)

def _is_iterable(self) -> bool:
def _is_iterable(self):
"""
Check if the object is iterable.
"""
return self._is_instance(Iterable)

def _is_string(self) -> bool:
def _is_string(self):
"""
Check if the object is a string.
"""
return self._is_instance(str)

def _is_number(self) -> bool:
def _is_number(self):
"""
Check if the object is a number (int or float).
"""
return self._is_instance(int, float)

def _is_bool(self) -> bool:
def _is_bool(self):
"""
Check if the object is a boolean.
"""
return self._is_instance(bool)
86 changes: 68 additions & 18 deletions fluent_validator/validators/value_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,100 @@


class ValueValidator(TypeValidator):
def _equal(self, value) -> bool:
def _equal(self, value):
"""
Check if the object is equal to the specified value.
"""
return self.obj == value

def _is_in(self, *args) -> bool:
return self.obj in args
def _is_in(self, *values):
"""
Check if the object is in a collection of values.
"""
return self.obj in values

def _greater_than(self, value) -> bool:
def _greater_than(self, value):
"""
Check if the object is greater than the specified value.
"""
return self.obj > value

def _less_than(self, value) -> bool:
def _less_than(self, value):
"""
Check if the object is less than the specified value.
"""
return self.obj < value

def _greater_or_equal_than(self, value) -> bool:
def _greater_or_equal_than(self, value):
"""
Check if the object is greater than or equal to the specified value.
"""
return self.obj >= value

def _less_or_equal_than(self, value) -> bool:
def _less_or_equal_than(self, value):
"""
Check if the object is less than or equal to the specified value.
"""
return self.obj <= value

def _min(self, value) -> bool:
def _min(self, value):
"""
Check if the object is greater than or equal to the specified minimum value.
"""
return self._greater_or_equal_than(value)

def _max(self, value) -> bool:
def _max(self, value):
"""
Check if the object is less than or equal to the specified maximum value.
"""
return self._less_or_equal_than(value)

def _between(self, min, max) -> bool:
return self._min(min) and self._max(max)
def _between(self, min_vl, max_vl):
"""
Check if the object is within the specified range.
"""
return self._min(min_vl) and self._max(max_vl)

def _is_true(self) -> bool:
def _is_true(self):
"""
Check if the object is a boolean and has a value of True.
"""
return self._is_bool() and self.obj is True

def _is_false(self) -> bool:
def _is_false(self):
"""
Check if the object is a boolean and has a value of False.
"""
return self._is_bool() and self.obj is False

def _contains_at_least(self, value) -> bool:
def _contains_at_least(self, value):
"""
Check if the object (assumed to be iterable) contains at least the specified number of elements.
"""
return len(self.obj) >= value

def _contains_at_most(self, value) -> bool:
def _contains_at_most(self, value):
"""
Check if the object (assumed to be iterable) contains at most the specified number of elements.
"""
return len(self.obj) <= value

def _contains_exactly(self, value) -> bool:
def _contains_exactly(self, value):
"""
Check if the object (assumed to be iterable) contains exactly the specified number of elements.
"""
return len(self.obj) == value

def _is_none(self) -> bool:
def _is_none(self):
"""
Check if the object is None.
"""
return self.obj is None

def _has_unique_values(self) -> bool:
def _has_unique_values(self):
"""
Check if the object (assumed to be iterable) contains unique values.

Note: This function assumes that the object's elements are hashable.
"""
return len(self.obj) == len(set(self.obj))
112 changes: 112 additions & 0 deletions scripts/generate_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import ast
import glob
import os

TEMPLATE = """
# fluent_validator

**Validate Your Data with Ease!**

[![GitHub stars](https://img.shields.io/github/stars/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/network)
[![GitHub issues](https://img.shields.io/github/issues/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/issues)
[![GitHub license](https://img.shields.io/github/license/mariotaddeucci/fluent_validator.svg?style=flat-square)](https://github.com/mariotaddeucci/fluent_validator/blob/main/LICENSE)

## Overview

`fluent_validator` is a Python package that makes data validation a breeze! Say goodbye to complex, nested if statements and hello to a fluent and expressive way to validate your data. With `fluent_validator`, you can easily define and execute validation rules for your data in a clean and readable manner.

## Features

- **Fluent Syntax**: Define validation rules in a clean and fluent manner.
- **No Extra Dependencies**: `fluent_validator` is lightweight and doesn't require any additional packages.
- **Python 3.7+ Support**: It works seamlessly with Python versions 3.7, 3.8, 3.9, 3.10, and 3.11.
- **Extensive Validation Library**: Check out our extensive list of available validations to cover all your validation needs.

## Installation

You can install `fluent_validator` using pip:

```bash
pip install fluent_validator
````

## Usage

Here's a quick example of how to use `fluent_validator`:

```python
from fluent_validator import validate, validate_all

# Validate a single value
validate(10).not_is_none().greater_than(5).not_equal(40)

# Or validate multiple values
validate_all(10, 100).not_is_none().greater_than(5).not_equal(40)
```

## Available Validations

`fluent_validator` offers a wide range of validations to suit your needs.

Notably, all validations have a corresponding negative form. Simply prefix the method with `not_`.

For example, the negative of `is_none()` is `not_is_none()`.

### Check out the full list of available above.

| Validation | Description |
| --- | --- |
{{ validation_list }}

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Support

If you encounter any issues or have questions about `fluent_validator`, please feel free to [open an issue](https://github.com/mariotaddeucci/fluent_validator/issues). We're here to help!

Happy Validating! 🚀
"""


def extract_validators_docs(filename):
with open(filename, "r", encoding="utf-8") as file:
tree = ast.parse(file.read())

for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
docstring = ast.get_docstring(node)
if docstring:
docstring = [
line.strip() for line in docstring.split("\n") if line.strip() != ""
]
docstring = " ".join(docstring)

method_name = node.name.strip("_")
args = [arg.arg for arg in node.args.args if arg.arg != "self"]
yield f"| `{method_name}({', '.join(args)})` | {docstring} |"


def main():
project_dir = os.path.join(os.path.dirname(__file__), "..")
validators_dir = os.path.join(project_dir, "fluent_validator", "validators")
output_file = os.path.join(project_dir, "README.md")

validations_list = [
validator_doc.strip()
for file in glob.glob(os.path.join(validators_dir, "*.py"))
for validator_doc in extract_validators_docs(file)
]

content = TEMPLATE.replace(
"{{ validation_list }}", "\n".join(sorted(validations_list))
)

with open(output_file, "w", encoding="utf-8") as file:
file.write(content)


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion tests/validator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


def test_chained_validation():
validate(True).is_true().not_is_false().not_is_none()
validate(10).not_is_none().greater_than(5).not_equal(40)

with pytest.raises(ValueError):
validate(True).is_false()
Expand Down