diff --git a/CHANGES.md b/CHANGES.md index 63372a91..e2eb3017 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,25 @@ Note to self: Breaking changes must increment either --> +## 0.26.0 (2024-04-02) + +_**Breaking**_ + +> No breaking changes were introduced in this version. + +_**Features**_ + +- feat: adds `base58` and `base64` validators by @yozachar in [#351](https://github.com/python-validators/validators/pull/351) + +_**Maintenance**_ + +- fix: regex ignore-case uses only `a-z` by @yozachar in [#349](https://github.com/python-validators/validators/pull/349) +- patch: supported extended latin in username by @yozachar in [#350](https://github.com/python-validators/validators/pull/350) + +**Full Changelog**: [`0.25.0...0.26.0`](https://github.com/python-validators/validators/compare/0.25.0...0.26.0) + +--- + ## 0.25.0 (2024-04-02) _**Breaking**_ @@ -24,7 +43,7 @@ _**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) +**Full Changelog**: [`0.24.2...0.25.0`](https://github.com/python-validators/validators/compare/0.24.2...0.25.0) --- diff --git a/SECURITY.md b/SECURITY.md index 3eda6b54..e2bc917d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,7 @@ | Version | Supported | | ---------- | ------------------ | -| `>=0.25.0` | :white_check_mark: | +| `>=0.26.0` | :white_check_mark: | ## Reporting a Vulnerability diff --git a/docs/api/hashes.md b/docs/api/hashes.md index 82b11bf0..0f583e1b 100644 --- a/docs/api/hashes.md +++ b/docs/api/hashes.md @@ -1,5 +1,7 @@ # hashes +::: validators.hashes.base58 +::: validators.hashes.base64 ::: validators.hashes.md5 ::: validators.hashes.sha1 ::: validators.hashes.sha224 diff --git a/docs/api/hashes.rst b/docs/api/hashes.rst index bc77b7b2..829f0992 100644 --- a/docs/api/hashes.rst +++ b/docs/api/hashes.rst @@ -2,6 +2,8 @@ hashes ------ .. module:: validators.hashes +.. autofunction:: base58 +.. autofunction:: base64 .. autofunction:: md5 .. autofunction:: sha1 .. autofunction:: sha224 diff --git a/src/validators/__init__.py b/src/validators/__init__.py index 8a92047f..806531ff 100644 --- a/src/validators/__init__.py +++ b/src/validators/__init__.py @@ -8,7 +8,7 @@ from .cron import cron from .domain import domain from .email import email -from .hashes import md5, sha1, sha224, sha256, sha512 +from .hashes import base58, base64, md5, sha1, sha224, sha256, sha512 from .hostname import hostname from .i18n import es_cif, es_doi, es_nie, es_nif, fi_business_id, fi_ssn, fr_department, fr_ssn from .iban import iban @@ -46,6 +46,8 @@ # ... "email", # hashes + "base58", + "base64", "md5", "sha1", "sha224", @@ -82,4 +84,4 @@ "validator", ) -__version__ = "0.25.0" +__version__ = "0.26.0" diff --git a/src/validators/hashes.py b/src/validators/hashes.py index 6004d30a..d72c86fe 100644 --- a/src/validators/hashes.py +++ b/src/validators/hashes.py @@ -7,6 +7,52 @@ from .utils import validator +@validator +def base58(value: str, /): + """Return whether or not given value is a valid base58 hash. + + Examples: + >>> base58('14pq6y9H2DLGahPsM4s7ugsNSD2uxpHsJx') + # Output: True + >>> base58('cUSECm5YzcXJwP') + # Output: ValidationError(func=base58, args={'value': 'cUSECm5YzcXJwP'}) + + Args: + value: + base58 string to validate. + + Returns: + (Literal[True]): If `value` is a valid base58 hash. + (ValidationError): If `value` is an invalid base58 hash. + """ + return re.match(r"^[1-9A-HJ-NP-Za-km-z]+$", value) if value else False + + +@validator +def base64(value: str, /): + """Return whether or not given value is a valid base64 hash. + + Examples: + >>> base64('Y2hhcmFjdGVyIHNldA==') + # Output: True + >>> base64('cUSECm5YzcXJwP') + # Output: ValidationError(func=base64, args={'value': 'cUSECm5YzcXJwP'}) + + Args: + value: + base64 string to validate. + + Returns: + (Literal[True]): If `value` is a valid base64 hash. + (ValidationError): If `value` is an invalid base64 hash. + """ + return ( + re.match(r"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$", value) + if value + else False + ) + + @validator def md5(value: str, /): """Return whether or not given value is a valid MD5 hash. diff --git a/tests/test_hashes.py b/tests/test_hashes.py index ee68f30f..39af2c4d 100644 --- a/tests/test_hashes.py +++ b/tests/test_hashes.py @@ -4,7 +4,54 @@ import pytest # local -from validators import ValidationError, md5, sha1, sha224, sha256, sha512 +from validators import ValidationError, base58, base64, md5, sha1, sha224, sha256, sha512 + +# ==> base58 <== # + + +@pytest.mark.parametrize( + "value", + [ + "cUSECaVvAiV3srWbFRvVPzm5YzcXJwPSwZfE7veYPHoXmR9h6YMQ", + "18KToMF5ckjXBYt2HAj77qsG3GPeej3PZn", + "n4FFXRNNEW1aA2WPscSuzHTCjzjs4TVE2Z", + "38XzQ9dPGb1uqbZsjPtUajp7omy8aefjqj", + ], +) +def test_returns_true_on_valid_base58(value: str): + """Test returns true on valid base58.""" + assert base58(value) + + +@pytest.mark.parametrize( + "value", + ["ThisIsAReallyLongStringThatIsDefinitelyNotBase58Encoded", "abcABC!@#", "InvalidBase58!"], +) +def test_returns_failed_validation_on_invalid_base58(value: str): + """Test returns failed validation on invalid base58.""" + assert isinstance(base58(value), ValidationError) + + +# ==> base64 <== # + + +@pytest.mark.parametrize( + "value", + ["SGVsbG8gV29ybGQ=", "U29tZSBkYXRhIHN0cmluZw==", "YW55IGNhcm5hbCBwbGVhcw=="], +) +def test_returns_true_on_valid_base64(value: str): + """Test returns true on valid base64.""" + assert base64(value) + + +@pytest.mark.parametrize( + "value", + ["SGVsbG8gV29ybGQ", "U29tZSBkYXRhIHN0cmluZw", "YW55IGNhcm5hbCBwbGVhc"], +) +def test_returns_failed_validation_on_invalid_base64(value: str): + """Test returns failed validation on invalid base64.""" + assert isinstance(base64(value), ValidationError) + # ==> md5 <== #