diff options
| author | natsuoto <[email protected]> | 2026-07-11 17:10:05 +0530 |
|---|---|---|
| committer | natsuoto <[email protected]> | 2026-07-11 17:10:05 +0530 |
| commit | ef2e40b7eb1c5c29a9ec5f203e2927dc42a533e2 (patch) | |
| tree | fd1ad74c079af76f6d1d31d8920ec6b212ca3851 | |
| parent | ef0d136cb9c6517c39bf0d693663034791eb0508 (diff) | |
| download | edify-ef2e40b7eb1c5c29a9ec5f203e2927dc42a533e2.tar.xz edify-ef2e40b7eb1c5c29a9ec5f203e2927dc42a533e2.zip | |
refactor(library): rewrite geo/postal to inline patterns; delete RegexBackedPattern escape hatch
44 files changed, 451 insertions, 719 deletions
diff --git a/edify/library/_support/regex.py b/edify/library/_support/regex.py deleted file mode 100644 index 69ab05b..0000000 --- a/edify/library/_support/regex.py +++ /dev/null @@ -1,43 +0,0 @@ -"""Private base class for validators whose regex is authored as a raw string. - -Used by the built-in library validators that carry a regex too dense to author -as a fluent chain (RFC 5322 email, full IPv6 form, etc.). The base class stores -the raw regex string, compiles it once at construction time, and threads it -through :meth:`to_regex_string`, :meth:`to_regex`, and :meth:`__call__` so the -resulting instance is indistinguishable from a fluent-chain :class:`Pattern` -for the purposes of ``isinstance``, ``.match()``, and the ``pattern(value)`` -call form. -""" - -from __future__ import annotations - -import re - -from edify.pattern.composition import Pattern -from edify.result.regex import Regex - - -class RegexBackedPattern(Pattern): - """A :class:`Pattern` whose emitted regex is a pre-authored raw string. - - Attributes: - raw_regex_string: The exact regex string emitted by :meth:`to_regex_string`. - """ - - def __init__(self, raw_regex_string: str) -> None: - super().__init__() - self._raw_regex_string = raw_regex_string - self._compiled_regex = re.compile(raw_regex_string) - - @property - def raw_regex_string(self) -> str: - """Return the raw regex string this pattern was constructed from.""" - return self._raw_regex_string - - def to_regex_string(self) -> str: - """Return the raw regex string verbatim.""" - return self._raw_regex_string - - def to_regex(self) -> Regex: - """Return a :class:`Regex` wrapping the pre-compiled pattern.""" - return Regex(self._raw_regex_string, self._compiled_regex, ()) diff --git a/edify/library/_support/zip.py b/edify/library/_support/zip.py deleted file mode 100644 index 9695a9d..0000000 --- a/edify/library/_support/zip.py +++ /dev/null @@ -1,272 +0,0 @@ -# flake8: noqa - -ZIP_LOCALES = [ - {"abbrev": "AF", "name": "Afghanistan", "zip": "[0-9]{4}"}, - {"abbrev": "AL", "name": "Albania", "zip": "(120|122)[0-9]{2}"}, - {"abbrev": "DZ", "name": "Algeria", "zip": "[0-9]{5}"}, - {"abbrev": "AS", "name": "American Samoa", "zip": "[0-9]{5}"}, - {"abbrev": "AD", "name": "Andorra", "zip": "[0-9]{5}"}, - {"abbrev": "AO", "name": "Angola"}, - {"abbrev": "AI", "name": "Anguilla", "zip": "AI-2640"}, - {"abbrev": "AG", "name": "Antigua and Barbuda"}, - {"abbrev": "AR", "name": "Argentina", "zip": "[A-Z]{1}[0-9]{4}[A-Z]{3}"}, - {"abbrev": "AM", "name": "Armenia", "zip": "[0-9]{4}"}, - {"abbrev": "AW", "name": "Aruba"}, - {"abbrev": "AU", "name": "Australia", "zip": "[0-9]{4}"}, - {"abbrev": "AT", "name": "Austria", "zip": "[0-9]{4}"}, - {"abbrev": "AZ", "name": "Azerbaijan", "zip": "[0-9]{4}"}, - {"abbrev": "BS", "name": "Bahamas"}, - {"abbrev": "BH", "name": "Bahrain"}, - {"abbrev": "BD", "name": "Bangladesh", "zip": "[0-9]{4}"}, - {"abbrev": "BB", "name": "Barbados", "zip": "BB[0-9]{5}"}, - {"abbrev": "BY", "name": "Belarus", "zip": "[0-9]{6}"}, - {"abbrev": "BE", "name": "Belgium", "zip": "[0-9]{4}"}, - {"abbrev": "BZ", "name": "Belize"}, - {"abbrev": "BJ", "name": "Benin"}, - {"abbrev": "BM", "name": "Bermuda", "zip": "[A-Z]{2}[0-9]{2}"}, - {"abbrev": "BT", "name": "Bhutan", "zip": "[0-9]{5}"}, - {"abbrev": "BO", "name": "Bolivia"}, - {"abbrev": "BQ", "name": "Bonaire"}, - {"abbrev": "BA", "name": "Bosnia and Herzegovina", "zip": "[0-9]{5}"}, - {"abbrev": "BW", "name": "Botswana"}, - {"abbrev": "BR", "name": "Brazil", "zip": "[0-9]{5}-[0-9]{3}"}, - {"abbrev": "BN", "name": "Brunei", "zip": "[A-Z]{2}[0-9]{4}"}, - {"abbrev": "BG", "name": "Bulgaria", "zip": "[0-9]{4}"}, - {"abbrev": "BF", "name": "Burkina Faso"}, - {"abbrev": "BI", "name": "Burundi"}, - {"abbrev": "KH", "name": "Cambodia", "zip": "[0-9]{5}"}, - {"abbrev": "CM", "name": "Cameroon"}, - {"abbrev": "CA", "name": "Canada", "zip": "[A-Z][0-9][A-Z] ?[0-9][A-Z][0-9]"}, - {"abbrev": "CI", "name": "Canary Islands", "zip": "[0-9]{5}"}, - {"abbrev": "CV", "name": "Cape Verde", "zip": "[0-9]{4}"}, - {"abbrev": "KY", "name": "Cayman Islands", "zip": "[A-Z]{2}[0-9]-[0-9]{4}"}, - {"abbrev": "CF", "name": "Central African Republic"}, - {"abbrev": "TD", "name": "Chad"}, - {"abbrev": "CI", "name": "Channel Islands", "zip": "[A-Z]{2}[0-9]{2}"}, - {"abbrev": "CL", "name": "Chile", "zip": "[0-9]{7}"}, - {"abbrev": "CN", "name": "China, People's Republic", "zip": "[0-9]{6}"}, - {"abbrev": "CO", "name": "Colombia", "zip": "[0-9]{6}"}, - {"abbrev": "KM", "name": "Comoros"}, - {"abbrev": "CG", "name": "Congo"}, - {"abbrev": "CD", "name": "Congo, The Democratic Republic of"}, - {"abbrev": "CK", "name": "Cook Islands"}, - {"abbrev": "CR", "name": "Costa Rica", "zip": "[0-9]{5}"}, - {"abbrev": "CI", "name": "Côte d'Ivoire"}, - {"abbrev": "HR", "name": "Croatia", "zip": "[0-9]{5}"}, - {"abbrev": "CU", "name": "Cuba", "zip": "[0-9]{5}"}, - {"abbrev": "CW", "name": "Curacao"}, - {"abbrev": "CY", "name": "Cyprus", "zip": "[0-9]{4}"}, - {"abbrev": "CZ", "name": "Czech Republic", "zip": "[0-9]{3} [0-9]{2}"}, - {"abbrev": "DK", "name": "Denmark", "zip": "[0-9]{5}"}, - {"abbrev": "DJ", "name": "Djibouti"}, - {"abbrev": "DM", "name": "Dominica"}, - {"abbrev": "DO", "name": "Dominican Republic", "zip": "[0-9]{5}"}, - {"abbrev": "TL", "name": "East Timor"}, - {"abbrev": "EC", "name": "Ecuador", "zip": "[0-9]{6}"}, - {"abbrev": "EG", "name": "Egypt", "zip": "[0-9]{5}"}, - {"abbrev": "SV", "name": "El Salvador", "zip": "[0-9]{4}"}, - {"abbrev": "ER", "name": "Eritrea"}, - {"abbrev": "EE", "name": "Estonia", "zip": "[0-9]{5}"}, - {"abbrev": "ET", "name": "Ethiopia", "zip": "[0-9]{4}"}, - {"abbrev": "FK", "name": "Falkland Islands", "zip": "FIQQ 1ZZ"}, - {"abbrev": "FO", "name": "Faroe Islands", "zip": "[0-9]{3}"}, - {"abbrev": "FJ", "name": "Fiji"}, - {"abbrev": "FI", "name": "Finland", "zip": "[0-9]{5}"}, - {"abbrev": "FR", "name": "France", "zip": "[0-9]{5}"}, - {"abbrev": "PF", "name": "French Polynesia", "zip": "987[0-9]{2}", "range": ["98700", "98790"]}, - {"abbrev": "GA", "name": "Gabon"}, - {"abbrev": "GM", "name": "Gambia"}, - {"abbrev": "GE", "name": "Georgia"}, - {"abbrev": "DE", "name": "Germany", "zip": "[0-9]{5}"}, - {"abbrev": "GH", "name": "Ghana"}, - {"abbrev": "GI", "name": "Gibraltar", "zip": "GX11 1AA"}, - {"abbrev": "GR", "name": "Greece", "zip": "[0-9]{3} [0-9]{2}"}, - {"abbrev": "GL", "name": "Greenland", "zip": "[0-9]{4}"}, - {"abbrev": "GD", "name": "Grenada"}, - {"abbrev": "GP", "name": "Guadeloupe", "zip": "971[0-9]{2}", "range": ["97100", "97190"]}, - {"abbrev": "GU", "name": "Guam", "zip": "\\d{5}(?:[-\\s]\\d{4})?", "range": ["96910", "96932"]}, - {"abbrev": "GT", "name": "Guatemala", "zip": "[0-9]{5}"}, - { - "abbrev": "GG", - "name": "Guernsey", - "zip": "([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\\s?[0-9][A-Za-z]{2})", - }, - {"abbrev": "GW", "name": "Guinea-Bissau", "zip": "[0-9]{4}"}, - {"abbrev": "GQ", "name": "Guinea-Equatorial"}, - {"abbrev": "GN", "name": "Guinea Republic", "zip": "[0-9]{3}"}, - {"abbrev": "GY", "name": "Guyana (British)"}, - {"abbrev": "GF", "name": "Guyana (French)", "zip": "973[0-9]{2}", "range": ["97300", "97390"]}, - {"abbrev": "HT", "name": "Haiti", "zip": "[0-9]{4}"}, - {"abbrev": "HN", "name": "Honduras", "zip": "[0-9]{5}"}, - {"abbrev": "HK", "name": "Hong Kong"}, - {"abbrev": "HU", "name": "Hungary", "zip": "[0-9]{4}"}, - {"abbrev": "IS", "name": "Iceland", "zip": "[0-9]{3}"}, - {"abbrev": "IN", "name": "India", "zip": "^(?!0{1})\\d{6}$"}, - {"abbrev": "ID", "name": "Indonesia", "zip": "[0-9]{5}"}, - {"abbrev": "IR", "name": "Iran", "zip": "[0-9]{5}"}, - {"abbrev": "IQ", "name": "Iraq", "zip": "[0-9]{5}"}, - { - "abbrev": "IE", - "name": "Ireland, Republic of", - "zip": "(?:^[AC-FHKNPRTV-Y][0-9]{2}|D6W)[ -]?[0-9AC-FHKNPRTV-Y]{4}$", - }, - {"abbrev": "FK", "name": "Islas Malvinas", "zip": "FIQQ 1ZZ"}, - {"abbrev": "IL", "name": "Israel", "zip": "[0-9]{5}|[0-9]{7}"}, - {"abbrev": "IT", "name": "Italy", "zip": "[0-9]{5}"}, - {"abbrev": "CI", "name": "Ivory Coast"}, - {"abbrev": "JM", "name": "Jamaica"}, - {"abbrev": "JP", "name": "Japan", "zip": "[0-9]{3}-[0-9]{4}"}, - { - "abbrev": "JE", - "name": "Jersey", - "zip": "([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\\s?[0-9][A-Za-z]{2})", - }, - {"abbrev": "JO", "name": "Jordan", "zip": "[0-9]{5}"}, - {"abbrev": "KZ", "name": "Kazakhstan", "zip": "[0-9]{6}"}, - {"abbrev": "KE", "name": "Kenya", "zip": "[0-9]{5}"}, - {"abbrev": "KI", "name": "Kiribati"}, - {"abbrev": "KR", "name": "Korea, Republic of", "zip": "[0-9]{5}"}, - {"abbrev": "KP", "name": "Korea, The D.P.R of"}, - {"abbrev": "XK", "name": "Kosovo", "zip": "[0-9]{5}"}, - {"abbrev": "KW", "name": "Kuwait", "zip": "[0-9]{5}"}, - {"abbrev": "KG", "name": "Kyrgyzstan", "zip": "[0-9]{6}"}, - {"abbrev": "LA", "name": "Laos", "zip": "[0-9]{5}"}, - {"abbrev": "LV", "name": "Latvia", "zip": "LV-[0-9]{4}"}, - {"abbrev": "LB", "name": "Lebanon", "zip": "[0-9]{4} [0-9]{4}"}, - {"abbrev": "LS", "name": "Lesotho", "zip": "[0-9]{3}"}, - {"abbrev": "LR", "name": "Liberia", "zip": "[0-9]{4}"}, - {"abbrev": "LY", "name": "Libya"}, - {"abbrev": "LI", "name": "Liechtenstein", "zip": "[0-9]{4}", "range": ["9485", "9498"]}, - {"abbrev": "LT", "name": "Lithuania", "zip": "LT-[0-9]{5}"}, - {"abbrev": "LU", "name": "Luxembourg", "zip": "[0-9]{4}"}, - {"abbrev": "MO", "name": "Macau"}, - {"abbrev": "MK", "name": "Macedonia, Republic of", "zip": "[0-9]{4}"}, - {"abbrev": "MG", "name": "Madagascar", "zip": "[0-9]{3}"}, - {"abbrev": "MW", "name": "Malawi"}, - {"abbrev": "MY", "name": "Malaysia", "zip": "[0-9]{5}"}, - {"abbrev": "MV", "name": "Maldives", "zip": "[0-9]{5}"}, - {"abbrev": "ML", "name": "Mali"}, - {"abbrev": "MT", "name": "Malta", "zip": "[A-Z]{3} [0-9]{4}"}, - { - "abbrev": "MH", - "name": "Marshall Islands", - "zip": "\\d{5}(?:[-\\s]\\d{4})?", - "range": ["96960", "96970"], - }, - {"abbrev": "MQ", "name": "Martinique", "zip": "972[0-9]{2}", "range": ["97200", "97290"]}, - {"abbrev": "MR", "name": "Mauritania"}, - {"abbrev": "MU", "name": "Mauritius", "zip": "[0-9]{5}"}, - {"abbrev": "YT", "name": "Mayotte", "zip": "976[0-9]{2}", "range": ["97600", "97690"]}, - {"abbrev": "MX", "name": "Mexico", "zip": "[0-9]{5}"}, - {"abbrev": "MD", "name": "Moldova, Republic of", "zip": "MD-?[0-9]{4}"}, - {"abbrev": "MC", "name": "Monaco", "zip": "980[0-9]{2}"}, - {"abbrev": "MN", "name": "Mongolia", "zip": "[0-9]{5}"}, - {"abbrev": "ME", "name": "Montenegro", "zip": "[0-9]{5}"}, - { - "abbrev": "MS", - "name": "Montserrat", - "zip": "MSR [0-9]{4}", - "range": ["MSR 1110", "MSR 1350"], - }, - {"abbrev": "MA", "name": "Morocco", "zip": "[0-9]{5}"}, - {"abbrev": "MZ", "name": "Mozambique", "zip": "[0-9]{4}"}, - {"abbrev": "MM", "name": "Myanmar", "zip": "[0-9]{5}"}, - {"abbrev": "NA", "name": "Namibia"}, - {"abbrev": "NR", "name": "Nauru"}, - {"abbrev": "NP", "name": "Nepal", "zip": "[0-9]{5}"}, - {"abbrev": "NL", "name": "Netherlands", "zip": "(?:NL-)?(\\d{4})\\s*([A-Z]{2})"}, - {"abbrev": "NC", "name": "New Caledonia", "zip": "988[0-9]{2}", "range": ["96950", "96952"]}, - {"abbrev": "NZ", "name": "New Zealand", "zip": "[0-9]{4}"}, - {"abbrev": "NI", "name": "Nicaragua"}, - {"abbrev": "NE", "name": "Niger", "zip": "[0-9]{4}"}, - {"abbrev": "NG", "name": "Nigeria", "zip": "[0-9]{6}"}, - {"abbrev": "NU", "name": "Niue"}, - {"abbrev": "MP", "name": "Northern Mariana Islands", "zip": "^\\d{5}(?:[-\\s]\\d{4})?$"}, - {"abbrev": "NO", "name": "Norway", "zip": "[0-9]{4}"}, - {"abbrev": "OM", "name": "Oman", "zip": "[0-9]{3}"}, - {"abbrev": "PK", "name": "Pakistan", "zip": "[0-9]{5}"}, - {"abbrev": "PW", "name": "Palau", "zip": "\\d{5}(?:[-\\s]\\d{4})?"}, - {"abbrev": "PA", "name": "Panama", "zip": "[0-9]{4}"}, - {"abbrev": "PG", "name": "Papua New Guinea", "zip": "[0-9]{3}"}, - {"abbrev": "PY", "name": "Paraguay", "zip": "[0-9]{4}"}, - {"abbrev": "PE", "name": "Peru", "zip": "[0-9]{5}"}, - {"abbrev": "PH", "name": "Philippines", "zip": "[0-9]{4}"}, - {"abbrev": "PL", "name": "Poland", "zip": "[0-9]{2}-[0-9]{3}"}, - {"abbrev": "PT", "name": "Portugal", "zip": "[0-9]{4}-[0-9]{3}"}, - {"abbrev": "PR", "name": "Puerto Rico", "zip": "\\d{5}(?:[-\\s]\\d{4})?"}, - {"abbrev": "QA", "name": "Qatar"}, - {"abbrev": "RE", "name": "Réunion", "zip": "974[0-9]{2}", "range": ["97400", "97490"]}, - {"abbrev": "RO", "name": "Romania", "zip": "[0-9]{6}"}, - {"abbrev": "RU", "name": "Russian Federation", "zip": "[0-9]{6}"}, - {"abbrev": "RW", "name": "Rwanda"}, - {"abbrev": "MP", "name": "Saipan", "zip": "96950"}, - {"abbrev": "WS", "name": "Samoa", "zip": "WS[0-9]{4}"}, - {"abbrev": "ST", "name": "Sao Tome and Principe"}, - {"abbrev": "SA", "name": "Saudi Arabia", "zip": "[0-9]{5}(-[0-9]{4})?"}, - {"abbrev": "SN", "name": "Senegal", "zip": "[0-9]{5}"}, - {"abbrev": "RS", "name": "Serbia", "zip": "[0-9]{5}"}, - {"abbrev": "SC", "name": "Seychelles"}, - {"abbrev": "SL", "name": "Sierra Leone"}, - {"abbrev": "SG", "name": "Singapore", "zip": "[0-9]{6}"}, - {"abbrev": "SK", "name": "Slovakia", "zip": "[0-9]{3} [0-9]{2}"}, - {"abbrev": "SI", "name": "Slovenia", "zip": "[0-9]{4}"}, - {"abbrev": "SB", "name": "Solomon Islands"}, - {"abbrev": "SO", "name": "Somalia", "zip": "[A-Z]{2} [0-9]{5}"}, - {"abbrev": "ZA", "name": "South Africa", "zip": "[0-9]{4}"}, - {"abbrev": "SS", "name": "South Sudan"}, - {"abbrev": "ES", "name": "Spain", "zip": "[0-9]{5}"}, - {"abbrev": "LK", "name": "Sri Lanka", "zip": "[0-9]{4}"}, - {"abbrev": "BL", "name": "St. Barthélemy", "zip": "[0-9]{5}", "range": ["97100", "97190"]}, - {"abbrev": "VI", "name": "St. Croix", "zip": "[0-9]{5}"}, - {"abbrev": "SE", "name": "St. Eustatius"}, - {"abbrev": "SH", "name": "St. Helena", "zip": "STHL 1ZZ"}, - {"abbrev": "AG", "name": "St. John", "zip": "\\d{5}(?:[-\\s]\\d{4})?"}, - {"abbrev": "KN", "name": "St. Kitts and Nevis", "zip": "[A-Z]{2}[0-9]{4}"}, - {"abbrev": "LC", "name": "St. Lucia", "zip": "[A-Z]{2}[0-9]{2} [0-9]{3}"}, - {"abbrev": "SX", "name": "St. Maarten"}, - {"abbrev": "VI", "name": "St. Thomas"}, - {"abbrev": "VC", "name": "St. Vincent and the Grenadines", "zip": "VC[0-9]{4}"}, - {"abbrev": "SD", "name": "Sudan", "zip": "[0-9]{5}"}, - {"abbrev": "SR", "name": "Suriname"}, - {"abbrev": "SZ", "name": "Swaziland", "zip": "[A-Z]{1}[0-9]{3}"}, - {"abbrev": "SE", "name": "Sweden", "zip": "[0-9]{3} [0-9]{2}"}, - {"abbrev": "CH", "name": "Switzerland", "zip": "[0-9]{4}"}, - {"abbrev": "SY", "name": "Syria"}, - {"abbrev": "PF", "name": "Tahiti", "zip": "[0-9]{5}"}, - {"abbrev": "TW", "name": "Taiwan", "zip": "[0-9]{3}(-[0-9]{2})?"}, - {"abbrev": "TZ", "name": "Tanzania", "zip": "[0-9]{5}"}, - {"abbrev": "TH", "name": "Thailand", "zip": "[0-9]{5}"}, - {"abbrev": "TG", "name": "Togo"}, - {"abbrev": "TO", "name": "Tonga"}, - {"abbrev": "VG", "name": "Tortola", "zip": "VG[0-9]{4}"}, - {"abbrev": "TT", "name": "Trinidad and Tobago", "zip": "[0-9]{6}"}, - {"abbrev": "TN", "name": "Tunisia", "zip": "[0-9]{4}"}, - {"abbrev": "TR", "name": "Turkey", "zip": "[0-9]{5}"}, - {"abbrev": "TM", "name": "Turkmenistan", "zip": "[0-9]{6}"}, - {"abbrev": "TC", "name": "Turks and Caicos Islands", "zip": "TKCA 1ZZ"}, - {"abbrev": "TV", "name": "Tuvalu"}, - {"abbrev": "UG", "name": "Uganda"}, - {"abbrev": "UA", "name": "Ukraine", "zip": "[0-9]{5}"}, - {"abbrev": "AE", "name": "United Arab Emirates"}, - { - "abbrev": "GB", - "name": "United Kingdom", - "zip": "([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\\s?[0-9][A-Za-z]{2})", - }, - {"abbrev": "US", "name": "United States of America", "zip": "^[0-9]{5}(?:-[0-9]{4})?$"}, - {"abbrev": "UY", "name": "Uruguay", "zip": "[0-9]{5}"}, - {"abbrev": "UZ", "name": "Uzbekistan", "zip": "[0-9]{6}"}, - {"abbrev": "VU", "name": "Vanuatu"}, - {"abbrev": "VE", "name": "Venezuela", "zip": "[0-9]{4}(-[A-Z]{1})?"}, - {"abbrev": "VN", "name": "Vietnam", "zip": "[0-9]{6}"}, - {"abbrev": "VG", "name": "Virgin Islands (British)", "zip": "VG[0-9]{4}"}, - { - "abbrev": "VI", - "name": "Virgin Islands (US)", - "range": ["00801", "00851"], - "zip": "\\d{5}(?:[-\\s]\\d{4})?", - }, - {"abbrev": "YE", "name": "Yemen"}, - {"abbrev": "ZM", "name": "Zambia", "zip": "[0-9]{5}"}, - {"abbrev": "ZW", "name": "Zimbabwe"}, -] diff --git a/edify/library/address/cidr.py b/edify/library/address/cidr.py index a2b9454..388cdb7 100644 --- a/edify/library/address/cidr.py +++ b/edify/library/address/cidr.py @@ -17,15 +17,7 @@ _ipv4_prefix = any_of( Pattern().optional().any_of_chars("12").digit(), ) -_hextet = ( - Pattern() - .between(1, 4) - .any_of() - .range("0", "9") - .range("a", "f") - .range("A", "F") - .end() -) +_hextet = Pattern().between(1, 4).any_of().range("0", "9").range("a", "f").range("A", "F").end() _ipv6_prefix = any_of( Pattern().string("12").any_of().range("0", "8").end(), @@ -57,12 +49,7 @@ _ipv6_cidr = ( .subexpression(_ipv6_prefix) ) -cidr = ( - Pattern() - .start_of_input() - .subexpression(any_of(_ipv4_cidr, _ipv6_cidr)) - .end_of_input() -) +cidr = Pattern().start_of_input().subexpression(any_of(_ipv4_cidr, _ipv6_cidr)).end_of_input() """Callable :class:`Pattern` for CIDR notation: IPv4 address + ``/0``-``/32`` or IPv6 address + ``/0``-``/128``. """ diff --git a/edify/library/address/ip.py b/edify/library/address/ip.py index e51ea54..d085164 100644 --- a/edify/library/address/ip.py +++ b/edify/library/address/ip.py @@ -10,15 +10,7 @@ def _hex_group() -> Pattern: return Pattern().between(1, 4).subexpression(hex_any) -_ipv4 = ( - Pattern() - .subexpression(octet) - .exactly(3) - .group() - .char(".") - .subexpression(octet) - .end() -) +_ipv4 = Pattern().subexpression(octet).exactly(3).group().char(".").subexpression(octet).end() def _b1() -> Pattern: @@ -34,15 +26,7 @@ def _b1() -> Pattern: def _b2() -> Pattern: - return ( - Pattern() - .between(1, 7) - .group() - .subexpression(_hex_group()) - .char(":") - .end() - .char(":") - ) + return Pattern().between(1, 7).group().subexpression(_hex_group()).char(":").end().char(":") def _b3() -> Pattern: @@ -95,14 +79,7 @@ def _b9() -> Pattern: .char(":") .group() .any_of() - .subexpression( - Pattern() - .between(1, 7) - .group() - .char(":") - .subexpression(_hex_group()) - .end() - ) + .subexpression(Pattern().between(1, 7).group().char(":").subexpression(_hex_group()).end()) .char(":") .end() .end() @@ -205,10 +182,5 @@ _ipv6 = any_of( _b_hybrid(), ) -ip = ( - Pattern() - .start_of_input() - .subexpression(any_of(_ipv4, _ipv6)) - .end_of_input() -) +ip = Pattern().start_of_input().subexpression(any_of(_ipv4, _ipv6)).end_of_input() """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 index 529f225..40f91ca 100644 --- a/edify/library/address/path.py +++ b/edify/library/address/path.py @@ -34,7 +34,7 @@ _windows = ( .one_or_more() .group() .one_or_more() - .anything_but_chars("\\/:*?\"<>|\r\n") + .anything_but_chars('\\/:*?"<>|\r\n') .optional() .char("\\") .end() @@ -43,24 +43,19 @@ _unc = ( Pattern() .string("\\\\") .one_or_more() - .anything_but_chars("\\/:*?\"<>|\r\n") + .anything_but_chars('\\/:*?"<>|\r\n') .char("\\") .one_or_more() - .anything_but_chars("\\/:*?\"<>|\r\n") + .anything_but_chars('\\/:*?"<>|\r\n') .zero_or_more() .group() .char("\\") .zero_or_more() - .anything_but_chars("\\/:*?\"<>|\r\n") + .anything_but_chars('\\/:*?"<>|\r\n') .end() ) -path = ( - Pattern() - .start_of_input() - .subexpression(any_of(_posix, _windows, _unc)) - .end_of_input() -) +path = Pattern().start_of_input().subexpression(any_of(_posix, _windows, _unc)).end_of_input() """Callable :class:`Pattern` for a filesystem path shape: POSIX (``/absolute`` or ``relative/``), Windows drive-letter, or UNC. """ diff --git a/edify/library/address/ptr.py b/edify/library/address/ptr.py index a8020d6..14d70ed 100644 --- a/edify/library/address/ptr.py +++ b/edify/library/address/ptr.py @@ -33,12 +33,7 @@ _ipv6_ptr = ( .char(".") ) -ptr = ( - Pattern() - .start_of_input() - .subexpression(any_of(_ipv4_ptr, _ipv6_ptr)) - .end_of_input() -) +ptr = Pattern().start_of_input().subexpression(any_of(_ipv4_ptr, _ipv6_ptr)).end_of_input() """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 index 020c7dc..2fa29c7 100644 --- a/edify/library/address/socket.py +++ b/edify/library/address/socket.py @@ -5,15 +5,7 @@ from __future__ import annotations from edify import Pattern, any_of from edify.library._support.atoms import octet -_ipv4 = ( - Pattern() - .subexpression(octet) - .exactly(3) - .group() - .char(".") - .subexpression(octet) - .end() -) +_ipv4 = Pattern().subexpression(octet).exactly(3).group().char(".").subexpression(octet).end() _ipv6_bracket = ( Pattern() .char("[") diff --git a/edify/library/color/color.py b/edify/library/color/color.py index 7503cc3..3102afd 100644 --- a/edify/library/color/color.py +++ b/edify/library/color/color.py @@ -123,10 +123,6 @@ color = any_of( .whitespace_char() .char(")") .end_of_input(), - Pattern() - .start_of_input() - .between(3, 20) - .letter() - .end_of_input(), + Pattern().start_of_input().between(3, 20).letter().end_of_input(), ) """Callable :class:`Pattern` for any common CSS colour shape.""" diff --git a/edify/library/contact/email.py b/edify/library/contact/email.py index dc98cdd..903691c 100644 --- a/edify/library/contact/email.py +++ b/edify/library/contact/email.py @@ -79,12 +79,7 @@ _basic_domain = ( .subexpression(_domain_label()) ) -_basic = ( - Pattern() - .subexpression(_basic_local) - .char("@") - .subexpression(_basic_domain) -) +_basic = Pattern().subexpression(_basic_local).char("@").subexpression(_basic_domain) def _quoted_text() -> Pattern: @@ -193,19 +188,9 @@ _ip_literal = ( _rfc_local = any_of(_basic_local, _quoted_local) _rfc_domain = any_of(_basic_domain, _ip_literal) -_rfc = ( - Pattern() - .subexpression(_rfc_local) - .char("@") - .subexpression(_rfc_domain) -) +_rfc = Pattern().subexpression(_rfc_local).char("@").subexpression(_rfc_domain) -email = ( - Pattern() - .start_of_input() - .subexpression(any_of(_basic, _rfc)) - .end_of_input() -) +email = Pattern().start_of_input().subexpression(any_of(_basic, _rfc)).end_of_input() """Callable :class:`Pattern` that accepts either the common permissive email shape or the full RFC 5322 mailbox shape. """ diff --git a/edify/library/contact/phone.py b/edify/library/contact/phone.py index 2293f5d..6ef4ed8 100644 --- a/edify/library/contact/phone.py +++ b/edify/library/contact/phone.py @@ -49,12 +49,7 @@ _international = ( ) _short = Pattern().between(2, 4).digit() -phone = ( - Pattern() - .start_of_input() - .subexpression(any_of(_international, _short)) - .end_of_input() -) +phone = Pattern().start_of_input().subexpression(any_of(_international, _short)).end_of_input() """Callable :class:`Pattern` for phone-number shapes: permissive international form (optional ``+``, country code, area code, and dash/dot/space separators) or 2-4 digit short-code fallback. diff --git a/edify/library/financial/wallet.py b/edify/library/financial/wallet.py index 6d6b106..e5f05bc 100644 --- a/edify/library/financial/wallet.py +++ b/edify/library/financial/wallet.py @@ -19,13 +19,7 @@ _bitcoin_legacy = ( ) _bitcoin_bech32 = ( - Pattern() - .string("bc1") - .between(25, 89) - .any_of() - .range("a", "z") - .range("0", "9") - .end() + Pattern().string("bc1").between(25, 89).any_of().range("a", "z").range("0", "9").end() ) _ethereum = ( diff --git a/edify/library/geo/coordinate.py b/edify/library/geo/coordinate.py index d69bdd4..ea9450d 100644 --- a/edify/library/geo/coordinate.py +++ b/edify/library/geo/coordinate.py @@ -4,16 +4,7 @@ from __future__ import annotations from edify import Pattern, any_of -_lat_ninety = ( - Pattern() - .string("90") - .optional() - .group() - .char(".") - .one_or_more() - .char("0") - .end() -) +_lat_ninety = Pattern().string("90").optional().group().char(".").one_or_more().char("0").end() _lat_below_ninety = ( Pattern() @@ -28,16 +19,7 @@ _lat_below_ninety = ( .end() ) -_lon_one_eighty = ( - Pattern() - .string("180") - .optional() - .group() - .char(".") - .one_or_more() - .char("0") - .end() -) +_lon_one_eighty = Pattern().string("180").optional().group().char(".").one_or_more().char("0").end() _lon_below_one_eighty_integer = any_of( Pattern().char("1").range("0", "7").digit(), diff --git a/edify/library/geo/postal.py b/edify/library/geo/postal.py index be8e869..0f52cb6 100644 --- a/edify/library/geo/postal.py +++ b/edify/library/geo/postal.py @@ -2,20 +2,236 @@ from __future__ import annotations -import re +from edify import Pattern, any_of -from edify.library._support.regex import RegexBackedPattern -from edify.library._support.zip import ZIP_LOCALES +_digit3 = Pattern().exactly(3).digit() +_digit4 = Pattern().exactly(4).digit() +_digit5 = Pattern().exactly(5).digit() +_digit6 = Pattern().exactly(6).digit() +_digit7 = Pattern().exactly(7).digit() -_alternatives: list[str] = [] -for _entry in ZIP_LOCALES: - _raw = _entry.get("zip") - if not _raw: - continue - _stripped = _raw.strip("^$") - _alternatives.append(f"(?:{_stripped})") +_alnum_upper = Pattern().any_of().range("A", "Z").range("0", "9").end() -postal = RegexBackedPattern(rf"^(?:{'|'.join(_alternatives)})$") -"""Callable :class:`Pattern` that accepts any known postal/ZIP shape by locale.""" +_prefix_120_or_122 = ( + Pattern().group().any_of().string("120").string("122").end().end().exactly(2).digit() +) + +_netherlands = ( + Pattern() + .optional() + .group() + .string("NL-") + .end() + .exactly(4) + .digit() + .zero_or_more() + .whitespace_char() + .exactly(2) + .uppercase() +) + +_eircode_prefix = ( + Pattern() + .any_of() + .char("A") + .range("C", "F") + .char("H") + .char("K") + .char("N") + .char("P") + .char("R") + .char("T") + .range("V", "Y") + .end() + .exactly(2) + .digit() +) +_eircode = ( + Pattern() + .group() + .any_of() + .subexpression(_eircode_prefix) + .string("D6W") + .end() + .end() + .optional() + .any_of_chars(" -") + .exactly(4) + .any_of() + .range("0", "9") + .char("A") + .range("C", "F") + .char("H") + .char("K") + .char("N") + .char("P") + .char("R") + .char("T") + .range("V", "Y") + .end() +) + +_uk_gir = Pattern().string("GIR 0AA") + +_uk_alpha_upper_lower = Pattern().any_of().range("A", "Z").range("a", "z").end() +_uk_alpha_no_ilo_upper_lower = ( + Pattern().any_of().range("A", "H").range("J", "Y").range("a", "h").range("j", "y").end() +) + +_uk_outward = any_of( + Pattern().subexpression(_uk_alpha_upper_lower).between(1, 2).digit(), + Pattern() + .subexpression(_uk_alpha_upper_lower) + .subexpression(_uk_alpha_no_ilo_upper_lower) + .between(1, 2) + .digit(), + Pattern().subexpression(_uk_alpha_upper_lower).digit().subexpression(_uk_alpha_upper_lower), + Pattern() + .subexpression(_uk_alpha_upper_lower) + .subexpression(_uk_alpha_no_ilo_upper_lower) + .digit() + .optional() + .subexpression(_uk_alpha_upper_lower), +) + +_uk_full = ( + Pattern() + .subexpression(_uk_outward) + .optional() + .whitespace_char() + .digit() + .exactly(2) + .subexpression(_uk_alpha_upper_lower) +) + +_uk = any_of(_uk_gir, _uk_full) -del re, _alternatives, _entry, _raw, _stripped +_lit_96950 = Pattern().string("96950") +_prefix_971 = Pattern().string("971").exactly(2).digit() +_prefix_972 = Pattern().string("972").exactly(2).digit() +_prefix_973 = Pattern().string("973").exactly(2).digit() +_prefix_974 = Pattern().string("974").exactly(2).digit() +_prefix_976 = Pattern().string("976").exactly(2).digit() +_prefix_980 = Pattern().string("980").exactly(2).digit() +_prefix_987 = Pattern().string("987").exactly(2).digit() +_prefix_988 = Pattern().string("988").exactly(2).digit() + +_ai_2640 = Pattern().string("AI-2640") +_bb_plus_5 = Pattern().string("BB").exactly(5).digit() +_fiqq = Pattern().string("FIQQ 1ZZ") +_gx11 = Pattern().string("GX11 1AA") +_lt_5 = Pattern().string("LT-").exactly(5).digit() +_lv_4 = Pattern().string("LV-").exactly(4).digit() +_md_4 = Pattern().string("MD").optional().char("-").exactly(4).digit() +_msr_4 = Pattern().string("MSR ").exactly(4).digit() +_sthl = Pattern().string("STHL 1ZZ") +_tkca = Pattern().string("TKCA 1ZZ") +_vc_4 = Pattern().string("VC").exactly(4).digit() +_vg_4 = Pattern().string("VG").exactly(4).digit() +_ws_4 = Pattern().string("WS").exactly(4).digit() + +_2_dash_3 = Pattern().exactly(2).digit().char("-").exactly(3).digit() +_3_sp_2 = Pattern().exactly(3).digit().whitespace_char().exactly(2).digit() +_3_opt_dash_2 = Pattern().exactly(3).digit().optional().group().char("-").exactly(2).digit().end() +_3_dash_4 = Pattern().exactly(3).digit().char("-").exactly(4).digit() +_4_sp_4 = Pattern().exactly(4).digit().whitespace_char().exactly(4).digit() +_4_opt_dash_letter = Pattern().exactly(4).digit().optional().group().char("-").uppercase().end() +_4_dash_3 = Pattern().exactly(4).digit().char("-").exactly(3).digit() +_5_opt_dash_4 = Pattern().exactly(5).digit().optional().group().char("-").exactly(4).digit().end() +_5_dash_3 = Pattern().exactly(5).digit().char("-").exactly(3).digit() +_5_or_7 = any_of(_digit5, _digit7) + +_ca = ( + Pattern() + .uppercase() + .digit() + .uppercase() + .optional() + .whitespace_char() + .digit() + .uppercase() + .digit() +) +_upper1_digit3 = Pattern().uppercase().exactly(3).digit() +_upper1_digit4_upper3 = Pattern().uppercase().exactly(4).digit().exactly(3).uppercase() +_upper2_sp_digit5 = Pattern().exactly(2).uppercase().whitespace_char().exactly(5).digit() +_upper2_digit1_dash_digit4 = Pattern().exactly(2).uppercase().digit().char("-").exactly(4).digit() +_upper2_digit2_sp_digit3 = ( + Pattern().exactly(2).uppercase().exactly(2).digit().whitespace_char().exactly(3).digit() +) +_upper2_digit2 = Pattern().exactly(2).uppercase().exactly(2).digit() +_upper2_digit4 = Pattern().exactly(2).uppercase().exactly(4).digit() +_upper3_sp_digit4 = Pattern().exactly(3).uppercase().whitespace_char().exactly(4).digit() + +_us_dash_or_space = ( + Pattern() + .exactly(5) + .digit() + .optional() + .group() + .any_of_chars("-") + .subexpression(Pattern().any_of().char("-").whitespace_char().end()) + .exactly(4) + .digit() + .end() +) + +_india = Pattern().assert_not_ahead().char("0").end().exactly(6).digit() + +_all_locales = any_of( + _prefix_120_or_122, + _netherlands, + _eircode, + _uk, + _lit_96950, + _prefix_971, + _prefix_972, + _prefix_973, + _prefix_974, + _prefix_976, + _prefix_980, + _prefix_987, + _prefix_988, + _ai_2640, + _bb_plus_5, + _fiqq, + _gx11, + _lt_5, + _lv_4, + _md_4, + _msr_4, + _sthl, + _tkca, + _vc_4, + _vg_4, + _ws_4, + _2_dash_3, + _3_sp_2, + _digit3, + _3_opt_dash_2, + _3_dash_4, + _4_sp_4, + _digit4, + _4_opt_dash_letter, + _4_dash_3, + _digit5, + _5_opt_dash_4, + _5_dash_3, + _5_or_7, + _digit6, + _digit7, + _ca, + _upper1_digit3, + _upper1_digit4_upper3, + _upper2_sp_digit5, + _upper2_digit1_dash_digit4, + _upper2_digit2_sp_digit3, + _upper2_digit2, + _upper2_digit4, + _upper3_sp_digit4, + _us_dash_or_space, + _india, +) + +postal = Pattern().start_of_input().subexpression(_all_locales).end_of_input() +"""Callable :class:`Pattern` that accepts any known postal/ZIP shape by locale.""" diff --git a/edify/library/media/extension.py b/edify/library/media/extension.py index 2a350bd..f9f5baf 100644 --- a/edify/library/media/extension.py +++ b/edify/library/media/extension.py @@ -4,12 +4,5 @@ from __future__ import annotations from edify import Pattern -extension = ( - Pattern() - .start_of_input() - .char(".") - .between(1, 10) - .alphanumeric() - .end_of_input() -) +extension = Pattern().start_of_input().char(".").between(1, 10).alphanumeric().end_of_input() """Callable :class:`Pattern` for a file extension: dot + 1-10 alphanumeric.""" diff --git a/edify/library/media/glob.py b/edify/library/media/glob.py index 4a4cfde..e18f482 100644 --- a/edify/library/media/glob.py +++ b/edify/library/media/glob.py @@ -6,15 +6,7 @@ from edify import Pattern def _not_ctrl() -> Pattern: - return ( - Pattern() - .assert_not_ahead() - .any_of() - .range("\x00", "\x1f") - .end() - .end() - .any_char() - ) + return Pattern().assert_not_ahead().any_of().range("\x00", "\x1f").end().end().any_char() glob = ( diff --git a/edify/library/medical/medical.py b/edify/library/medical/medical.py index e702681..97c4d16 100644 --- a/edify/library/medical/medical.py +++ b/edify/library/medical/medical.py @@ -7,23 +7,29 @@ from edify import Pattern, any_of _snomed = Pattern().between(6, 18).digit() _icd = ( Pattern() - .any_of().range("A", "T").range("V", "Z").end() + .any_of() + .range("A", "T") + .range("V", "Z") + .end() .digit() - .any_of().range("A", "Z").range("0", "9").end() + .any_of() + .range("A", "Z") + .range("0", "9") + .end() .optional() .group() .char(".") .between(1, 4) - .any_of().range("A", "Z").range("0", "9").end() + .any_of() + .range("A", "Z") + .range("0", "9") + .end() .end() ) _npi = Pattern().exactly(10).digit() _loinc = Pattern().between(1, 7).digit().char("-").digit() medical = ( - Pattern() - .start_of_input() - .subexpression(any_of(_snomed, _icd, _npi, _loinc)) - .end_of_input() + Pattern().start_of_input().subexpression(any_of(_snomed, _icd, _npi, _loinc)).end_of_input() ) """Callable :class:`Pattern` for medical-coding-system codes.""" diff --git a/edify/library/numeric/integer.py b/edify/library/numeric/integer.py index 5737758..ef1d13b 100644 --- a/edify/library/numeric/integer.py +++ b/edify/library/numeric/integer.py @@ -5,12 +5,6 @@ from __future__ import annotations from edify import Pattern integer = ( - Pattern() - .start_of_input() - .optional() - .any_of_chars("+-") - .one_or_more() - .digit() - .end_of_input() + Pattern().start_of_input().optional().any_of_chars("+-").one_or_more().digit().end_of_input() ) """Callable :class:`Pattern` for a signed decimal integer.""" diff --git a/edify/library/numeric/number.py b/edify/library/numeric/number.py index 8f316f5..109375e 100644 --- a/edify/library/numeric/number.py +++ b/edify/library/numeric/number.py @@ -10,15 +10,7 @@ def _sign() -> Pattern: _int = Pattern().subexpression(_sign()).one_or_more().digit() -_dec = ( - Pattern() - .subexpression(_sign()) - .one_or_more() - .digit() - .char(".") - .one_or_more() - .digit() -) +_dec = Pattern().subexpression(_sign()).one_or_more().digit().char(".").one_or_more().digit() _ldec = Pattern().subexpression(_sign()).char(".").one_or_more().digit() _sci_dec = ( Pattern() diff --git a/edify/library/numeric/ratio.py b/edify/library/numeric/ratio.py index 49ae1c7..bf797e5 100644 --- a/edify/library/numeric/ratio.py +++ b/edify/library/numeric/ratio.py @@ -5,13 +5,6 @@ from __future__ import annotations from edify import Pattern ratio = ( - Pattern() - .start_of_input() - .one_or_more() - .digit() - .char(":") - .one_or_more() - .digit() - .end_of_input() + Pattern().start_of_input().one_or_more().digit().char(":").one_or_more().digit().end_of_input() ) """Callable :class:`Pattern` for a ratio shape: ``digits:digits``.""" diff --git a/edify/library/publishing/arxiv.py b/edify/library/publishing/arxiv.py index c65f7cb..27dacd3 100644 --- a/edify/library/publishing/arxiv.py +++ b/edify/library/publishing/arxiv.py @@ -39,10 +39,5 @@ _old = ( .end() ) -arxiv = ( - Pattern() - .start_of_input() - .subexpression(any_of(_new, _old)) - .end_of_input() -) +arxiv = Pattern().start_of_input().subexpression(any_of(_new, _old)).end_of_input() """Callable :class:`Pattern` for an arXiv identifier.""" diff --git a/edify/library/publishing/pmc.py b/edify/library/publishing/pmc.py index f70e8a8..944876e 100644 --- a/edify/library/publishing/pmc.py +++ b/edify/library/publishing/pmc.py @@ -4,12 +4,5 @@ from __future__ import annotations from edify import Pattern -pmc = ( - Pattern() - .start_of_input() - .string("PMC") - .between(1, 9) - .digit() - .end_of_input() -) +pmc = Pattern().start_of_input().string("PMC").between(1, 9).digit().end_of_input() """Callable :class:`Pattern` for a PubMed Central identifier.""" diff --git a/edify/library/publishing/pmid.py b/edify/library/publishing/pmid.py index 85134ef..a7e6476 100644 --- a/edify/library/publishing/pmid.py +++ b/edify/library/publishing/pmid.py @@ -4,11 +4,5 @@ from __future__ import annotations from edify import Pattern -pmid = ( - Pattern() - .start_of_input() - .between(1, 8) - .digit() - .end_of_input() -) +pmid = Pattern().start_of_input().between(1, 8).digit().end_of_input() """Callable :class:`Pattern` for a PubMed identifier (1-8 digits).""" diff --git a/edify/library/software/ref.py b/edify/library/software/ref.py index ba9d253..c8f55e5 100644 --- a/edify/library/software/ref.py +++ b/edify/library/software/ref.py @@ -31,14 +31,7 @@ def _forbidden_incl_slash() -> Pattern: ) -_sha = ( - Pattern() - .between(7, 40) - .any_of() - .range("a", "f") - .range("0", "9") - .end() -) +_sha = Pattern().between(7, 40).any_of().range("a", "f").range("0", "9").end() _refs = ( Pattern() .string("refs/") @@ -53,17 +46,7 @@ _refs = ( .one_or_more() .subexpression(_forbidden()) ) -_bare = ( - Pattern() - .subexpression(_forbidden_incl_slash()) - .between(0, 127) - .subexpression(_forbidden()) -) +_bare = Pattern().subexpression(_forbidden_incl_slash()).between(0, 127).subexpression(_forbidden()) -ref = ( - Pattern() - .start_of_input() - .subexpression(any_of(_sha, _refs, _bare)) - .end_of_input() -) +ref = Pattern().start_of_input().subexpression(any_of(_sha, _refs, _bare)).end_of_input() """Callable :class:`Pattern` for a git ref: SHA, ``refs/heads/…``, or bare branch/tag name.""" diff --git a/edify/library/temporal/cron.py b/edify/library/temporal/cron.py index ada3d89..3c35e59 100644 --- a/edify/library/temporal/cron.py +++ b/edify/library/temporal/cron.py @@ -42,12 +42,7 @@ _expr = ( .subexpression(_field) ) -cron = ( - Pattern() - .start_of_input() - .subexpression(any_of(_alias, _expr)) - .end_of_input() -) +cron = Pattern().start_of_input().subexpression(any_of(_alias, _expr)).end_of_input() """Callable :class:`Pattern` for cron-expression shapes: shortcut aliases (``@daily`` etc.) or 5-/6-field whitespace-separated expressions. """ diff --git a/edify/library/temporal/datetime.py b/edify/library/temporal/datetime.py index 1e659d3..ad54099 100644 --- a/edify/library/temporal/datetime.py +++ b/edify/library/temporal/datetime.py @@ -8,39 +8,72 @@ from edify import Pattern, any_of def _iso_extended() -> Pattern: return ( Pattern() - .exactly(4).digit().char("-").exactly(2).digit().char("-").exactly(2).digit() + .exactly(4) + .digit() + .char("-") + .exactly(2) + .digit() + .char("-") + .exactly(2) + .digit() .any_of_chars("Tt ") - .exactly(2).digit().char(":").exactly(2).digit() - .optional().group().char(":").exactly(2).digit() - .optional().group().char(".").one_or_more().digit().end() + .exactly(2) + .digit() + .char(":") + .exactly(2) + .digit() + .optional() + .group() + .char(":") + .exactly(2) + .digit() + .optional() + .group() + .char(".") + .one_or_more() + .digit() .end() - .optional().group().any_of() + .end() + .optional() + .group() + .any_of() .any_of_chars("Zz") .subexpression( Pattern().any_of_chars("+-").exactly(2).digit().optional().char(":").exactly(2).digit() ) - .end().end() + .end() + .end() ) def _iso_basic() -> Pattern: return ( Pattern() - .exactly(4).digit().exactly(2).digit().exactly(2).digit() + .exactly(4) + .digit() + .exactly(2) + .digit() + .exactly(2) + .digit() .any_of_chars("Tt") - .exactly(2).digit().exactly(2).digit().exactly(2).digit() - .optional().group().any_of() + .exactly(2) + .digit() + .exactly(2) + .digit() + .exactly(2) + .digit() + .optional() + .group() + .any_of() .any_of_chars("Zz") .subexpression(Pattern().any_of_chars("+-").exactly(4).digit()) - .end().end() + .end() + .end() ) datetime = ( - Pattern() - .start_of_input() - .subexpression(any_of(_iso_extended(), _iso_basic())) - .end_of_input() + Pattern().start_of_input().subexpression(any_of(_iso_extended(), _iso_basic())).end_of_input() ) """Callable :class:`Pattern` for combined date-time shapes: ISO 8601 / RFC 3339 forms with ``T`` or space separator, optional fractional seconds, diff --git a/edify/library/temporal/duration.py b/edify/library/temporal/duration.py index 40129fe..6f7a9ea 100644 --- a/edify/library/temporal/duration.py +++ b/edify/library/temporal/duration.py @@ -10,8 +10,14 @@ def _num_with_letter(letter: str) -> Pattern: Pattern() .optional() .group() - .one_or_more().digit() - .optional().group().char(".").one_or_more().digit().end() + .one_or_more() + .digit() + .optional() + .group() + .char(".") + .one_or_more() + .digit() + .end() .char(letter) .end() ) @@ -21,13 +27,19 @@ def _duration_body() -> Pattern: return ( Pattern() .char("P") - .assert_ahead().any_char().end() + .assert_ahead() + .any_char() + .end() .subexpression(_num_with_letter("Y")) .subexpression(_num_with_letter("M")) .subexpression(_num_with_letter("W")) .subexpression(_num_with_letter("D")) - .optional().group() - .char("T").assert_ahead().digit().end() + .optional() + .group() + .char("T") + .assert_ahead() + .digit() + .end() .subexpression(_num_with_letter("H")) .subexpression(_num_with_letter("M")) .subexpression(_num_with_letter("S")) @@ -35,12 +47,7 @@ def _duration_body() -> Pattern: ) -duration = ( - Pattern() - .start_of_input() - .subexpression(_duration_body()) - .end_of_input() -) +duration = Pattern().start_of_input().subexpression(_duration_body()).end_of_input() """Callable :class:`Pattern` for the ISO 8601 duration shape: ``PnYnMnDTnHnMnS`` with optional fractional components. """ diff --git a/edify/library/temporal/epoch.py b/edify/library/temporal/epoch.py index fec49bb..ffd958e 100644 --- a/edify/library/temporal/epoch.py +++ b/edify/library/temporal/epoch.py @@ -4,15 +4,7 @@ from __future__ import annotations from edify import Pattern -epoch = ( - Pattern() - .start_of_input() - .optional() - .char("-") - .between(1, 10) - .digit() - .end_of_input() -) +epoch = Pattern().start_of_input().optional().char("-").between(1, 10).digit().end_of_input() """Callable :class:`Pattern` for a Unix epoch-seconds value: optional sign followed by 1-10 digits (fits in a 32-bit signed integer). """ diff --git a/edify/library/temporal/interval.py b/edify/library/temporal/interval.py index e1397b6..745ad97 100644 --- a/edify/library/temporal/interval.py +++ b/edify/library/temporal/interval.py @@ -8,18 +8,41 @@ from edify import Pattern, any_of def _iso_extended() -> Pattern: return ( Pattern() - .exactly(4).digit().char("-").exactly(2).digit().char("-").exactly(2).digit() + .exactly(4) + .digit() + .char("-") + .exactly(2) + .digit() + .char("-") + .exactly(2) + .digit() .any_of_chars("Tt ") - .exactly(2).digit().char(":").exactly(2).digit() - .optional().group().char(":").exactly(2).digit() - .optional().group().char(".").one_or_more().digit().end() + .exactly(2) + .digit() + .char(":") + .exactly(2) + .digit() + .optional() + .group() + .char(":") + .exactly(2) + .digit() + .optional() + .group() + .char(".") + .one_or_more() + .digit() .end() - .optional().group().any_of() + .end() + .optional() + .group() + .any_of() .any_of_chars("Zz") .subexpression( Pattern().any_of_chars("+-").exactly(2).digit().optional().char(":").exactly(2).digit() ) - .end().end() + .end() + .end() ) @@ -28,8 +51,14 @@ def _num_with_letter(letter: str) -> Pattern: Pattern() .optional() .group() - .one_or_more().digit() - .optional().group().char(".").one_or_more().digit().end() + .one_or_more() + .digit() + .optional() + .group() + .char(".") + .one_or_more() + .digit() + .end() .char(letter) .end() ) @@ -39,13 +68,19 @@ def _duration_body() -> Pattern: return ( Pattern() .char("P") - .assert_ahead().any_char().end() + .assert_ahead() + .any_char() + .end() .subexpression(_num_with_letter("Y")) .subexpression(_num_with_letter("M")) .subexpression(_num_with_letter("W")) .subexpression(_num_with_letter("D")) - .optional().group() - .char("T").assert_ahead().digit().end() + .optional() + .group() + .char("T") + .assert_ahead() + .digit() + .end() .subexpression(_num_with_letter("H")) .subexpression(_num_with_letter("M")) .subexpression(_num_with_letter("S")) diff --git a/edify/library/temporal/offset.py b/edify/library/temporal/offset.py index b72bf40..55a0943 100644 --- a/edify/library/temporal/offset.py +++ b/edify/library/temporal/offset.py @@ -15,11 +15,7 @@ offset = ( .any_of() .char("Z") .subexpression( - Pattern() - .any_of_chars("+-") - .subexpression(_hh) - .optional().char(":") - .range("0", "5").digit() + Pattern().any_of_chars("+-").subexpression(_hh).optional().char(":").range("0", "5").digit() ) .end() .end_of_input() diff --git a/edify/library/temporal/time.py b/edify/library/temporal/time.py index 5b2b2f5..65e74bf 100644 --- a/edify/library/temporal/time.py +++ b/edify/library/temporal/time.py @@ -10,9 +10,20 @@ _h24 = ( .subexpression(Pattern().char("2").range("0", "3")) .subexpression(Pattern().optional().any_of_chars("01").digit()) .end() - .char(":").range("0", "5").digit() - .optional().group().char(":").range("0", "5").digit() - .optional().group().char(".").between(1, 6).digit().end() + .char(":") + .range("0", "5") + .digit() + .optional() + .group() + .char(":") + .range("0", "5") + .digit() + .optional() + .group() + .char(".") + .between(1, 6) + .digit() + .end() .end() ) @@ -22,18 +33,22 @@ _h12 = ( .subexpression(Pattern().char("1").range("0", "2")) .subexpression(Pattern().optional().char("0").range("1", "9")) .end() - .char(":").range("0", "5").digit() - .optional().group().char(":").range("0", "5").digit().end() - .optional().whitespace_char() - .any_of_chars("AaPp").any_of_chars("Mm") + .char(":") + .range("0", "5") + .digit() + .optional() + .group() + .char(":") + .range("0", "5") + .digit() + .end() + .optional() + .whitespace_char() + .any_of_chars("AaPp") + .any_of_chars("Mm") ) -time = ( - Pattern() - .start_of_input() - .subexpression(any_of(_h24, _h12)) - .end_of_input() -) +time = Pattern().start_of_input().subexpression(any_of(_h24, _h12)).end_of_input() """Callable :class:`Pattern` for clock-time shapes: 24-hour ``HH:MM[:SS[.ffffff]]`` or 12-hour ``H:MM[:SS] AM/PM``. """ diff --git a/edify/library/temporal/timestamp.py b/edify/library/temporal/timestamp.py index 2cf4ed2..ec4fbba 100644 --- a/edify/library/temporal/timestamp.py +++ b/edify/library/temporal/timestamp.py @@ -4,15 +4,7 @@ from __future__ import annotations from edify import Pattern -timestamp = ( - Pattern() - .start_of_input() - .optional() - .char("-") - .between(10, 13) - .digit() - .end_of_input() -) +timestamp = Pattern().start_of_input().optional().char("-").between(10, 13).digit().end_of_input() """Callable :class:`Pattern` for a Unix epoch timestamp in seconds or milliseconds: optional sign followed by 10-13 digits. """ diff --git a/edify/library/temporal/timezone.py b/edify/library/temporal/timezone.py index 2bf5b54..1a6b329 100644 --- a/edify/library/temporal/timezone.py +++ b/edify/library/temporal/timezone.py @@ -33,12 +33,7 @@ _short = any_of( ) _abbrev = Pattern().between(2, 5).uppercase() -timezone = ( - Pattern() - .start_of_input() - .subexpression(any_of(_iana, _short, _abbrev)) - .end_of_input() -) +timezone = Pattern().start_of_input().subexpression(any_of(_iana, _short, _abbrev)).end_of_input() """Callable :class:`Pattern` for the timezone shape: IANA region/city (``America/Los_Angeles``), or short abbreviation (``UTC``, ``PST``, ``EST``, …). diff --git a/edify/library/text/alpha.py b/edify/library/text/alpha.py index 538aff8..e31e516 100644 --- a/edify/library/text/alpha.py +++ b/edify/library/text/alpha.py @@ -4,11 +4,5 @@ from __future__ import annotations from edify import Pattern -alpha = ( - Pattern() - .start_of_input() - .one_or_more() - .letter() - .end_of_input() -) +alpha = Pattern().start_of_input().one_or_more().letter().end_of_input() """Callable :class:`Pattern` for a letters-only string.""" diff --git a/edify/library/text/alphanumeric.py b/edify/library/text/alphanumeric.py index 6d3cadc..899b5ae 100644 --- a/edify/library/text/alphanumeric.py +++ b/edify/library/text/alphanumeric.py @@ -4,11 +4,5 @@ from __future__ import annotations from edify import Pattern -alphanumeric = ( - Pattern() - .start_of_input() - .one_or_more() - .alphanumeric() - .end_of_input() -) +alphanumeric = Pattern().start_of_input().one_or_more().alphanumeric().end_of_input() """Callable :class:`Pattern` for a letters-and-digits-only string.""" diff --git a/edify/library/text/ascii.py b/edify/library/text/ascii.py index 4412196..42abfde 100644 --- a/edify/library/text/ascii.py +++ b/edify/library/text/ascii.py @@ -4,13 +4,7 @@ from __future__ import annotations from edify import Pattern -ascii = ( - Pattern() - .start_of_input() - .one_or_more() - .range("\x20", "\x7E") - .end_of_input() -) +ascii = Pattern().start_of_input().one_or_more().range("\x20", "\x7e").end_of_input() """Callable :class:`Pattern` for a printable-ASCII-only string (``0x20``-``0x7E`` — space through tilde). """ diff --git a/edify/library/text/base.py b/edify/library/text/base.py index 959d7a0..77b3680 100644 --- a/edify/library/text/base.py +++ b/edify/library/text/base.py @@ -4,24 +4,9 @@ from __future__ import annotations from edify import Pattern, any_of -_base16 = ( - Pattern() - .one_or_more() - .any_of() - .range("0", "9") - .range("A", "F") - .range("a", "f") - .end() -) +_base16 = Pattern().one_or_more().any_of().range("0", "9").range("A", "F").range("a", "f").end() _base32 = ( - Pattern() - .one_or_more() - .any_of() - .range("A", "Z") - .range("2", "7") - .end() - .zero_or_more() - .char("=") + Pattern().one_or_more().any_of().range("A", "Z").range("2", "7").end().zero_or_more().char("=") ) _base58 = ( Pattern() diff --git a/edify/library/text/emoji.py b/edify/library/text/emoji.py index ec1caa8..b32b7ba 100644 --- a/edify/library/text/emoji.py +++ b/edify/library/text/emoji.py @@ -9,7 +9,7 @@ emoji = ( .start_of_input() .one_or_more() .any_of() - .range("\U0001F300", "\U0001FAFF") + .range("\U0001f300", "\U0001faff") .range("☀", "➿") .end() .end_of_input() diff --git a/edify/library/text/numeric.py b/edify/library/text/numeric.py index 67b6bc5..36950c3 100644 --- a/edify/library/text/numeric.py +++ b/edify/library/text/numeric.py @@ -4,11 +4,5 @@ from __future__ import annotations from edify import Pattern -numeric = ( - Pattern() - .start_of_input() - .one_or_more() - .digit() - .end_of_input() -) +numeric = Pattern().start_of_input().one_or_more().digit().end_of_input() """Callable :class:`Pattern` for a digits-only string.""" diff --git a/edify/library/text/printable.py b/edify/library/text/printable.py index 96e2382..af2e238 100644 --- a/edify/library/text/printable.py +++ b/edify/library/text/printable.py @@ -10,21 +10,15 @@ def _not_ctrl() -> Pattern: Pattern() .assert_not_ahead() .any_of() - .range("\x00", "\x1F") - .char("\x7F") + .range("\x00", "\x1f") + .char("\x7f") .end() .end() .any_char() ) -printable = ( - Pattern() - .start_of_input() - .one_or_more() - .subexpression(_not_ctrl()) - .end_of_input() -) +printable = Pattern().start_of_input().one_or_more().subexpression(_not_ctrl()).end_of_input() """Callable :class:`Pattern` for a printable-character string (anything except ASCII control codes ``0x00``-``0x1F`` and ``0x7F``). """ diff --git a/edify/library/text/script.py b/edify/library/text/script.py index d5e053d..154df33 100644 --- a/edify/library/text/script.py +++ b/edify/library/text/script.py @@ -4,27 +4,10 @@ from __future__ import annotations from edify import Pattern, any_of -_latin = ( - Pattern() - .one_or_more() - .any_of() - .range("A", "Z") - .range("a", "z") - .range("À", "ɏ") - .end() -) +_latin = Pattern().one_or_more().any_of().range("A", "Z").range("a", "z").range("À", "ɏ").end() _cyrillic = Pattern().one_or_more().range("Ѐ", "ӿ") _greek = Pattern().one_or_more().range("Ͱ", "Ͽ") -_cjk = ( - Pattern() - .one_or_more() - .any_of() - .range("一", "鿿") - .range("", "ゟ") - .range("゠", "ヿ") # noqa: RUF001 - .range("가", "") - .end() -) +_cjk = Pattern().one_or_more().any_of().range("一", "鿿").range("", "ヿ").range("가", "").end() _arabic = Pattern().one_or_more().range("", "ۿ") _hebrew = Pattern().one_or_more().range("", "") _devanagari = Pattern().one_or_more().range("ऀ", "ॿ") diff --git a/edify/library/text/unicode.py b/edify/library/text/unicode.py index a6d95ae..abb6a42 100644 --- a/edify/library/text/unicode.py +++ b/edify/library/text/unicode.py @@ -10,19 +10,13 @@ def _not_ctrl() -> Pattern: Pattern() .assert_not_ahead() .any_of() - .range("\x00", "\x1F") - .char("\x7F") + .range("\x00", "\x1f") + .char("\x7f") .end() .end() .any_char() ) -unicode = ( - Pattern() - .start_of_input() - .one_or_more() - .subexpression(_not_ctrl()) - .end_of_input() -) +unicode = Pattern().start_of_input().one_or_more().subexpression(_not_ctrl()).end_of_input() """Callable :class:`Pattern` for any Unicode string containing no control codes.""" diff --git a/edify/library/text/word.py b/edify/library/text/word.py index 7fbaff6..d7a7c47 100644 --- a/edify/library/text/word.py +++ b/edify/library/text/word.py @@ -4,13 +4,7 @@ from __future__ import annotations from edify import Pattern -word = ( - Pattern() - .start_of_input() - .one_or_more() - .word() - .end_of_input() -) +word = Pattern().start_of_input().one_or_more().word().end_of_input() """Callable :class:`Pattern` for a word-character string: letters, digits, and underscore. """ diff --git a/tests/library/media/regex.test.py b/tests/library/media/regex.test.py new file mode 100644 index 0000000..c1c687c --- /dev/null +++ b/tests/library/media/regex.test.py @@ -0,0 +1,13 @@ +from edify.library import regex + + +def test_valid_regex(): + assert regex(r"^\d+$") + + +def test_invalid_regex(): + assert not regex(r"(?P<name>") + + +def test_non_string(): + assert not regex(42) diff --git a/tests/library/password.test.py b/tests/library/password.test.py index ec3b627..45b1672 100644 --- a/tests/library/password.test.py +++ b/tests/library/password.test.py @@ -13,3 +13,7 @@ def test_password(): ) is False ) + + +def test_password_rejects_non_string(): + assert password(42) is False |
