Skip to content

Commit

Permalink
Merge pull request #348 from yozachar/workshop
Browse files Browse the repository at this point in the history
feat: adds basic `cron` validator
  • Loading branch information
yozachar authored Apr 2, 2024
2 parents 1f49c5f + f1acf4f commit e3d30f8
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 2 deletions.
21 changes: 21 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ Note to self: Breaking changes must increment either
-->

## 0.25.0 (2024-04-02)

_**Breaking**_

> No breaking changes were introduced in this version.
_**Features**_

- feat: adds basic `cron` validator by @yozachar in [#348](https://github.com/python-validators/validators/pull/348)

_**Maintenance**_

- maint: adds quick start docs by @yozachar in [#344](https://github.com/python-validators/validators/pull/344)
- fix: `domain` validation is now more consistent across rfcs by @yozachar in [#347](https://github.com/python-validators/validators/pull/347)

**Full Changelog**: [`0.24.2...0.25.0`](https://github.com/python-validators/validators/compare/0.23.2...0.24.0)

---

## 0.24.0 (2024-03-24)

_**Breaking**_
Expand All @@ -27,6 +46,8 @@ _**Maintenance**_

**Full Changelog**: [`0.23.2...0.24.0`](https://github.com/python-validators/validators/compare/0.23.2...0.24.0)

---

## 0.23.2 (2024-03-20)

_**Breaking**_
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

| Version | Supported |
| ---------- | ------------------ |
| `>=0.24.0` | :white_check_mark: |
| `>=0.25.0` | :white_check_mark: |

## Reporting a Vulnerability

Expand Down
3 changes: 3 additions & 0 deletions docs/api/cron.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# cron

::: validators.cron.cron
5 changes: 5 additions & 0 deletions docs/api/cron.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cron
----

.. module:: validators.cron
.. autofunction:: cron
1 change: 1 addition & 0 deletions mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ nav:
- api/btc_address.md
- api/card.md
- api/country_code.md
- api/cron.md
- api/domain.md
- api/email.md
- api/hashes.md
Expand Down
5 changes: 4 additions & 1 deletion src/validators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .btc_address import btc_address
from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa
from .country_code import country_code
from .cron import cron
from .domain import domain
from .email import email
from .hashes import md5, sha1, sha224, sha256, sha512
Expand Down Expand Up @@ -39,6 +40,8 @@
# ...
"country_code",
# ...
"cron",
# ...
"domain",
# ...
"email",
Expand Down Expand Up @@ -79,4 +82,4 @@
"validator",
)

__version__ = "0.24.0"
__version__ = "0.25.0"
78 changes: 78 additions & 0 deletions src/validators/cron.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Cron."""

# local
from .utils import validator


def _validate_cron_component(component: str, min_val: int, max_val: int):
if component == "*":
return True

if component.isdecimal():
return min_val <= int(component) <= max_val

if "/" in component:
parts = component.split("/")
if len(parts) != 2 or not parts[1].isdecimal() or int(parts[1]) < 1:
return False
if parts[0] == "*":
return True
return parts[0].isdecimal() and min_val <= int(parts[0]) <= max_val

if "-" in component:
parts = component.split("-")
if len(parts) != 2 or not parts[0].isdecimal() or not parts[1].isdecimal():
return False
start, end = int(parts[0]), int(parts[1])
return min_val <= start <= max_val and min_val <= end <= max_val and start <= end

if "," in component:
for item in component.split(","):
if not _validate_cron_component(item, min_val, max_val):
return False
return True
# return all(
# _validate_cron_component(item, min_val, max_val) for item in component.split(",")
# ) # throws type error. why?

return False


@validator
def cron(value: str, /):
"""Return whether or not given value is a valid cron string.
Examples:
>>> cron('*/5 * * * *')
# Output: True
>>> cron('30-20 * * * *')
# Output: ValidationError(func=cron, ...)
Args:
value:
Cron string to validate.
Returns:
(Literal[True]): If `value` is a valid cron string.
(ValidationError): If `value` is an invalid cron string.
"""
if not value:
return False

try:
minutes, hours, days, months, weekdays = value.strip().split()
except ValueError as err:
raise ValueError("Badly formatted cron string") from err

if not _validate_cron_component(minutes, 0, 59):
return False
if not _validate_cron_component(hours, 0, 23):
return False
if not _validate_cron_component(days, 1, 31):
return False
if not _validate_cron_component(months, 1, 12):
return False
if not _validate_cron_component(weekdays, 0, 6):
return False

return True
54 changes: 54 additions & 0 deletions tests/test_cron.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Test Cron."""

# external
import pytest

# local
from validators import ValidationError, cron


@pytest.mark.parametrize(
"value",
[
"* * * * *",
"*/5 * * * *",
"0 0 * * *",
"30 3 * * 1-5",
"15 5 * * 1,3,5",
"0 12 1 */2 *",
"0 */3 * * *",
"0 0 1 1 *",
"0 12 * 1-6 1-5",
"0 3-6 * * *",
"*/15 0,6,12,18 * * *",
"0 12 * * 0",
"*/61 * * * *",
# "5-10/2 * * * *", # this is valid, but not supported yet
],
)
def test_returns_true_on_valid_cron(value: str):
"""Test returns true on valid cron string."""
assert cron(value)


@pytest.mark.parametrize(
"value",
[
"* * * * * *",
"* * * *",
"*/5 25 * * *",
"*/5 * *-1 * *",
"32-30 * * * *",
"0 12 32 * *",
"0 12 * * 8",
"0 */0 * * *",
"30-20 * * * *",
"10-* * * * *",
"*/15 0,6,12,24 * * *",
"& * * & * *",
"* - * * - *",
],
)
def test_returns_failed_validation_on_invalid_cron(value: str):
"""Test returns failed validation on invalid cron string."""
assert isinstance(cron(value), ValidationError)

0 comments on commit e3d30f8

Please sign in to comment.