Skip to content
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
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ Python `3.7` or higher is required.
pip install nitro-validator
```

## AI Assistant Integration
### AI Assistant Integration

Add Nitro CLI knowledge to your AI coding assistant:
Add Nitro Validator knowledge to your AI coding assistant:

```bash
npx skills add nitrosh/nitro-validate
```

This enables AI assistants like Claude Code to understand Nitro Validator and generate correct validation code.

## Features

- **Simple API** - Easy to learn with minimal boilerplate
Expand Down Expand Up @@ -349,15 +351,17 @@ The `examples/` directory contains working examples:
python examples/basic_usage.py
python examples/custom_rules.py
python examples/advanced_validation.py
python examples/date_validation.py
python examples/formats.py
```

## Development

### Setup

```bash
git clone https://github.com/nitro/nitro-validator.git
cd nitro-validator
git clone https://github.com/nitrosh/nitro-validate.git
cd nitro-validate
pip install -e ".[dev]"
```

Expand Down Expand Up @@ -395,10 +399,11 @@ Nitro Validator is inspired by [GUMP](https://github.com/Wixel/GUMP) (a PHP vali

## Ecosystem

- **[nitro-ui](https://github.com/nitrosh/nitro-ui)** - Programmatic HTML generation
- **[nitro-datastore](https://github.com/nitrosh/nitro-datastore)** - Data loading with dot notation access
- **[nitro-dispatch](https://github.com/nitrosh/nitro-dispatch)** - Plugin system
- **[nitro-validate](https://github.com/nitrosh/nitro-validate)** - Data validation
- **[nitro-cli](https://github.com/nitrosh/nitro-cli)** - Static site generator that builds sites with Python code
- **[nitro-ui](https://github.com/nitrosh/nitro-ui)** - Build HTML with Python, not strings
- **[nitro-datastore](https://github.com/nitrosh/nitro-datastore)** - Schema-free JSON data store with dot notation access
- **[nitro-dispatch](https://github.com/nitrosh/nitro-dispatch)** - Framework-agnostic plugin system
- **[nitro-image](https://github.com/nitrosh/nitro-image)** - Fast, friendly image processing for the web

## License

Expand Down
2 changes: 1 addition & 1 deletion examples/advanced_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def example_optional_fields():
validator = Validator()

data = {
"name": "John Doe",
"name": "John",
# middle_name is not provided (optional)
# phone is not provided (optional)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def example_basic_validation():
data = {"username": "john_doe", "email": "john@example.com", "age": 25}

rules = {
"username": "required|alphanumeric",
"username": "required|alpha_dash",
"email": "required|email",
"age": "required|numeric|min:18",
}
Expand Down
118 changes: 118 additions & 0 deletions examples/date_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""
Date validation examples.

Covers the five date rules: date, before, after, date_equals, date_format.

Note: date, before, after, and date_equals accept only unambiguous ISO 8601
formats (YYYY-MM-DD, YYYY/MM/DD, and datetime variants). For regional
formats like DD-MM-YYYY, use date_format with an explicit format string.
"""

from nitro_validator import Validator, ValidationError


def example_basic_date():
"""Valid ISO 8601 dates pass the date rule."""
print("=== Basic Date Validation ===\n")

validator = Validator()

data = {"birthdate": "1990-06-15", "joined_at": "2024-01-10T09:30:00"}
rules = {"birthdate": "required|date", "joined_at": "required|date"}

try:
validated = validator.validate(data, rules)
print("✓ ISO 8601 dates accepted!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


def example_ambiguous_format_rejected():
"""Regional formats are rejected by the date rule."""
print("=== Ambiguous Format Rejection ===\n")

validator = Validator()

# DD-MM-YYYY and MM-DD-YYYY are indistinguishable — the date rule rejects both
data = {"event_date": "15-06-1990"}
rules = {"event_date": "required|date"}

try:
validator.validate(data, rules)
print("✓ Passed (unexpected!)")
except ValidationError as e:
print("✗ Rejected as expected (use date_format for regional formats):")
print(f" Errors: {e.errors}\n")


def example_before_after():
"""Compare a date against a reference date."""
print("=== Before / After ===\n")

validator = Validator()

data = {
"start_date": "2024-03-01",
"end_date": "2024-12-31",
}
rules = {
"start_date": "required|date|after:2024-01-01",
"end_date": "required|date|before:2025-01-01",
}

try:
validated = validator.validate(data, rules)
print("✓ Date range valid!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


def example_date_equals():
"""Require an exact date match."""
print("=== Date Equals ===\n")

validator = Validator()

data = {"launch_date": "2025-07-04"}
rules = {"launch_date": "required|date_equals:2025-07-04"}

try:
validator.validate(data, rules)
print("✓ Date matches expected value!\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


def example_date_format():
"""Use date_format for regional or non-ISO formats."""
print("=== Date Format (Regional) ===\n")

validator = Validator()

data = {
"uk_date": "15/06/1990", # DD/MM/YYYY
"us_date": "06-15-1990", # MM-DD-YYYY
"timestamp": "15 Jun 1990 09:30", # DD Mon YYYY HH:MM
}
rules = {
"uk_date": "required|date_format:%d/%m/%Y",
"us_date": "required|date_format:%m-%d-%Y",
"timestamp": "required|date_format:%d %b %Y %H:%M",
}

try:
validated = validator.validate(data, rules)
print("✓ All regional formats parsed!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


if __name__ == "__main__":
example_basic_date()
example_ambiguous_format_rejected()
example_before_after()
example_date_equals()
example_date_format()
132 changes: 132 additions & 0 deletions examples/formats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""
Format validation examples.

Covers identifier and format-string rules: uuid, url, ipv4, mac_address,
credit_card, hex_color, and regex.

Note: the string form splits on `|` (rules) and `,` (args). For regex
patterns containing those characters, pass a RegexRule object instead.
"""

from nitro_validator import Validator, ValidationError, RegexRule, RequiredRule


def example_identifiers():
"""UUID, URL, IPv4, and MAC address rules."""
print("=== Identifiers (UUID, URL, IPv4, MAC) ===\n")

validator = Validator()

data = {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"website": "https://example.com/about",
"server_ip": "192.168.1.42",
"nic": "00:1B:44:11:3A:B7",
}
rules = {
"request_id": "required|uuid",
"website": "required|url",
"server_ip": "required|ipv4",
"nic": "required|mac_address",
}

try:
validated = validator.validate(data, rules)
print("✓ All identifiers valid!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


def example_credit_card_and_color():
"""Credit card (Luhn-checked) and hex color codes."""
print("=== Credit Card & Hex Color ===\n")

validator = Validator()

# 4532015112830366 is a Luhn-valid test number
data = {
"card": "4532015112830366",
"brand_color": "#3366FF",
"accent": "#f0a",
}
rules = {
"card": "required|credit_card",
"brand_color": "required|hex_color",
"accent": "required|hex_color",
}

try:
validated = validator.validate(data, rules)
print("✓ Card number (Luhn) and colors valid!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


def example_invalid_credit_card():
"""A number that fails the Luhn check is rejected."""
print("=== Invalid Credit Card (Luhn Fail) ===\n")

validator = Validator()

data = {"card": "4532015112830367"} # last digit altered — Luhn fails
rules = {"card": "required|credit_card"}

try:
validator.validate(data, rules)
print("✓ Passed (unexpected!)")
except ValidationError as e:
print("✗ Rejected as expected:")
print(f" Errors: {e.errors}\n")


def example_regex_string_form():
"""Simple regex via the pipe-string syntax (no `|` or `,` in pattern)."""
print("=== Regex (String Form) ===\n")

validator = Validator()

# US zip code: 5 digits, optional -4 extension
data = {"zip": "94103", "zip_plus_four": "94103-1234"}
rules = {
"zip": "required|regex:^\\d{5}$",
"zip_plus_four": "required|regex:^\\d{5}-\\d{4}$",
}

try:
validated = validator.validate(data, rules)
print("✓ Both zip formats matched!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


def example_regex_rule_object():
"""Use the RegexRule object when the pattern contains `|` or `,`."""
print("=== Regex (Rule Object for Complex Patterns) ===\n")

validator = Validator()

# Pattern uses alternation (`|`) and a quantifier with a comma —
# both would be mis-parsed by the string form.
data = {"country_code": "GB", "postcode": "SW1A 1AA"}
rules = {
"country_code": [RequiredRule(), RegexRule(r"^(US|GB|CA|AU)$")],
"postcode": [RequiredRule(), RegexRule(r"^[A-Z]{1,2}\d{1,2}[A-Z]? \d[A-Z]{2}$")],
}

try:
validated = validator.validate(data, rules)
print("✓ Complex patterns matched!")
print(f"Validated data: {validated}\n")
except ValidationError as e:
print(f"✗ Validation failed: {e.errors}\n")


if __name__ == "__main__":
example_identifiers()
example_credit_card_and_color()
example_invalid_credit_card()
example_regex_string_form()
example_regex_rule_object()
2 changes: 1 addition & 1 deletion nitro_validator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
validated = validator.validate(data, rules)
"""

__version__ = "1.0.0"
__version__ = "1.0.2"

from .core import (
NitroValidator,
Expand Down
13 changes: 7 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ build-backend = "setuptools.build_meta"

[project]
name = "nitro-validator"
version = "1.0.1"
version = "1.0.2"
description = "A powerful, standalone, dependency-free data validation library for Python with extensible rules and a clean, intuitive API."
readme = "README.md"
authors = [
{ name = "Sean Nieuwoudt", email = "sean@nitro.sh" }
]
license = { text = "BSD-3-Clause" }
license-files = ["LICEN[CS]E*"]
requires-python = ">=3.7"
classifiers = [
"Operating System :: OS Independent",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
Expand All @@ -42,10 +43,10 @@ dev = [
]

[project.urls]
Homepage = "https://github.com/nitro/nitro-validator"
Documentation = "https://github.com/nitro/nitro-validator/blob/main/README.md"
Repository = "https://github.com/nitro/nitro-validator"
Issues = "https://github.com/nitro/nitro-validator/issues"
Homepage = "https://github.com/nitrosh/nitro-validate"
Documentation = "https://github.com/nitrosh/nitro-validate/blob/main/README.md"
Repository = "https://github.com/nitrosh/nitro-validate"
Issues = "https://github.com/nitrosh/nitro-validate/issues"

[tool.setuptools]
packages = ["nitro_validator", "nitro_validator.core", "nitro_validator.utils"]
Expand Down
Loading