diff --git a/CHANGES.md b/CHANGES.md index 144a53f9..956f689f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,24 @@ Note to self: Breaking changes must increment either --> +## 0.24.0 (2024-03-24) + +_**Breaking**_ + +> No breaking changes were introduced in this version. + +_**Features**_ + +- feat: conditionally raises `ValidationError`; bump version by @yozachar in [#343](https://github.com/python-validators/validators/pull/343) + +_**Maintenance**_ + +- patch: `domain` & `url` modules by @yozachar in [#339](https://github.com/python-validators/validators/pull/339) +- fix: domain name not confirming to rfc_2782 by @yozachar in [#341](https://github.com/python-validators/validators/pull/341) +- maint: update dev dependencies; adds favicon to docs by @yozachar in [#342](https://github.com/python-validators/validators/pull/342) + +**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**_ @@ -147,19 +165,21 @@ _**Breaking**_ ⚠️ - `extremes.py` renamed to `_extremes.py` and is no longer exposed. - `truthy` was discarded in favour of simple `bool()` function. - `ipv4_cidr()` and `ipv6_cidr()` has been dropped in favour of `cidr: bool = True` and `cidr: bool = True` keyword-only parameters. -- `email` API now accepts the following keyword-only arguments: +- `email()` API now accepts the following keyword-only arguments: - `simple_host: bool = False`, - `ipv6_address: bool = False`, - `ipv4_address: bool = False`, - `rfc_1034: bool = False` and - `rfc_2782: bool = False`. -- `url` has been refactored, it accepts the following keyword-only arguments: +- `whitelist=None` has been removed from `email()`. +- `url()` has been refactored, it accepts the following keyword-only arguments: - `skip_ipv6_addr: bool = False`, - `skip_ipv4_addr: bool = False`, - `may_have_port: bool = True`, - `simple_host: bool = False`, - `rfc_1034: bool = False` and - `rfc_2782: bool = False`. +- `public=False` keyword argument has been removed from `url()`. - Exposes `i18n` functions directly via `__init__.py`. - `@validator` decorator catches `Exception`. diff --git a/SECURITY.md b/SECURITY.md index 32541cd0..930d0a8f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,7 @@ | Version | Supported | | ---------- | ------------------ | -| `>=0.23.2` | :white_check_mark: | +| `>=0.24.0` | :white_check_mark: | ## Reporting a Vulnerability diff --git a/docs/assets/icons/favicon.svg b/docs/assets/icons/favicon.svg new file mode 100644 index 00000000..ed0b99ef --- /dev/null +++ b/docs/assets/icons/favicon.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/mkdocs.yaml b/mkdocs.yaml index 6031461d..62033761 100644 --- a/mkdocs.yaml +++ b/mkdocs.yaml @@ -11,6 +11,7 @@ theme: name: material icon: logo: material/marker-check + favicon: assets/icons/favicon.svg font: text: Inter code: "Fira Code" diff --git a/package/requirements.mkdocs.txt b/package/requirements.mkdocs.txt index cefb5b84..c2a9f459 100644 --- a/package/requirements.mkdocs.txt +++ b/package/requirements.mkdocs.txt @@ -109,12 +109,12 @@ griffe==0.42.1 \ idna==3.6 \ --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f -importlib-metadata==7.0.2 \ - --hash=sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792 \ - --hash=sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100 -importlib-resources==6.3.2 \ - --hash=sha256:963eb79649252b0160c1afcfe5a1d3fe3ad66edd0a8b114beacffb70c0674223 \ - --hash=sha256:f41f4098b16cd140a97d256137cfd943d958219007990b2afb00439fc623f580 +importlib-metadata==7.1.0 \ + --hash=sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570 \ + --hash=sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2 +importlib-resources==6.4.0 \ + --hash=sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c \ + --hash=sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145 jinja2==3.1.3 \ --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 diff --git a/package/requirements.sphinx.txt b/package/requirements.sphinx.txt index ccbc98e3..ca7438a3 100644 --- a/package/requirements.sphinx.txt +++ b/package/requirements.sphinx.txt @@ -100,9 +100,9 @@ idna==3.6 \ imagesize==1.4.1 \ --hash=sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b \ --hash=sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a -importlib-metadata==7.0.2 \ - --hash=sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792 \ - --hash=sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100 +importlib-metadata==7.1.0 \ + --hash=sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570 \ + --hash=sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2 jinja2==3.1.3 \ --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 diff --git a/package/requirements.tooling.txt b/package/requirements.tooling.txt index 4f72b846..b816a4e1 100644 --- a/package/requirements.tooling.txt +++ b/package/requirements.tooling.txt @@ -62,30 +62,30 @@ pypandoc-binary==1.13 \ --hash=sha256:67c0c7af811bcf3cd4f3221be756a4975ec35b2d7df89d8de4313a8caa2cd54f \ --hash=sha256:9455fdd9521cbf4b56d79a56b806afa94c8c22f3c8ef878536e58d941a70f6d6 \ --hash=sha256:946666388eb79b307d7f497b3b33045ef807750f8e5ef3440e0ba3bbab698044 -pyright==1.1.354 \ - --hash=sha256:b1070dc774ff2e79eb0523fe87f4ba9a90550de7e4b030a2bc9e031864029a1f \ - --hash=sha256:f28d61ae8ae035fc52ded1070e8d9e786051a26a4127bbd7a4ba0399b81b37b5 +pyright==1.1.355 \ + --hash=sha256:bf30b6728fd68ae7d09c98292b67152858dd89738569836896df786e52b5fe48 \ + --hash=sha256:dca4104cd53d6484e6b1b50b7a239ad2d16d2ffd20030bcf3111b56f44c263bf pytest==8.1.1 \ --hash=sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7 \ --hash=sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044 -ruff==0.3.3 \ - --hash=sha256:0171aab5fecdc54383993389710a3d1227f2da124d76a2784a7098e818f92d61 \ - --hash=sha256:0da458989ce0159555ef224d5b7c24d3d2e4bf4c300b85467b08c3261c6bc6a8 \ - --hash=sha256:1eca7ff7a47043cf6ce5c7f45f603b09121a7cc047447744b029d1b719278eb5 \ - --hash=sha256:2700a804d5336bcffe063fd789ca2c7b02b552d2e323a336700abb8ae9e6a3f8 \ - --hash=sha256:352e95ead6964974b234e16ba8a66dad102ec7bf8ac064a23f95371d8b198aab \ - --hash=sha256:38671be06f57a2f8aba957d9f701ea889aa5736be806f18c0cd03d6ff0cbca8d \ - --hash=sha256:45817af234605525cdf6317005923bf532514e1ea3d9270acf61ca2440691376 \ - --hash=sha256:5a6cbf216b69c7090f0fe4669501a27326c34e119068c1494f35aaf4cc683778 \ - --hash=sha256:79bca3a03a759cc773fca69e0bdeac8abd1c13c31b798d5bb3c9da4a03144a9f \ - --hash=sha256:8d6ab88c81c4040a817aa432484e838aaddf8bfd7ca70e4e615482757acb64f8 \ - --hash=sha256:973a0e388b7bc2e9148c7f9be8b8c6ae7471b9be37e1cc732f8f44a6f6d7720d \ - --hash=sha256:b24c19e8598916d9c6f5a5437671f55ee93c212a2c4c569605dc3842b6820386 \ - --hash=sha256:be90bcae57c24d9f9d023b12d627e958eb55f595428bafcb7fec0791ad25ddfc \ - --hash=sha256:cfa60d23269d6e2031129b053fdb4e5a7b0637fc6c9c0586737b962b2f834493 \ - --hash=sha256:e7d3f6762217c1da954de24b4a1a70515630d29f71e268ec5000afe81377642d \ - --hash=sha256:f2831ec6a580a97f1ea82ea1eda0401c3cdf512cf2045fa3c85e8ef109e87de0 \ - --hash=sha256:fd66469f1a18fdb9d32e22b79f486223052ddf057dc56dea0caaf1a47bdfaf4e +ruff==0.3.4 \ + --hash=sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365 \ + --hash=sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488 \ + --hash=sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4 \ + --hash=sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9 \ + --hash=sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232 \ + --hash=sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91 \ + --hash=sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369 \ + --hash=sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed \ + --hash=sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102 \ + --hash=sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e \ + --hash=sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c \ + --hash=sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7 \ + --hash=sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378 \ + --hash=sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854 \ + --hash=sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6 \ + --hash=sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50 \ + --hash=sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1 setuptools==69.2.0 \ --hash=sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e \ --hash=sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c diff --git a/pdm.lock b/pdm.lock index c8dc2a0e..bd9c0ac7 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "package", "runner", "sast", "docs-offline", "hooks", "docs-online", "tooling"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:2607ff83af55ecb23667ed8d234bc82d90b98f8e5877fdd0930e557aa734602a" +content_hash = "sha256:14e8926f6b74e07094cf798e8c94470f980f937e3875fdf07449f00247491376" [[package]] name = "alabaster" @@ -428,7 +428,7 @@ files = [ [[package]] name = "importlib-metadata" -version = "7.0.2" +version = "7.1.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["docs-offline", "docs-online", "package"] @@ -436,13 +436,13 @@ dependencies = [ "zipp>=0.5", ] files = [ - {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, - {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, + {file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"}, + {file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"}, ] [[package]] name = "importlib-resources" -version = "6.3.2" +version = "6.4.0" requires_python = ">=3.8" summary = "Read resources from Python packages" groups = ["docs-online"] @@ -450,8 +450,8 @@ dependencies = [ "zipp>=3.1.0; python_version < \"3.10\"", ] files = [ - {file = "importlib_resources-6.3.2-py3-none-any.whl", hash = "sha256:f41f4098b16cd140a97d256137cfd943d958219007990b2afb00439fc623f580"}, - {file = "importlib_resources-6.3.2.tar.gz", hash = "sha256:963eb79649252b0160c1afcfe5a1d3fe3ad66edd0a8b114beacffb70c0674223"}, + {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, + {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, ] [[package]] @@ -982,7 +982,7 @@ files = [ [[package]] name = "pyright" -version = "1.1.354" +version = "1.1.355" requires_python = ">=3.7" summary = "Command line wrapper for pyright" groups = ["tooling"] @@ -990,8 +990,8 @@ dependencies = [ "nodeenv>=1.6.0", ] files = [ - {file = "pyright-1.1.354-py3-none-any.whl", hash = "sha256:f28d61ae8ae035fc52ded1070e8d9e786051a26a4127bbd7a4ba0399b81b37b5"}, - {file = "pyright-1.1.354.tar.gz", hash = "sha256:b1070dc774ff2e79eb0523fe87f4ba9a90550de7e4b030a2bc9e031864029a1f"}, + {file = "pyright-1.1.355-py3-none-any.whl", hash = "sha256:bf30b6728fd68ae7d09c98292b67152858dd89738569836896df786e52b5fe48"}, + {file = "pyright-1.1.355.tar.gz", hash = "sha256:dca4104cd53d6484e6b1b50b7a239ad2d16d2ffd20030bcf3111b56f44c263bf"}, ] [[package]] @@ -1222,28 +1222,28 @@ files = [ [[package]] name = "ruff" -version = "0.3.3" +version = "0.3.4" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["tooling"] files = [ - {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:973a0e388b7bc2e9148c7f9be8b8c6ae7471b9be37e1cc732f8f44a6f6d7720d"}, - {file = "ruff-0.3.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfa60d23269d6e2031129b053fdb4e5a7b0637fc6c9c0586737b962b2f834493"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1eca7ff7a47043cf6ce5c7f45f603b09121a7cc047447744b029d1b719278eb5"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7d3f6762217c1da954de24b4a1a70515630d29f71e268ec5000afe81377642d"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b24c19e8598916d9c6f5a5437671f55ee93c212a2c4c569605dc3842b6820386"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5a6cbf216b69c7090f0fe4669501a27326c34e119068c1494f35aaf4cc683778"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352e95ead6964974b234e16ba8a66dad102ec7bf8ac064a23f95371d8b198aab"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d6ab88c81c4040a817aa432484e838aaddf8bfd7ca70e4e615482757acb64f8"}, - {file = "ruff-0.3.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79bca3a03a759cc773fca69e0bdeac8abd1c13c31b798d5bb3c9da4a03144a9f"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2700a804d5336bcffe063fd789ca2c7b02b552d2e323a336700abb8ae9e6a3f8"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fd66469f1a18fdb9d32e22b79f486223052ddf057dc56dea0caaf1a47bdfaf4e"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45817af234605525cdf6317005923bf532514e1ea3d9270acf61ca2440691376"}, - {file = "ruff-0.3.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0da458989ce0159555ef224d5b7c24d3d2e4bf4c300b85467b08c3261c6bc6a8"}, - {file = "ruff-0.3.3-py3-none-win32.whl", hash = "sha256:f2831ec6a580a97f1ea82ea1eda0401c3cdf512cf2045fa3c85e8ef109e87de0"}, - {file = "ruff-0.3.3-py3-none-win_amd64.whl", hash = "sha256:be90bcae57c24d9f9d023b12d627e958eb55f595428bafcb7fec0791ad25ddfc"}, - {file = "ruff-0.3.3-py3-none-win_arm64.whl", hash = "sha256:0171aab5fecdc54383993389710a3d1227f2da124d76a2784a7098e818f92d61"}, - {file = "ruff-0.3.3.tar.gz", hash = "sha256:38671be06f57a2f8aba957d9f701ea889aa5736be806f18c0cd03d6ff0cbca8d"}, + {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, + {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, + {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, + {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, + {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, + {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, ] [[package]] @@ -1413,7 +1413,7 @@ files = [ [[package]] name = "tox" -version = "4.14.1" +version = "4.14.2" requires_python = ">=3.8" summary = "tox is a generic virtualenv management and test command line tool" groups = ["runner"] @@ -1430,8 +1430,8 @@ dependencies = [ "virtualenv>=20.25", ] files = [ - {file = "tox-4.14.1-py3-none-any.whl", hash = "sha256:b03754b6ee6dadc70f2611da82b4ed8f625fcafd247e15d1d0cb056f90a06d3b"}, - {file = "tox-4.14.1.tar.gz", hash = "sha256:f0ad758c3bbf7e237059c929d3595479363c3cdd5a06ac3e49d1dd020ffbee45"}, + {file = "tox-4.14.2-py3-none-any.whl", hash = "sha256:2900c4eb7b716af4a928a7fdc2ed248ad6575294ed7cfae2ea41203937422847"}, + {file = "tox-4.14.2.tar.gz", hash = "sha256:0defb44f6dafd911b61788325741cc6b2e12ea71f987ac025ad4d649f1f1a104"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 0b41886d..908df622 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,12 +64,12 @@ docs-online = [ ] hooks = ["pre-commit>=3.5.0"] package = ["build>=1.1.1"] -runner = ["tox>=4.14.1"] +runner = ["tox>=4.14.2"] sast = ["bandit[toml]>=1.7.8"] tooling = [ "black>=24.3.0", - "ruff>=0.3.3", - "pyright>=1.1.354", + "ruff>=0.3.4", + "pyright>=1.1.355", "pytest>=8.1.1", "pypandoc-binary>=1.13", # helps with type checking ] diff --git a/src/validators/__init__.py b/src/validators/__init__.py index d1995822..31da3aa9 100644 --- a/src/validators/__init__.py +++ b/src/validators/__init__.py @@ -79,4 +79,4 @@ "validator", ) -__version__ = "0.23.2" +__version__ = "0.24.0" diff --git a/src/validators/domain.py b/src/validators/domain.py index 1c25df5f..c5eea7d4 100644 --- a/src/validators/domain.py +++ b/src/validators/domain.py @@ -42,7 +42,8 @@ def domain(value: str, /, *, rfc_1034: bool = False, rfc_2782: bool = False): # First character of the domain rf"^(?:[a-zA-Z0-9{'_'if rfc_2782 else ''}]" # Sub domain + hostname - + r"(?:[a-zA-Z0-9-_]{0,61}[A-Za-z0-9])?\.)" + + rf"(?:[a-zA-Z0-9-{'_'if rfc_2782 else ''}]{{0,61}}" + + rf"[A-Za-z0-9{'_'if rfc_2782 else ''}])?\.)" # First 61 characters of the gTLD + r"+[A-Za-z0-9][A-Za-z0-9-_]{0,61}" # Last character of the gTLD diff --git a/src/validators/url.py b/src/validators/url.py index ac7f9364..177e94d1 100644 --- a/src/validators/url.py +++ b/src/validators/url.py @@ -38,7 +38,14 @@ def _validate_scheme(value: str): """Validate scheme.""" # More schemes will be considered later. return ( - value in {"ftp", "ftps", "git", "http", "https", "rtsp", "sftp", "ssh", "telnet"} + value + # fmt: off + in { + "ftp", "ftps", "git", "http", "https", + "rtmp", "rtmps", "rtsp", "sftp", + "ssh", "telnet", + } + # fmt: on if value else False ) @@ -112,8 +119,18 @@ def _validate_optionals(path: str, query: str, fragment: str, strict_query: bool optional_segments = True if path: optional_segments &= bool(_path_regex().match(path)) - if query and parse_qs(query, strict_parsing=strict_query): - optional_segments &= True + try: + if ( + query + # ref: https://github.com/python/cpython/issues/117109 + and parse_qs(query, strict_parsing=strict_query, separator="&") + and parse_qs(query, strict_parsing=strict_query, separator=";") + ): + optional_segments &= True + except TypeError: + # for Python < v3.9.2 (official v3.10) + if query and parse_qs(query, strict_parsing=strict_query): + optional_segments &= True if fragment: # See RFC3986 Section 3.5 Fragment for allowed characters optional_segments &= bool(re.fullmatch(r"[0-9a-zA-Z?/:@\-._~%!$&'()*+,;=]*", fragment)) diff --git a/src/validators/utils.py b/src/validators/utils.py index 82bb14ba..80af7893 100644 --- a/src/validators/utils.py +++ b/src/validators/utils.py @@ -4,6 +4,7 @@ from functools import wraps from inspect import getfullargspec from itertools import chain +from os import environ from typing import Any, Callable, Dict @@ -64,17 +65,38 @@ def validator(func: Callable[..., Any]): (Callable[..., ValidationError | Literal[True]]): A decorator which returns either `ValidationError` or `Literal[True]`. + + Raises: + (ValidationError): If `r_ve` or `RAISE_VALIDATION_ERROR` is `True` """ @wraps(func) def wrapper(*args: Any, **kwargs: Any): + raise_validation_error = False + if "r_ve" in kwargs: + raise_validation_error = True + del kwargs["r_ve"] + if environ.get("RAISE_VALIDATION_ERROR", "False") == "True": + raise_validation_error = True + try: - return ( - True - if func(*args, **kwargs) - else ValidationError(func, _func_args_as_dict(func, *args, **kwargs)) - ) - except Exception as exp: - return ValidationError(func, _func_args_as_dict(func, *args, **kwargs), str(exp)) + if raise_validation_error: + if func(*args, **kwargs): + return True + else: + raise ValidationError(func, _func_args_as_dict(func, *args, **kwargs)) + else: + return ( + True + if func(*args, **kwargs) + else ValidationError(func, _func_args_as_dict(func, *args, **kwargs)) + ) + except (ValueError, TypeError) as exp: + if raise_validation_error: + raise ValidationError( + func, _func_args_as_dict(func, *args, **kwargs), str(exp) + ) from exp + else: + return ValidationError(func, _func_args_as_dict(func, *args, **kwargs), str(exp)) return wrapper diff --git a/tests/test_domain.py b/tests/test_domain.py index 9f82b55d..c8f5c4d9 100644 --- a/tests/test_domain.py +++ b/tests/test_domain.py @@ -11,13 +11,15 @@ ("value", "rfc_1034", "rfc_2782"), [ ("example.com", False, False), + ("exa_mple.com", False, True), ("xn----gtbspbbmkef.xn--p1ai", False, False), - ("underscore_subdomain.example.com", False, False), + ("underscore_subdomain.example.com", False, True), ("something.versicherung", False, False), ("someThing.versicherung.", True, False), ("11.com", False, False), ("3.cn.", True, False), ("_example.com", False, True), + ("example_.com", False, True), ("a.cn", False, False), ("sub1.sub2.sample.co.uk", False, False), ("somerandomexample.xn--fiqs8s", False, False), diff --git a/tests/test_url.py b/tests/test_url.py index 23baf8fa..079e62e2 100644 --- a/tests/test_url.py +++ b/tests/test_url.py @@ -152,6 +152,7 @@ def test_returns_true_on_valid_url(value: str): "http://[2010:836B:4179::836B:4179", "http://2010:836B:4179::836B:4179", "http://2010:836B:4179::836B:4179:80/index.html", + "https://example.org?q=search');alert(document.domain);", "https://www.example.com/foo/?bar=baz&inga=42&quux", "https://foo.com/img/bar/baz.jpg?-62169987208", "https://foo.bar.net/baz.php?-/inga/test-lenient-query/",