diff options
| author | natsuoto <[email protected]> | 2026-07-11 11:39:27 +0530 |
|---|---|---|
| committer | natsuoto <[email protected]> | 2026-07-11 11:39:27 +0530 |
| commit | b40f35842497df6ba301cb17738b6b571867cb30 (patch) | |
| tree | a88e2c852f0cc92ca3929f5fd6ae30d11ae2ce20 | |
| parent | 45d86cbf87a50a93cf38a0af10782ab7c0f628e4 (diff) | |
| download | edify-b40f35842497df6ba301cb17738b6b571867cb30.tar.xz edify-b40f35842497df6ba301cb17738b6b571867cb30.zip | |
feat(library/address): 13 address patterns (ip, cidr, subnet, hostname, domain, subdomain, tld, url, uri, path, port, socket, ptr)
| -rw-r--r-- | edify/library/address/__init__.py | 29 | ||||
| -rw-r--r-- | edify/library/address/cidr.py | 18 | ||||
| -rw-r--r-- | edify/library/address/domain.py | 13 | ||||
| -rw-r--r-- | edify/library/address/hostname.py | 12 | ||||
| -rw-r--r-- | edify/library/address/ip.py | 32 | ||||
| -rw-r--r-- | edify/library/address/path.py | 16 | ||||
| -rw-r--r-- | edify/library/address/port.py | 16 | ||||
| -rw-r--r-- | edify/library/address/ptr.py | 15 | ||||
| -rw-r--r-- | edify/library/address/socket.py | 18 | ||||
| -rw-r--r-- | edify/library/address/subdomain.py | 17 | ||||
| -rw-r--r-- | edify/library/address/subnet.py | 15 | ||||
| -rw-r--r-- | edify/library/address/tld.py | 13 | ||||
| -rw-r--r-- | edify/library/address/uri.py | 12 | ||||
| -rw-r--r-- | edify/library/address/url.py | 16 |
14 files changed, 242 insertions, 0 deletions
diff --git a/edify/library/address/__init__.py b/edify/library/address/__init__.py new file mode 100644 index 0000000..13a934d --- /dev/null +++ b/edify/library/address/__init__.py @@ -0,0 +1,29 @@ +from edify.library.address.cidr import cidr +from edify.library.address.domain import domain +from edify.library.address.hostname import hostname +from edify.library.address.ip import ip +from edify.library.address.path import path +from edify.library.address.port import port +from edify.library.address.ptr import ptr +from edify.library.address.socket import socket +from edify.library.address.subdomain import subdomain +from edify.library.address.subnet import subnet +from edify.library.address.tld import tld +from edify.library.address.uri import uri +from edify.library.address.url import url + +__all__ = [ + "cidr", + "domain", + "hostname", + "ip", + "path", + "port", + "ptr", + "socket", + "subdomain", + "subnet", + "tld", + "uri", + "url", +] diff --git a/edify/library/address/cidr.py b/edify/library/address/cidr.py new file mode 100644 index 0000000..d361396 --- /dev/null +++ b/edify/library/address/cidr.py @@ -0,0 +1,18 @@ +"""``cidr`` — CIDR-notation subnet ``address/prefix`` shape.""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +cidr = RegexBackedPattern( + r"^(?:" + r"(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)" + r"(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}" + r"/(?:3[0-2]|[12]?\d)" + r"|(?:[0-9a-fA-F]{1,4}:){0,7}[0-9a-fA-F]{1,4}" + r"/(?:12[0-8]|1[01]\d|[1-9]?\d)" + r")$" +) +"""Callable :class:`Pattern` for CIDR notation: IPv4 address + ``/0``–``/32`` +or IPv6 address + ``/0``–``/128``. +""" diff --git a/edify/library/address/domain.py b/edify/library/address/domain.py new file mode 100644 index 0000000..673eca9 --- /dev/null +++ b/edify/library/address/domain.py @@ -0,0 +1,13 @@ +"""``domain`` — DNS domain name shape (label.label.tld).""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +domain = RegexBackedPattern( + r"^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+" + r"[a-zA-Z]{2,63}$" +) +"""Callable :class:`Pattern` for the DNS domain name shape: +at least one label followed by a TLD of 2–63 letters. +""" diff --git a/edify/library/address/hostname.py b/edify/library/address/hostname.py new file mode 100644 index 0000000..06a8924 --- /dev/null +++ b/edify/library/address/hostname.py @@ -0,0 +1,12 @@ +"""``hostname`` — RFC 1123 hostname shape.""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +hostname = RegexBackedPattern( + r"^(?=.{1,253}$)(?:[a-zA-Z0-9]" + r"(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)" + r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$" +) +"""Callable :class:`Pattern` for the RFC 1123 hostname shape.""" diff --git a/edify/library/address/ip.py b/edify/library/address/ip.py new file mode 100644 index 0000000..25deaae --- /dev/null +++ b/edify/library/address/ip.py @@ -0,0 +1,32 @@ +"""``ip`` — IPv4 or IPv6 address shape.""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +_IPV4_OCTET = r"(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)" +_IPV4 = rf"{_IPV4_OCTET}\.{_IPV4_OCTET}\.{_IPV4_OCTET}\.{_IPV4_OCTET}" + +_IPV6 = ( + r"(?:" + r"([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}" + r"|([0-9a-fA-F]{1,4}:){1,7}:" + r"|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}" + r"|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}" + r"|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}" + r"|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}" + r"|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}" + r"|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})" + r"|:((:[0-9a-fA-F]{1,4}){1,7}|:)" + r"|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}" + r"|::(ffff(:0{1,4}){0,1}:){0,1}" + r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}" + r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])" + r"|([0-9a-fA-F]{1,4}:){1,4}:" + r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}" + r"(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])" + r")" +) + +ip = RegexBackedPattern(rf"^(?:{_IPV4}|{_IPV6})$") +"""Callable :class:`Pattern` for IPv4 dotted-quad or any IPv6 form.""" diff --git a/edify/library/address/path.py b/edify/library/address/path.py new file mode 100644 index 0000000..6f1a1aa --- /dev/null +++ b/edify/library/address/path.py @@ -0,0 +1,16 @@ +"""``path`` — filesystem path shape (POSIX or Windows).""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +path = RegexBackedPattern( + r"^(?:" + r"(?:/|(?:\./|(?:\.\./)+))?(?:[^\0\r\n/]+/?)+" + r"|[a-zA-Z]:\\(?:[^\\/:*?\"<>|\r\n]+\\?)+" + r"|\\\\[^\\/:*?\"<>|\r\n]+\\[^\\/:*?\"<>|\r\n]+(?:\\[^\\/:*?\"<>|\r\n]*)*" + r")$" +) +"""Callable :class:`Pattern` for a filesystem path shape: POSIX +(``/absolute`` or ``relative/``), Windows drive-letter, or UNC. +""" diff --git a/edify/library/address/port.py b/edify/library/address/port.py new file mode 100644 index 0000000..baa9ddc --- /dev/null +++ b/edify/library/address/port.py @@ -0,0 +1,16 @@ +"""``port`` — TCP/UDP port number 0–65535.""" + +from __future__ import annotations + +from edify import Pattern, any_of + +port = any_of( + Pattern().start_of_input().string("6553").range("0", "5").end_of_input(), + Pattern().start_of_input().string("655").range("0", "2").digit().end_of_input(), + Pattern().start_of_input().string("65").range("0", "4").exactly(2).digit().end_of_input(), + Pattern().start_of_input().string("6").range("0", "4").exactly(3).digit().end_of_input(), + Pattern().start_of_input().range("1", "5").exactly(4).digit().end_of_input(), + Pattern().start_of_input().range("1", "9").between(0, 3).digit().end_of_input(), + Pattern().start_of_input().char("0").end_of_input(), +) +"""Callable :class:`Pattern` for a TCP/UDP port number 0–65535.""" diff --git a/edify/library/address/ptr.py b/edify/library/address/ptr.py new file mode 100644 index 0000000..5ce2303 --- /dev/null +++ b/edify/library/address/ptr.py @@ -0,0 +1,15 @@ +"""``ptr`` — reverse-DNS PTR record shape (``d.c.b.a.in-addr.arpa`` or IPv6 nibble form).""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +ptr = RegexBackedPattern( + r"^(?:" + r"(?:\d{1,3}\.){4}in-addr\.arpa\.?" + r"|(?:[0-9a-fA-F]\.){32}ip6\.arpa\.?" + r")$" +) +"""Callable :class:`Pattern` for the reverse-DNS PTR shape: IPv4 +``d.c.b.a.in-addr.arpa`` or IPv6 32-nibble ``…ip6.arpa`` form. +""" diff --git a/edify/library/address/socket.py b/edify/library/address/socket.py new file mode 100644 index 0000000..66c428e --- /dev/null +++ b/edify/library/address/socket.py @@ -0,0 +1,18 @@ +"""``socket`` — ``host:port`` socket address shape.""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +socket = RegexBackedPattern( + r"^" + r"(?:" + r"(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)" + r"(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}" + r"|\[[0-9a-fA-F:]+\]" + r"|[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?" + r"(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*" + r")" + r":\d{1,5}$" +) +"""Callable :class:`Pattern` for the ``host:port`` socket-address shape.""" diff --git a/edify/library/address/subdomain.py b/edify/library/address/subdomain.py new file mode 100644 index 0000000..8c480f8 --- /dev/null +++ b/edify/library/address/subdomain.py @@ -0,0 +1,17 @@ +"""``subdomain`` — DNS subdomain label shape (single label under an existing domain).""" + +from __future__ import annotations + +from edify import Pattern + +subdomain = ( + Pattern() + .start_of_input() + .any_of().range("a", "z").range("A", "Z").range("0", "9").end() + .between(0, 61).any_of().range("a", "z").range("A", "Z").range("0", "9").char("-").end() + .any_of().range("a", "z").range("A", "Z").range("0", "9").end() + .end_of_input() +) +"""Callable :class:`Pattern` for a single DNS subdomain label (1–63 chars, +alphanumeric with optional interior hyphens). +""" diff --git a/edify/library/address/subnet.py b/edify/library/address/subnet.py new file mode 100644 index 0000000..3ff5262 --- /dev/null +++ b/edify/library/address/subnet.py @@ -0,0 +1,15 @@ +"""``subnet`` — dotted-decimal IPv4 subnet mask shape.""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +subnet = RegexBackedPattern( + r"^(?:255|254|252|248|240|224|192|128|0)\." + r"(?:255|254|252|248|240|224|192|128|0)\." + r"(?:255|254|252|248|240|224|192|128|0)\." + r"(?:255|254|252|248|240|224|192|128|0)$" +) +"""Callable :class:`Pattern` for a dotted-decimal IPv4 subnet mask +(each octet is one of ``255``, ``254``, ``252``, …, ``128``, ``0``). +""" diff --git a/edify/library/address/tld.py b/edify/library/address/tld.py new file mode 100644 index 0000000..5c405fb --- /dev/null +++ b/edify/library/address/tld.py @@ -0,0 +1,13 @@ +"""``tld`` — top-level domain shape (2–63 alpha).""" + +from __future__ import annotations + +from edify import Pattern + +tld = ( + Pattern() + .start_of_input() + .between(2, 63).any_of().range("a", "z").range("A", "Z").end() + .end_of_input() +) +"""Callable :class:`Pattern` for the TLD shape: 2 to 63 letters.""" diff --git a/edify/library/address/uri.py b/edify/library/address/uri.py new file mode 100644 index 0000000..b8033fe --- /dev/null +++ b/edify/library/address/uri.py @@ -0,0 +1,12 @@ +"""``uri`` — generic URI shape (scheme + path).""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +uri = RegexBackedPattern( + r"^[a-zA-Z][a-zA-Z0-9+.\-]*:[^\s]+$" +) +"""Callable :class:`Pattern` for the generic URI shape: +``scheme:opaque-or-path`` where scheme starts with a letter. +""" diff --git a/edify/library/address/url.py b/edify/library/address/url.py new file mode 100644 index 0000000..45bde6e --- /dev/null +++ b/edify/library/address/url.py @@ -0,0 +1,16 @@ +"""``url`` — HTTP/HTTPS URL shape (with or without protocol).""" + +from __future__ import annotations + +from edify.library._support.regex import RegexBackedPattern + +url = RegexBackedPattern( + r"^(?:https?://)?" + r"(?:www\.)?" + r"[-a-zA-Z0-9@:%._\+~#=]{1,256}" + r"\.[a-zA-Z0-9()]{1,6}" + r"\b(?:[-a-zA-Z0-9()@:%_\+.~#?&/=]*)$" +) +"""Callable :class:`Pattern` for the URL shape: optional ``http[s]://``, +optional ``www.``, host with dot-separated labels, TLD, and optional path. +""" |
