super-easy-validator-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ """Stable error codes.
2
+
3
+ Message text is for humans and may be reworded between releases. Codes are
4
+ meant to be compared against, so they do not change.
5
+ """
6
+
7
+ REQUIRED = "REQUIRED"
8
+ DATA_REQUIRED = "DATA_REQUIRED"
9
+ DATA_NOT_OBJECT = "DATA_NOT_OBJECT"
10
+ UNEXPECTED_FIELD = "UNEXPECTED_FIELD"
11
+
12
+ NOT_STRING = "NOT_STRING"
13
+ NOT_NUMBER = "NOT_NUMBER"
14
+ NOT_NUMERIC_STRING = "NOT_NUMERIC_STRING"
15
+ NOT_BOOLEAN = "NOT_BOOLEAN"
16
+ NOT_BOOLEAN_STRING = "NOT_BOOLEAN_STRING"
17
+ NOT_ARRAY = "NOT_ARRAY"
18
+ NOT_OBJECT = "NOT_OBJECT"
19
+
20
+ NOT_EMAIL = "NOT_EMAIL"
21
+ NOT_URL = "NOT_URL"
22
+ NOT_DOMAIN = "NOT_DOMAIN"
23
+ NOT_NAME = "NOT_NAME"
24
+ NOT_FULLNAME = "NOT_FULLNAME"
25
+ NOT_USERNAME = "NOT_USERNAME"
26
+ NOT_ALPHA = "NOT_ALPHA"
27
+ NOT_ALPHANUMERIC = "NOT_ALPHANUMERIC"
28
+ NOT_PHONE = "NOT_PHONE"
29
+ NOT_PHONECODE = "NOT_PHONECODE"
30
+ NOT_OBJECTID = "NOT_OBJECTID"
31
+ NOT_UUID = "NOT_UUID"
32
+ NOT_DATE = "NOT_DATE"
33
+ NOT_DATEONLY = "NOT_DATEONLY"
34
+ NOT_TIME = "NOT_TIME"
35
+ NOT_IP = "NOT_IP"
36
+
37
+ NOT_LOWERCASE = "NOT_LOWERCASE"
38
+ NOT_UPPERCASE = "NOT_UPPERCASE"
39
+
40
+ NOT_INTEGER = "NOT_INTEGER"
41
+ NOT_POSITIVE = "NOT_POSITIVE"
42
+ NOT_NEGATIVE = "NOT_NEGATIVE"
43
+ NOT_NATURAL = "NOT_NATURAL"
44
+ NOT_WHOLE = "NOT_WHOLE"
45
+
46
+ LENGTH_MISMATCH = "LENGTH_MISMATCH"
47
+ DIGITS_MISMATCH = "DIGITS_MISMATCH"
48
+ TOO_SHORT = "TOO_SHORT"
49
+ TOO_LONG = "TOO_LONG"
50
+ TOO_SMALL = "TOO_SMALL"
51
+ TOO_LARGE = "TOO_LARGE"
52
+ DATE_TOO_EARLY = "DATE_TOO_EARLY"
53
+ DATE_TOO_LATE = "DATE_TOO_LATE"
54
+ NOT_EQUAL = "NOT_EQUAL"
55
+
56
+ DECIMAL_SIZE_MISMATCH = "DECIMAL_SIZE_MISMATCH"
57
+ DECIMAL_TOO_FEW = "DECIMAL_TOO_FEW"
58
+ DECIMAL_TOO_MANY = "DECIMAL_TOO_MANY"
59
+ NOT_A_NUMBER = "NOT_A_NUMBER"
60
+
61
+ ENUM_MISMATCH = "ENUM_MISMATCH"
62
+ REGEX_MISMATCH = "REGEX_MISMATCH"
63
+
64
+ ATLEAST_NOT_MET = "ATLEAST_NOT_MET"
65
+ ATMOST_EXCEEDED = "ATMOST_EXCEEDED"
66
+
67
+ NO_BRANCH_MATCHED = "NO_BRANCH_MATCHED"
68
+ CUSTOM_RULE_FAILED = "CUSTOM_RULE_FAILED"
69
+ NO_CASE_MATCHED = "NO_CASE_MATCHED"
70
+
71
+ INVALID_RULE = "INVALID_RULE"
72
+ INTERNAL_ERROR = "INTERNAL_ERROR"
73
+
74
+
75
+ class ErrorCodes:
76
+ """Namespace for the codes, so `ErrorCodes.REQUIRED` reads naturally."""
77
+
78
+ REQUIRED = REQUIRED
79
+ DATA_REQUIRED = DATA_REQUIRED
80
+ DATA_NOT_OBJECT = DATA_NOT_OBJECT
81
+ UNEXPECTED_FIELD = UNEXPECTED_FIELD
82
+
83
+ NOT_STRING = NOT_STRING
84
+ NOT_NUMBER = NOT_NUMBER
85
+ NOT_NUMERIC_STRING = NOT_NUMERIC_STRING
86
+ NOT_BOOLEAN = NOT_BOOLEAN
87
+ NOT_BOOLEAN_STRING = NOT_BOOLEAN_STRING
88
+ NOT_ARRAY = NOT_ARRAY
89
+ NOT_OBJECT = NOT_OBJECT
90
+
91
+ NOT_EMAIL = NOT_EMAIL
92
+ NOT_URL = NOT_URL
93
+ NOT_DOMAIN = NOT_DOMAIN
94
+ NOT_NAME = NOT_NAME
95
+ NOT_FULLNAME = NOT_FULLNAME
96
+ NOT_USERNAME = NOT_USERNAME
97
+ NOT_ALPHA = NOT_ALPHA
98
+ NOT_ALPHANUMERIC = NOT_ALPHANUMERIC
99
+ NOT_PHONE = NOT_PHONE
100
+ NOT_PHONECODE = NOT_PHONECODE
101
+ NOT_OBJECTID = NOT_OBJECTID
102
+ NOT_UUID = NOT_UUID
103
+ NOT_DATE = NOT_DATE
104
+ NOT_DATEONLY = NOT_DATEONLY
105
+ NOT_TIME = NOT_TIME
106
+ NOT_IP = NOT_IP
107
+
108
+ NOT_LOWERCASE = NOT_LOWERCASE
109
+ NOT_UPPERCASE = NOT_UPPERCASE
110
+
111
+ NOT_INTEGER = NOT_INTEGER
112
+ NOT_POSITIVE = NOT_POSITIVE
113
+ NOT_NEGATIVE = NOT_NEGATIVE
114
+ NOT_NATURAL = NOT_NATURAL
115
+ NOT_WHOLE = NOT_WHOLE
116
+
117
+ LENGTH_MISMATCH = LENGTH_MISMATCH
118
+ DIGITS_MISMATCH = DIGITS_MISMATCH
119
+ TOO_SHORT = TOO_SHORT
120
+ TOO_LONG = TOO_LONG
121
+ TOO_SMALL = TOO_SMALL
122
+ TOO_LARGE = TOO_LARGE
123
+ DATE_TOO_EARLY = DATE_TOO_EARLY
124
+ DATE_TOO_LATE = DATE_TOO_LATE
125
+ NOT_EQUAL = NOT_EQUAL
126
+
127
+ DECIMAL_SIZE_MISMATCH = DECIMAL_SIZE_MISMATCH
128
+ DECIMAL_TOO_FEW = DECIMAL_TOO_FEW
129
+ DECIMAL_TOO_MANY = DECIMAL_TOO_MANY
130
+ NOT_A_NUMBER = NOT_A_NUMBER
131
+
132
+ ENUM_MISMATCH = ENUM_MISMATCH
133
+ REGEX_MISMATCH = REGEX_MISMATCH
134
+
135
+ ATLEAST_NOT_MET = ATLEAST_NOT_MET
136
+ ATMOST_EXCEEDED = ATMOST_EXCEEDED
137
+
138
+ NO_BRANCH_MATCHED = NO_BRANCH_MATCHED
139
+ CUSTOM_RULE_FAILED = CUSTOM_RULE_FAILED
140
+ NO_CASE_MATCHED = NO_CASE_MATCHED
141
+
142
+ INVALID_RULE = INVALID_RULE
143
+ INTERNAL_ERROR = INTERNAL_ERROR
@@ -0,0 +1,316 @@
1
+ """Built-in format patterns.
2
+
3
+ Python's ``re`` has no ``\\p{L}`` / ``\\p{M}`` escapes, which the name and
4
+ fullname rules need in order to accept every script. The equivalent character
5
+ ranges are embedded below, derived from ``unicodedata`` categories, so the
6
+ package keeps its zero-dependency promise without paying a 0.12s Unicode scan
7
+ on every import.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+
14
+ # Unicode categories L* (letters) and M* (marks), as code-point ranges.
15
+ _LETTER_RANGES: tuple[tuple[int, int], ...] = (
16
+ (0x00041, 0x0005a), (0x00061, 0x0007a), (0x000aa, 0x000aa), (0x000b5, 0x000b5), (0x000ba, 0x000ba), (0x000c0, 0x000d6),
17
+ (0x000d8, 0x000f6), (0x000f8, 0x002c1), (0x002c6, 0x002d1), (0x002e0, 0x002e4), (0x002ec, 0x002ec), (0x002ee, 0x002ee),
18
+ (0x00370, 0x00374), (0x00376, 0x00377), (0x0037a, 0x0037d), (0x0037f, 0x0037f), (0x00386, 0x00386), (0x00388, 0x0038a),
19
+ (0x0038c, 0x0038c), (0x0038e, 0x003a1), (0x003a3, 0x003f5), (0x003f7, 0x00481), (0x0048a, 0x0052f), (0x00531, 0x00556),
20
+ (0x00559, 0x00559), (0x00560, 0x00588), (0x005d0, 0x005ea), (0x005ef, 0x005f2), (0x00620, 0x0064a), (0x0066e, 0x0066f),
21
+ (0x00671, 0x006d3), (0x006d5, 0x006d5), (0x006e5, 0x006e6), (0x006ee, 0x006ef), (0x006fa, 0x006fc), (0x006ff, 0x006ff),
22
+ (0x00710, 0x00710), (0x00712, 0x0072f), (0x0074d, 0x007a5), (0x007b1, 0x007b1), (0x007ca, 0x007ea), (0x007f4, 0x007f5),
23
+ (0x007fa, 0x007fa), (0x00800, 0x00815), (0x0081a, 0x0081a), (0x00824, 0x00824), (0x00828, 0x00828), (0x00840, 0x00858),
24
+ (0x00860, 0x0086a), (0x00870, 0x00887), (0x00889, 0x0088e), (0x008a0, 0x008c9), (0x00904, 0x00939), (0x0093d, 0x0093d),
25
+ (0x00950, 0x00950), (0x00958, 0x00961), (0x00971, 0x00980), (0x00985, 0x0098c), (0x0098f, 0x00990), (0x00993, 0x009a8),
26
+ (0x009aa, 0x009b0), (0x009b2, 0x009b2), (0x009b6, 0x009b9), (0x009bd, 0x009bd), (0x009ce, 0x009ce), (0x009dc, 0x009dd),
27
+ (0x009df, 0x009e1), (0x009f0, 0x009f1), (0x009fc, 0x009fc), (0x00a05, 0x00a0a), (0x00a0f, 0x00a10), (0x00a13, 0x00a28),
28
+ (0x00a2a, 0x00a30), (0x00a32, 0x00a33), (0x00a35, 0x00a36), (0x00a38, 0x00a39), (0x00a59, 0x00a5c), (0x00a5e, 0x00a5e),
29
+ (0x00a72, 0x00a74), (0x00a85, 0x00a8d), (0x00a8f, 0x00a91), (0x00a93, 0x00aa8), (0x00aaa, 0x00ab0), (0x00ab2, 0x00ab3),
30
+ (0x00ab5, 0x00ab9), (0x00abd, 0x00abd), (0x00ad0, 0x00ad0), (0x00ae0, 0x00ae1), (0x00af9, 0x00af9), (0x00b05, 0x00b0c),
31
+ (0x00b0f, 0x00b10), (0x00b13, 0x00b28), (0x00b2a, 0x00b30), (0x00b32, 0x00b33), (0x00b35, 0x00b39), (0x00b3d, 0x00b3d),
32
+ (0x00b5c, 0x00b5d), (0x00b5f, 0x00b61), (0x00b71, 0x00b71), (0x00b83, 0x00b83), (0x00b85, 0x00b8a), (0x00b8e, 0x00b90),
33
+ (0x00b92, 0x00b95), (0x00b99, 0x00b9a), (0x00b9c, 0x00b9c), (0x00b9e, 0x00b9f), (0x00ba3, 0x00ba4), (0x00ba8, 0x00baa),
34
+ (0x00bae, 0x00bb9), (0x00bd0, 0x00bd0), (0x00c05, 0x00c0c), (0x00c0e, 0x00c10), (0x00c12, 0x00c28), (0x00c2a, 0x00c39),
35
+ (0x00c3d, 0x00c3d), (0x00c58, 0x00c5a), (0x00c5d, 0x00c5d), (0x00c60, 0x00c61), (0x00c80, 0x00c80), (0x00c85, 0x00c8c),
36
+ (0x00c8e, 0x00c90), (0x00c92, 0x00ca8), (0x00caa, 0x00cb3), (0x00cb5, 0x00cb9), (0x00cbd, 0x00cbd), (0x00cdd, 0x00cde),
37
+ (0x00ce0, 0x00ce1), (0x00cf1, 0x00cf2), (0x00d04, 0x00d0c), (0x00d0e, 0x00d10), (0x00d12, 0x00d3a), (0x00d3d, 0x00d3d),
38
+ (0x00d4e, 0x00d4e), (0x00d54, 0x00d56), (0x00d5f, 0x00d61), (0x00d7a, 0x00d7f), (0x00d85, 0x00d96), (0x00d9a, 0x00db1),
39
+ (0x00db3, 0x00dbb), (0x00dbd, 0x00dbd), (0x00dc0, 0x00dc6), (0x00e01, 0x00e30), (0x00e32, 0x00e33), (0x00e40, 0x00e46),
40
+ (0x00e81, 0x00e82), (0x00e84, 0x00e84), (0x00e86, 0x00e8a), (0x00e8c, 0x00ea3), (0x00ea5, 0x00ea5), (0x00ea7, 0x00eb0),
41
+ (0x00eb2, 0x00eb3), (0x00ebd, 0x00ebd), (0x00ec0, 0x00ec4), (0x00ec6, 0x00ec6), (0x00edc, 0x00edf), (0x00f00, 0x00f00),
42
+ (0x00f40, 0x00f47), (0x00f49, 0x00f6c), (0x00f88, 0x00f8c), (0x01000, 0x0102a), (0x0103f, 0x0103f), (0x01050, 0x01055),
43
+ (0x0105a, 0x0105d), (0x01061, 0x01061), (0x01065, 0x01066), (0x0106e, 0x01070), (0x01075, 0x01081), (0x0108e, 0x0108e),
44
+ (0x010a0, 0x010c5), (0x010c7, 0x010c7), (0x010cd, 0x010cd), (0x010d0, 0x010fa), (0x010fc, 0x01248), (0x0124a, 0x0124d),
45
+ (0x01250, 0x01256), (0x01258, 0x01258), (0x0125a, 0x0125d), (0x01260, 0x01288), (0x0128a, 0x0128d), (0x01290, 0x012b0),
46
+ (0x012b2, 0x012b5), (0x012b8, 0x012be), (0x012c0, 0x012c0), (0x012c2, 0x012c5), (0x012c8, 0x012d6), (0x012d8, 0x01310),
47
+ (0x01312, 0x01315), (0x01318, 0x0135a), (0x01380, 0x0138f), (0x013a0, 0x013f5), (0x013f8, 0x013fd), (0x01401, 0x0166c),
48
+ (0x0166f, 0x0167f), (0x01681, 0x0169a), (0x016a0, 0x016ea), (0x016f1, 0x016f8), (0x01700, 0x01711), (0x0171f, 0x01731),
49
+ (0x01740, 0x01751), (0x01760, 0x0176c), (0x0176e, 0x01770), (0x01780, 0x017b3), (0x017d7, 0x017d7), (0x017dc, 0x017dc),
50
+ (0x01820, 0x01878), (0x01880, 0x01884), (0x01887, 0x018a8), (0x018aa, 0x018aa), (0x018b0, 0x018f5), (0x01900, 0x0191e),
51
+ (0x01950, 0x0196d), (0x01970, 0x01974), (0x01980, 0x019ab), (0x019b0, 0x019c9), (0x01a00, 0x01a16), (0x01a20, 0x01a54),
52
+ (0x01aa7, 0x01aa7), (0x01b05, 0x01b33), (0x01b45, 0x01b4c), (0x01b83, 0x01ba0), (0x01bae, 0x01baf), (0x01bba, 0x01be5),
53
+ (0x01c00, 0x01c23), (0x01c4d, 0x01c4f), (0x01c5a, 0x01c7d), (0x01c80, 0x01c8a), (0x01c90, 0x01cba), (0x01cbd, 0x01cbf),
54
+ (0x01ce9, 0x01cec), (0x01cee, 0x01cf3), (0x01cf5, 0x01cf6), (0x01cfa, 0x01cfa), (0x01d00, 0x01dbf), (0x01e00, 0x01f15),
55
+ (0x01f18, 0x01f1d), (0x01f20, 0x01f45), (0x01f48, 0x01f4d), (0x01f50, 0x01f57), (0x01f59, 0x01f59), (0x01f5b, 0x01f5b),
56
+ (0x01f5d, 0x01f5d), (0x01f5f, 0x01f7d), (0x01f80, 0x01fb4), (0x01fb6, 0x01fbc), (0x01fbe, 0x01fbe), (0x01fc2, 0x01fc4),
57
+ (0x01fc6, 0x01fcc), (0x01fd0, 0x01fd3), (0x01fd6, 0x01fdb), (0x01fe0, 0x01fec), (0x01ff2, 0x01ff4), (0x01ff6, 0x01ffc),
58
+ (0x02071, 0x02071), (0x0207f, 0x0207f), (0x02090, 0x0209c), (0x02102, 0x02102), (0x02107, 0x02107), (0x0210a, 0x02113),
59
+ (0x02115, 0x02115), (0x02119, 0x0211d), (0x02124, 0x02124), (0x02126, 0x02126), (0x02128, 0x02128), (0x0212a, 0x0212d),
60
+ (0x0212f, 0x02139), (0x0213c, 0x0213f), (0x02145, 0x02149), (0x0214e, 0x0214e), (0x02183, 0x02184), (0x02c00, 0x02ce4),
61
+ (0x02ceb, 0x02cee), (0x02cf2, 0x02cf3), (0x02d00, 0x02d25), (0x02d27, 0x02d27), (0x02d2d, 0x02d2d), (0x02d30, 0x02d67),
62
+ (0x02d6f, 0x02d6f), (0x02d80, 0x02d96), (0x02da0, 0x02da6), (0x02da8, 0x02dae), (0x02db0, 0x02db6), (0x02db8, 0x02dbe),
63
+ (0x02dc0, 0x02dc6), (0x02dc8, 0x02dce), (0x02dd0, 0x02dd6), (0x02dd8, 0x02dde), (0x02e2f, 0x02e2f), (0x03005, 0x03006),
64
+ (0x03031, 0x03035), (0x0303b, 0x0303c), (0x03041, 0x03096), (0x0309d, 0x0309f), (0x030a1, 0x030fa), (0x030fc, 0x030ff),
65
+ (0x03105, 0x0312f), (0x03131, 0x0318e), (0x031a0, 0x031bf), (0x031f0, 0x031ff), (0x03400, 0x04dbf), (0x04e00, 0x0a48c),
66
+ (0x0a4d0, 0x0a4fd), (0x0a500, 0x0a60c), (0x0a610, 0x0a61f), (0x0a62a, 0x0a62b), (0x0a640, 0x0a66e), (0x0a67f, 0x0a69d),
67
+ (0x0a6a0, 0x0a6e5), (0x0a717, 0x0a71f), (0x0a722, 0x0a788), (0x0a78b, 0x0a7cd), (0x0a7d0, 0x0a7d1), (0x0a7d3, 0x0a7d3),
68
+ (0x0a7d5, 0x0a7dc), (0x0a7f2, 0x0a801), (0x0a803, 0x0a805), (0x0a807, 0x0a80a), (0x0a80c, 0x0a822), (0x0a840, 0x0a873),
69
+ (0x0a882, 0x0a8b3), (0x0a8f2, 0x0a8f7), (0x0a8fb, 0x0a8fb), (0x0a8fd, 0x0a8fe), (0x0a90a, 0x0a925), (0x0a930, 0x0a946),
70
+ (0x0a960, 0x0a97c), (0x0a984, 0x0a9b2), (0x0a9cf, 0x0a9cf), (0x0a9e0, 0x0a9e4), (0x0a9e6, 0x0a9ef), (0x0a9fa, 0x0a9fe),
71
+ (0x0aa00, 0x0aa28), (0x0aa40, 0x0aa42), (0x0aa44, 0x0aa4b), (0x0aa60, 0x0aa76), (0x0aa7a, 0x0aa7a), (0x0aa7e, 0x0aaaf),
72
+ (0x0aab1, 0x0aab1), (0x0aab5, 0x0aab6), (0x0aab9, 0x0aabd), (0x0aac0, 0x0aac0), (0x0aac2, 0x0aac2), (0x0aadb, 0x0aadd),
73
+ (0x0aae0, 0x0aaea), (0x0aaf2, 0x0aaf4), (0x0ab01, 0x0ab06), (0x0ab09, 0x0ab0e), (0x0ab11, 0x0ab16), (0x0ab20, 0x0ab26),
74
+ (0x0ab28, 0x0ab2e), (0x0ab30, 0x0ab5a), (0x0ab5c, 0x0ab69), (0x0ab70, 0x0abe2), (0x0ac00, 0x0d7a3), (0x0d7b0, 0x0d7c6),
75
+ (0x0d7cb, 0x0d7fb), (0x0f900, 0x0fa6d), (0x0fa70, 0x0fad9), (0x0fb00, 0x0fb06), (0x0fb13, 0x0fb17), (0x0fb1d, 0x0fb1d),
76
+ (0x0fb1f, 0x0fb28), (0x0fb2a, 0x0fb36), (0x0fb38, 0x0fb3c), (0x0fb3e, 0x0fb3e), (0x0fb40, 0x0fb41), (0x0fb43, 0x0fb44),
77
+ (0x0fb46, 0x0fbb1), (0x0fbd3, 0x0fd3d), (0x0fd50, 0x0fd8f), (0x0fd92, 0x0fdc7), (0x0fdf0, 0x0fdfb), (0x0fe70, 0x0fe74),
78
+ (0x0fe76, 0x0fefc), (0x0ff21, 0x0ff3a), (0x0ff41, 0x0ff5a), (0x0ff66, 0x0ffbe), (0x0ffc2, 0x0ffc7), (0x0ffca, 0x0ffcf),
79
+ (0x0ffd2, 0x0ffd7), (0x0ffda, 0x0ffdc), (0x10000, 0x1000b), (0x1000d, 0x10026), (0x10028, 0x1003a), (0x1003c, 0x1003d),
80
+ (0x1003f, 0x1004d), (0x10050, 0x1005d), (0x10080, 0x100fa), (0x10280, 0x1029c), (0x102a0, 0x102d0), (0x10300, 0x1031f),
81
+ (0x1032d, 0x10340), (0x10342, 0x10349), (0x10350, 0x10375), (0x10380, 0x1039d), (0x103a0, 0x103c3), (0x103c8, 0x103cf),
82
+ (0x10400, 0x1049d), (0x104b0, 0x104d3), (0x104d8, 0x104fb), (0x10500, 0x10527), (0x10530, 0x10563), (0x10570, 0x1057a),
83
+ (0x1057c, 0x1058a), (0x1058c, 0x10592), (0x10594, 0x10595), (0x10597, 0x105a1), (0x105a3, 0x105b1), (0x105b3, 0x105b9),
84
+ (0x105bb, 0x105bc), (0x105c0, 0x105f3), (0x10600, 0x10736), (0x10740, 0x10755), (0x10760, 0x10767), (0x10780, 0x10785),
85
+ (0x10787, 0x107b0), (0x107b2, 0x107ba), (0x10800, 0x10805), (0x10808, 0x10808), (0x1080a, 0x10835), (0x10837, 0x10838),
86
+ (0x1083c, 0x1083c), (0x1083f, 0x10855), (0x10860, 0x10876), (0x10880, 0x1089e), (0x108e0, 0x108f2), (0x108f4, 0x108f5),
87
+ (0x10900, 0x10915), (0x10920, 0x10939), (0x10980, 0x109b7), (0x109be, 0x109bf), (0x10a00, 0x10a00), (0x10a10, 0x10a13),
88
+ (0x10a15, 0x10a17), (0x10a19, 0x10a35), (0x10a60, 0x10a7c), (0x10a80, 0x10a9c), (0x10ac0, 0x10ac7), (0x10ac9, 0x10ae4),
89
+ (0x10b00, 0x10b35), (0x10b40, 0x10b55), (0x10b60, 0x10b72), (0x10b80, 0x10b91), (0x10c00, 0x10c48), (0x10c80, 0x10cb2),
90
+ (0x10cc0, 0x10cf2), (0x10d00, 0x10d23), (0x10d4a, 0x10d65), (0x10d6f, 0x10d85), (0x10e80, 0x10ea9), (0x10eb0, 0x10eb1),
91
+ (0x10ec2, 0x10ec4), (0x10f00, 0x10f1c), (0x10f27, 0x10f27), (0x10f30, 0x10f45), (0x10f70, 0x10f81), (0x10fb0, 0x10fc4),
92
+ (0x10fe0, 0x10ff6), (0x11003, 0x11037), (0x11071, 0x11072), (0x11075, 0x11075), (0x11083, 0x110af), (0x110d0, 0x110e8),
93
+ (0x11103, 0x11126), (0x11144, 0x11144), (0x11147, 0x11147), (0x11150, 0x11172), (0x11176, 0x11176), (0x11183, 0x111b2),
94
+ (0x111c1, 0x111c4), (0x111da, 0x111da), (0x111dc, 0x111dc), (0x11200, 0x11211), (0x11213, 0x1122b), (0x1123f, 0x11240),
95
+ (0x11280, 0x11286), (0x11288, 0x11288), (0x1128a, 0x1128d), (0x1128f, 0x1129d), (0x1129f, 0x112a8), (0x112b0, 0x112de),
96
+ (0x11305, 0x1130c), (0x1130f, 0x11310), (0x11313, 0x11328), (0x1132a, 0x11330), (0x11332, 0x11333), (0x11335, 0x11339),
97
+ (0x1133d, 0x1133d), (0x11350, 0x11350), (0x1135d, 0x11361), (0x11380, 0x11389), (0x1138b, 0x1138b), (0x1138e, 0x1138e),
98
+ (0x11390, 0x113b5), (0x113b7, 0x113b7), (0x113d1, 0x113d1), (0x113d3, 0x113d3), (0x11400, 0x11434), (0x11447, 0x1144a),
99
+ (0x1145f, 0x11461), (0x11480, 0x114af), (0x114c4, 0x114c5), (0x114c7, 0x114c7), (0x11580, 0x115ae), (0x115d8, 0x115db),
100
+ (0x11600, 0x1162f), (0x11644, 0x11644), (0x11680, 0x116aa), (0x116b8, 0x116b8), (0x11700, 0x1171a), (0x11740, 0x11746),
101
+ (0x11800, 0x1182b), (0x118a0, 0x118df), (0x118ff, 0x11906), (0x11909, 0x11909), (0x1190c, 0x11913), (0x11915, 0x11916),
102
+ (0x11918, 0x1192f), (0x1193f, 0x1193f), (0x11941, 0x11941), (0x119a0, 0x119a7), (0x119aa, 0x119d0), (0x119e1, 0x119e1),
103
+ (0x119e3, 0x119e3), (0x11a00, 0x11a00), (0x11a0b, 0x11a32), (0x11a3a, 0x11a3a), (0x11a50, 0x11a50), (0x11a5c, 0x11a89),
104
+ (0x11a9d, 0x11a9d), (0x11ab0, 0x11af8), (0x11bc0, 0x11be0), (0x11c00, 0x11c08), (0x11c0a, 0x11c2e), (0x11c40, 0x11c40),
105
+ (0x11c72, 0x11c8f), (0x11d00, 0x11d06), (0x11d08, 0x11d09), (0x11d0b, 0x11d30), (0x11d46, 0x11d46), (0x11d60, 0x11d65),
106
+ (0x11d67, 0x11d68), (0x11d6a, 0x11d89), (0x11d98, 0x11d98), (0x11ee0, 0x11ef2), (0x11f02, 0x11f02), (0x11f04, 0x11f10),
107
+ (0x11f12, 0x11f33), (0x11fb0, 0x11fb0), (0x12000, 0x12399), (0x12480, 0x12543), (0x12f90, 0x12ff0), (0x13000, 0x1342f),
108
+ (0x13441, 0x13446), (0x13460, 0x143fa), (0x14400, 0x14646), (0x16100, 0x1611d), (0x16800, 0x16a38), (0x16a40, 0x16a5e),
109
+ (0x16a70, 0x16abe), (0x16ad0, 0x16aed), (0x16b00, 0x16b2f), (0x16b40, 0x16b43), (0x16b63, 0x16b77), (0x16b7d, 0x16b8f),
110
+ (0x16d40, 0x16d6c), (0x16e40, 0x16e7f), (0x16f00, 0x16f4a), (0x16f50, 0x16f50), (0x16f93, 0x16f9f), (0x16fe0, 0x16fe1),
111
+ (0x16fe3, 0x16fe3), (0x17000, 0x187f7), (0x18800, 0x18cd5), (0x18cff, 0x18d08), (0x1aff0, 0x1aff3), (0x1aff5, 0x1affb),
112
+ (0x1affd, 0x1affe), (0x1b000, 0x1b122), (0x1b132, 0x1b132), (0x1b150, 0x1b152), (0x1b155, 0x1b155), (0x1b164, 0x1b167),
113
+ (0x1b170, 0x1b2fb), (0x1bc00, 0x1bc6a), (0x1bc70, 0x1bc7c), (0x1bc80, 0x1bc88), (0x1bc90, 0x1bc99), (0x1d400, 0x1d454),
114
+ (0x1d456, 0x1d49c), (0x1d49e, 0x1d49f), (0x1d4a2, 0x1d4a2), (0x1d4a5, 0x1d4a6), (0x1d4a9, 0x1d4ac), (0x1d4ae, 0x1d4b9),
115
+ (0x1d4bb, 0x1d4bb), (0x1d4bd, 0x1d4c3), (0x1d4c5, 0x1d505), (0x1d507, 0x1d50a), (0x1d50d, 0x1d514), (0x1d516, 0x1d51c),
116
+ (0x1d51e, 0x1d539), (0x1d53b, 0x1d53e), (0x1d540, 0x1d544), (0x1d546, 0x1d546), (0x1d54a, 0x1d550), (0x1d552, 0x1d6a5),
117
+ (0x1d6a8, 0x1d6c0), (0x1d6c2, 0x1d6da), (0x1d6dc, 0x1d6fa), (0x1d6fc, 0x1d714), (0x1d716, 0x1d734), (0x1d736, 0x1d74e),
118
+ (0x1d750, 0x1d76e), (0x1d770, 0x1d788), (0x1d78a, 0x1d7a8), (0x1d7aa, 0x1d7c2), (0x1d7c4, 0x1d7cb), (0x1df00, 0x1df1e),
119
+ (0x1df25, 0x1df2a), (0x1e030, 0x1e06d), (0x1e100, 0x1e12c), (0x1e137, 0x1e13d), (0x1e14e, 0x1e14e), (0x1e290, 0x1e2ad),
120
+ (0x1e2c0, 0x1e2eb), (0x1e4d0, 0x1e4eb), (0x1e5d0, 0x1e5ed), (0x1e5f0, 0x1e5f0), (0x1e7e0, 0x1e7e6), (0x1e7e8, 0x1e7eb),
121
+ (0x1e7ed, 0x1e7ee), (0x1e7f0, 0x1e7fe), (0x1e800, 0x1e8c4), (0x1e900, 0x1e943), (0x1e94b, 0x1e94b), (0x1ee00, 0x1ee03),
122
+ (0x1ee05, 0x1ee1f), (0x1ee21, 0x1ee22), (0x1ee24, 0x1ee24), (0x1ee27, 0x1ee27), (0x1ee29, 0x1ee32), (0x1ee34, 0x1ee37),
123
+ (0x1ee39, 0x1ee39), (0x1ee3b, 0x1ee3b), (0x1ee42, 0x1ee42), (0x1ee47, 0x1ee47), (0x1ee49, 0x1ee49), (0x1ee4b, 0x1ee4b),
124
+ (0x1ee4d, 0x1ee4f), (0x1ee51, 0x1ee52), (0x1ee54, 0x1ee54), (0x1ee57, 0x1ee57), (0x1ee59, 0x1ee59), (0x1ee5b, 0x1ee5b),
125
+ (0x1ee5d, 0x1ee5d), (0x1ee5f, 0x1ee5f), (0x1ee61, 0x1ee62), (0x1ee64, 0x1ee64), (0x1ee67, 0x1ee6a), (0x1ee6c, 0x1ee72),
126
+ (0x1ee74, 0x1ee77), (0x1ee79, 0x1ee7c), (0x1ee7e, 0x1ee7e), (0x1ee80, 0x1ee89), (0x1ee8b, 0x1ee9b), (0x1eea1, 0x1eea3),
127
+ (0x1eea5, 0x1eea9), (0x1eeab, 0x1eebb), (0x20000, 0x2a6df), (0x2a700, 0x2b739), (0x2b740, 0x2b81d), (0x2b820, 0x2cea1),
128
+ (0x2ceb0, 0x2ebe0), (0x2ebf0, 0x2ee5d), (0x2f800, 0x2fa1d), (0x30000, 0x3134a), (0x31350, 0x323af),
129
+ )
130
+
131
+ _MARK_RANGES: tuple[tuple[int, int], ...] = (
132
+ (0x00300, 0x0036f), (0x00483, 0x00489), (0x00591, 0x005bd), (0x005bf, 0x005bf), (0x005c1, 0x005c2), (0x005c4, 0x005c5),
133
+ (0x005c7, 0x005c7), (0x00610, 0x0061a), (0x0064b, 0x0065f), (0x00670, 0x00670), (0x006d6, 0x006dc), (0x006df, 0x006e4),
134
+ (0x006e7, 0x006e8), (0x006ea, 0x006ed), (0x00711, 0x00711), (0x00730, 0x0074a), (0x007a6, 0x007b0), (0x007eb, 0x007f3),
135
+ (0x007fd, 0x007fd), (0x00816, 0x00819), (0x0081b, 0x00823), (0x00825, 0x00827), (0x00829, 0x0082d), (0x00859, 0x0085b),
136
+ (0x00897, 0x0089f), (0x008ca, 0x008e1), (0x008e3, 0x00903), (0x0093a, 0x0093c), (0x0093e, 0x0094f), (0x00951, 0x00957),
137
+ (0x00962, 0x00963), (0x00981, 0x00983), (0x009bc, 0x009bc), (0x009be, 0x009c4), (0x009c7, 0x009c8), (0x009cb, 0x009cd),
138
+ (0x009d7, 0x009d7), (0x009e2, 0x009e3), (0x009fe, 0x009fe), (0x00a01, 0x00a03), (0x00a3c, 0x00a3c), (0x00a3e, 0x00a42),
139
+ (0x00a47, 0x00a48), (0x00a4b, 0x00a4d), (0x00a51, 0x00a51), (0x00a70, 0x00a71), (0x00a75, 0x00a75), (0x00a81, 0x00a83),
140
+ (0x00abc, 0x00abc), (0x00abe, 0x00ac5), (0x00ac7, 0x00ac9), (0x00acb, 0x00acd), (0x00ae2, 0x00ae3), (0x00afa, 0x00aff),
141
+ (0x00b01, 0x00b03), (0x00b3c, 0x00b3c), (0x00b3e, 0x00b44), (0x00b47, 0x00b48), (0x00b4b, 0x00b4d), (0x00b55, 0x00b57),
142
+ (0x00b62, 0x00b63), (0x00b82, 0x00b82), (0x00bbe, 0x00bc2), (0x00bc6, 0x00bc8), (0x00bca, 0x00bcd), (0x00bd7, 0x00bd7),
143
+ (0x00c00, 0x00c04), (0x00c3c, 0x00c3c), (0x00c3e, 0x00c44), (0x00c46, 0x00c48), (0x00c4a, 0x00c4d), (0x00c55, 0x00c56),
144
+ (0x00c62, 0x00c63), (0x00c81, 0x00c83), (0x00cbc, 0x00cbc), (0x00cbe, 0x00cc4), (0x00cc6, 0x00cc8), (0x00cca, 0x00ccd),
145
+ (0x00cd5, 0x00cd6), (0x00ce2, 0x00ce3), (0x00cf3, 0x00cf3), (0x00d00, 0x00d03), (0x00d3b, 0x00d3c), (0x00d3e, 0x00d44),
146
+ (0x00d46, 0x00d48), (0x00d4a, 0x00d4d), (0x00d57, 0x00d57), (0x00d62, 0x00d63), (0x00d81, 0x00d83), (0x00dca, 0x00dca),
147
+ (0x00dcf, 0x00dd4), (0x00dd6, 0x00dd6), (0x00dd8, 0x00ddf), (0x00df2, 0x00df3), (0x00e31, 0x00e31), (0x00e34, 0x00e3a),
148
+ (0x00e47, 0x00e4e), (0x00eb1, 0x00eb1), (0x00eb4, 0x00ebc), (0x00ec8, 0x00ece), (0x00f18, 0x00f19), (0x00f35, 0x00f35),
149
+ (0x00f37, 0x00f37), (0x00f39, 0x00f39), (0x00f3e, 0x00f3f), (0x00f71, 0x00f84), (0x00f86, 0x00f87), (0x00f8d, 0x00f97),
150
+ (0x00f99, 0x00fbc), (0x00fc6, 0x00fc6), (0x0102b, 0x0103e), (0x01056, 0x01059), (0x0105e, 0x01060), (0x01062, 0x01064),
151
+ (0x01067, 0x0106d), (0x01071, 0x01074), (0x01082, 0x0108d), (0x0108f, 0x0108f), (0x0109a, 0x0109d), (0x0135d, 0x0135f),
152
+ (0x01712, 0x01715), (0x01732, 0x01734), (0x01752, 0x01753), (0x01772, 0x01773), (0x017b4, 0x017d3), (0x017dd, 0x017dd),
153
+ (0x0180b, 0x0180d), (0x0180f, 0x0180f), (0x01885, 0x01886), (0x018a9, 0x018a9), (0x01920, 0x0192b), (0x01930, 0x0193b),
154
+ (0x01a17, 0x01a1b), (0x01a55, 0x01a5e), (0x01a60, 0x01a7c), (0x01a7f, 0x01a7f), (0x01ab0, 0x01ace), (0x01b00, 0x01b04),
155
+ (0x01b34, 0x01b44), (0x01b6b, 0x01b73), (0x01b80, 0x01b82), (0x01ba1, 0x01bad), (0x01be6, 0x01bf3), (0x01c24, 0x01c37),
156
+ (0x01cd0, 0x01cd2), (0x01cd4, 0x01ce8), (0x01ced, 0x01ced), (0x01cf4, 0x01cf4), (0x01cf7, 0x01cf9), (0x01dc0, 0x01dff),
157
+ (0x020d0, 0x020f0), (0x02cef, 0x02cf1), (0x02d7f, 0x02d7f), (0x02de0, 0x02dff), (0x0302a, 0x0302f), (0x03099, 0x0309a),
158
+ (0x0a66f, 0x0a672), (0x0a674, 0x0a67d), (0x0a69e, 0x0a69f), (0x0a6f0, 0x0a6f1), (0x0a802, 0x0a802), (0x0a806, 0x0a806),
159
+ (0x0a80b, 0x0a80b), (0x0a823, 0x0a827), (0x0a82c, 0x0a82c), (0x0a880, 0x0a881), (0x0a8b4, 0x0a8c5), (0x0a8e0, 0x0a8f1),
160
+ (0x0a8ff, 0x0a8ff), (0x0a926, 0x0a92d), (0x0a947, 0x0a953), (0x0a980, 0x0a983), (0x0a9b3, 0x0a9c0), (0x0a9e5, 0x0a9e5),
161
+ (0x0aa29, 0x0aa36), (0x0aa43, 0x0aa43), (0x0aa4c, 0x0aa4d), (0x0aa7b, 0x0aa7d), (0x0aab0, 0x0aab0), (0x0aab2, 0x0aab4),
162
+ (0x0aab7, 0x0aab8), (0x0aabe, 0x0aabf), (0x0aac1, 0x0aac1), (0x0aaeb, 0x0aaef), (0x0aaf5, 0x0aaf6), (0x0abe3, 0x0abea),
163
+ (0x0abec, 0x0abed), (0x0fb1e, 0x0fb1e), (0x0fe00, 0x0fe0f), (0x0fe20, 0x0fe2f), (0x101fd, 0x101fd), (0x102e0, 0x102e0),
164
+ (0x10376, 0x1037a), (0x10a01, 0x10a03), (0x10a05, 0x10a06), (0x10a0c, 0x10a0f), (0x10a38, 0x10a3a), (0x10a3f, 0x10a3f),
165
+ (0x10ae5, 0x10ae6), (0x10d24, 0x10d27), (0x10d69, 0x10d6d), (0x10eab, 0x10eac), (0x10efc, 0x10eff), (0x10f46, 0x10f50),
166
+ (0x10f82, 0x10f85), (0x11000, 0x11002), (0x11038, 0x11046), (0x11070, 0x11070), (0x11073, 0x11074), (0x1107f, 0x11082),
167
+ (0x110b0, 0x110ba), (0x110c2, 0x110c2), (0x11100, 0x11102), (0x11127, 0x11134), (0x11145, 0x11146), (0x11173, 0x11173),
168
+ (0x11180, 0x11182), (0x111b3, 0x111c0), (0x111c9, 0x111cc), (0x111ce, 0x111cf), (0x1122c, 0x11237), (0x1123e, 0x1123e),
169
+ (0x11241, 0x11241), (0x112df, 0x112ea), (0x11300, 0x11303), (0x1133b, 0x1133c), (0x1133e, 0x11344), (0x11347, 0x11348),
170
+ (0x1134b, 0x1134d), (0x11357, 0x11357), (0x11362, 0x11363), (0x11366, 0x1136c), (0x11370, 0x11374), (0x113b8, 0x113c0),
171
+ (0x113c2, 0x113c2), (0x113c5, 0x113c5), (0x113c7, 0x113ca), (0x113cc, 0x113d0), (0x113d2, 0x113d2), (0x113e1, 0x113e2),
172
+ (0x11435, 0x11446), (0x1145e, 0x1145e), (0x114b0, 0x114c3), (0x115af, 0x115b5), (0x115b8, 0x115c0), (0x115dc, 0x115dd),
173
+ (0x11630, 0x11640), (0x116ab, 0x116b7), (0x1171d, 0x1172b), (0x1182c, 0x1183a), (0x11930, 0x11935), (0x11937, 0x11938),
174
+ (0x1193b, 0x1193e), (0x11940, 0x11940), (0x11942, 0x11943), (0x119d1, 0x119d7), (0x119da, 0x119e0), (0x119e4, 0x119e4),
175
+ (0x11a01, 0x11a0a), (0x11a33, 0x11a39), (0x11a3b, 0x11a3e), (0x11a47, 0x11a47), (0x11a51, 0x11a5b), (0x11a8a, 0x11a99),
176
+ (0x11c2f, 0x11c36), (0x11c38, 0x11c3f), (0x11c92, 0x11ca7), (0x11ca9, 0x11cb6), (0x11d31, 0x11d36), (0x11d3a, 0x11d3a),
177
+ (0x11d3c, 0x11d3d), (0x11d3f, 0x11d45), (0x11d47, 0x11d47), (0x11d8a, 0x11d8e), (0x11d90, 0x11d91), (0x11d93, 0x11d97),
178
+ (0x11ef3, 0x11ef6), (0x11f00, 0x11f01), (0x11f03, 0x11f03), (0x11f34, 0x11f3a), (0x11f3e, 0x11f42), (0x11f5a, 0x11f5a),
179
+ (0x13440, 0x13440), (0x13447, 0x13455), (0x1611e, 0x1612f), (0x16af0, 0x16af4), (0x16b30, 0x16b36), (0x16f4f, 0x16f4f),
180
+ (0x16f51, 0x16f87), (0x16f8f, 0x16f92), (0x16fe4, 0x16fe4), (0x16ff0, 0x16ff1), (0x1bc9d, 0x1bc9e), (0x1cf00, 0x1cf2d),
181
+ (0x1cf30, 0x1cf46), (0x1d165, 0x1d169), (0x1d16d, 0x1d172), (0x1d17b, 0x1d182), (0x1d185, 0x1d18b), (0x1d1aa, 0x1d1ad),
182
+ (0x1d242, 0x1d244), (0x1da00, 0x1da36), (0x1da3b, 0x1da6c), (0x1da75, 0x1da75), (0x1da84, 0x1da84), (0x1da9b, 0x1da9f),
183
+ (0x1daa1, 0x1daaf), (0x1e000, 0x1e006), (0x1e008, 0x1e018), (0x1e01b, 0x1e021), (0x1e023, 0x1e024), (0x1e026, 0x1e02a),
184
+ (0x1e08f, 0x1e08f), (0x1e130, 0x1e136), (0x1e2ae, 0x1e2ae), (0x1e2ec, 0x1e2ef), (0x1e4ec, 0x1e4ef), (0x1e5ee, 0x1e5ef),
185
+ (0x1e8d0, 0x1e8d6), (0x1e944, 0x1e94a), (0xe0100, 0xe01ef),
186
+ )
187
+
188
+
189
+ def _char_class(ranges: tuple[tuple[int, int], ...]) -> str:
190
+ parts = []
191
+ for lo, hi in ranges:
192
+ parts.append(f"\\U{lo:08x}" if lo == hi else f"\\U{lo:08x}-\\U{hi:08x}")
193
+ return "".join(parts)
194
+
195
+
196
+ _L = _char_class(_LETTER_RANGES)
197
+ _M = _char_class(_MARK_RANGES)
198
+
199
+ #: One name word: a letter, then letters or combining marks, optional period.
200
+ _WORD = f"[{_L}][{_L}{_M}]*\\.?"
201
+
202
+ RE_EMAIL = re.compile(
203
+ r"^[A-Z0-9_'%=+!`#~$*?^{}&|-]+([.][A-Z0-9_'%=+!`#~$*?^{}&|-]+)*"
204
+ r"@[A-Z0-9-]+(\.[A-Z0-9-]+)+$",
205
+ re.IGNORECASE,
206
+ )
207
+
208
+ RE_URL = re.compile(
209
+ r"^(https?://(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}"
210
+ r"|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}"
211
+ r"|https?://(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}"
212
+ r"|www\.[a-zA-Z0-9]+\.[^\s]{2,})$"
213
+ )
214
+
215
+ RE_DOMAIN = re.compile(
216
+ r"^[a-zA-Z0-9][a-zA-Z0-9-_]{0,61}[a-zA-Z0-9]{0,1}"
217
+ r"\.([a-zA-Z]{1,6}|[a-zA-Z0-9-]{1,30}\.[a-zA-Z]{2,3})$"
218
+ )
219
+
220
+ RE_NAME = re.compile(f"^{_WORD}(?:[ '\u2019\\-]{_WORD})*$")
221
+
222
+ RE_FULLNAME = re.compile(
223
+ f"^{_WORD}(?:['\u2019\\-]{_WORD})*(?: {_WORD}(?:['\u2019\\-]{_WORD})*)+$"
224
+ )
225
+
226
+ RE_USERNAME = re.compile(r"^[^\W_](?!.*?[._]{2})[\w.]{6,18}[^\W_]$")
227
+
228
+ RE_ALPHA = re.compile(r"^[A-Za-z]{1,}$")
229
+
230
+ RE_ALPHANUMERIC = re.compile(r"^[A-Za-z0-9]{1,}$")
231
+
232
+ RE_PHONE = re.compile(r"^(?:\+\d{1,3}\s?)?(?:\(\d+\))?(?:\d+\s?)+(?:\d{1,4})$")
233
+
234
+ RE_PHONECODE = re.compile(r"^\+\d{1,3}$")
235
+
236
+ RE_OBJECTID = re.compile(r"^[0-9a-fA-F]{24}$")
237
+
238
+ RE_UUID = re.compile(
239
+ r"^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b"
240
+ r"-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$"
241
+ )
242
+
243
+ RE_DATE = re.compile(
244
+ r"^(\d{4})(?:-?(0[1-9]|1[0-2])(?:-?([12]\d|0[1-9]|3[01]))?"
245
+ r"|(?:-?([12]\d|0[1-9]|3[01]))?-(0[1-9]|1[0-2]))"
246
+ r"(?:[T ](\d{2}):([0-5]\d):([0-5]\d)(?:\.(\d{1,4}))?)?"
247
+ r"(?:Z|([+-])([01]\d|2[0-3])(?::?([0-5]\d))?)?$"
248
+ )
249
+
250
+ RE_DATEONLY = re.compile(r"^(\d{4})-(0[1-9]|1[0-2])-([12]\d|0[1-9]|3[01])$")
251
+
252
+ RE_TIME = re.compile(r"^(?:[01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d))?(?:\.\d{1,4})?$")
253
+
254
+ RE_LOWER = re.compile(r"^[^A-Z]+$")
255
+
256
+ RE_UPPER = re.compile(r"^[^a-z]+$")
257
+
258
+ RE_IP = re.compile(
259
+ r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}"
260
+ r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
261
+ )
262
+
263
+ #: Patterns keyed by rule name, for the format rules.
264
+ FORMAT_PATTERNS = {
265
+ "email": RE_EMAIL,
266
+ "url": RE_URL,
267
+ "domain": RE_DOMAIN,
268
+ "name": RE_NAME,
269
+ "fullname": RE_FULLNAME,
270
+ "username": RE_USERNAME,
271
+ "alpha": RE_ALPHA,
272
+ "alphanumeric": RE_ALPHANUMERIC,
273
+ "phone": RE_PHONE,
274
+ "phonecode": RE_PHONECODE,
275
+ "objectid": RE_OBJECTID,
276
+ "uuid": RE_UUID,
277
+ "date": RE_DATE,
278
+ "dateonly": RE_DATEONLY,
279
+ "time": RE_TIME,
280
+ "lower": RE_LOWER,
281
+ "upper": RE_UPPER,
282
+ "ip": RE_IP,
283
+ }
284
+
285
+
286
+ def translate_user_regex(pattern: str) -> re.Pattern[str]:
287
+ """Accept a pattern in literal form, ``/pattern/flags``, or plain form.
288
+
289
+ Flags ``i``, ``m`` and ``s`` map to the matching ``re`` flags. ``g``, ``y``
290
+ and ``u`` are accepted and ignored: they are iteration or encoding flags
291
+ with no meaning for a full-string test.
292
+ """
293
+ body = pattern
294
+ flags = 0
295
+
296
+ if body.startswith("/"):
297
+ end = body.rfind("/")
298
+ if end == 0:
299
+ raise ValueError(f"has an unterminated regex literal: {pattern}")
300
+ body, raw_flags = body[1:end], body[end + 1 :]
301
+ for f in raw_flags:
302
+ if f == "i":
303
+ flags |= re.IGNORECASE
304
+ elif f == "m":
305
+ flags |= re.MULTILINE
306
+ elif f == "s":
307
+ flags |= re.DOTALL
308
+ elif f in ("g", "y", "u"):
309
+ continue
310
+ else:
311
+ raise ValueError(f"has an unsupported regex flag '{f}'")
312
+
313
+ try:
314
+ return re.compile(body, flags)
315
+ except re.error as exc:
316
+ raise ValueError(f"has an invalid regex: {exc}") from exc
@@ -0,0 +1,162 @@
1
+ """Rule-key resolution: dotted paths, and bracket/slice indexing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from typing import Any, Optional
7
+
8
+ from .types import Data
9
+
10
+ _BRACKET = re.compile(r"\[(-?\d*)(:?)(-?\d*)\]")
11
+ _HAS_BRACKET = re.compile(r"\[-?\d*:?-?\d*\]")
12
+ _SLICE_BRACKET = re.compile(r"\[-?\d*:-?\d*\]")
13
+ _TRAILING_INDEX = re.compile(r"\[-?\d+\]$")
14
+
15
+ _MISSING = object()
16
+
17
+
18
+ def has_index_syntax(key: str) -> bool:
19
+ return bool(_HAS_BRACKET.search(key))
20
+
21
+
22
+ def is_slice_key(key: str) -> bool:
23
+ return bool(_SLICE_BRACKET.search(key))
24
+
25
+
26
+ def ends_with_index(key: str) -> bool:
27
+ return bool(_TRAILING_INDEX.search(key))
28
+
29
+
30
+ def is_object(value: Any) -> bool:
31
+ """Objects are dicts. Nothing else counts."""
32
+ return isinstance(value, dict)
33
+
34
+
35
+ def is_array(value: Any) -> bool:
36
+ """Arrays are lists. A string is iterable but is not an array."""
37
+ return isinstance(value, list)
38
+
39
+
40
+ def get_prop_by_string(data: Data, prop_string: str) -> Any:
41
+ """Walk a dotted path.
42
+
43
+ Mirrors the reference implementation: the walk stops at the first segment
44
+ that is not an object, and the final segment is then read from the last
45
+ object reached.
46
+ """
47
+ if not prop_string:
48
+ return data
49
+
50
+ props = prop_string.split(".")
51
+ obj: Any = data
52
+ i = 0
53
+ for i in range(len(props) - 1):
54
+ if not is_object(obj):
55
+ break
56
+ candidate = obj.get(props[i], _MISSING)
57
+ if (
58
+ candidate is not _MISSING
59
+ and candidate is not None
60
+ and isinstance(candidate, (dict, list))
61
+ ):
62
+ obj = candidate
63
+ else:
64
+ break
65
+ else:
66
+ # The whole chain was walked, so the leaf is the last segment.
67
+ i = len(props) - 1
68
+
69
+ if not is_object(obj):
70
+ return None
71
+ return obj.get(props[i])
72
+
73
+
74
+ def _at(container: list, raw: str) -> Any:
75
+ n = int(raw)
76
+ if n < 0:
77
+ n = len(container) + n
78
+ if n < 0 or n >= len(container):
79
+ return None
80
+ return container[n]
81
+
82
+
83
+ def resolve_indexed_path(data: Data, key: str) -> tuple[Any, list[str]]:
84
+ """Resolve a key containing bracket syntax.
85
+
86
+ Returns the selected value and, for slices, a label per selected element.
87
+ """
88
+ current: Any = data
89
+ labels: Optional[list[str]] = None
90
+ prefix = ""
91
+
92
+ for segment in key.split("."):
93
+ bracket_start = segment.find("[")
94
+ base = segment if bracket_start == -1 else segment[:bracket_start]
95
+
96
+ if base:
97
+ if current is None or not isinstance(current, (dict, list)):
98
+ return None, []
99
+ if labels is not None:
100
+ picked: list[Any] = []
101
+ kept: list[str] = []
102
+ for i, el in enumerate(current):
103
+ if is_object(el):
104
+ picked.append(el.get(base))
105
+ kept.append(f"{labels[i]}.{base}")
106
+ current, labels, prefix = picked, kept, ""
107
+ continue
108
+ if not is_object(current):
109
+ return None, []
110
+ current = current.get(base)
111
+
112
+ prefix = f"{prefix}.{base}" if prefix else base
113
+
114
+ if bracket_start == -1:
115
+ continue
116
+
117
+ for m in _BRACKET.finditer(segment[bracket_start:]):
118
+ frm, colon, to = m.group(1), m.group(2), m.group(3)
119
+ if not is_array(current):
120
+ return None, []
121
+ if colon:
122
+ f = 0 if frm == "" else int(frm)
123
+ t = len(current) if to == "" else int(to)
124
+ start = max(len(current) + f, 0) if f < 0 else min(f, len(current))
125
+ # Python slicing already clamps and handles negative bounds
126
+ # the same way the reference implementation does.
127
+ picked_slice = current[f:t]
128
+ labels = [f"{prefix}[{start + i}]" for i in range(len(picked_slice))]
129
+ current = picked_slice
130
+ else:
131
+ current = _at(current, frm)
132
+ prefix = f"{prefix}[{frm}]"
133
+
134
+ return current, (labels if labels is not None else [])
135
+
136
+
137
+ def lookup(data: Data, key: str) -> Any:
138
+ """Resolve a plain (non-indexed) key."""
139
+ if "." not in key:
140
+ return data.get(key)
141
+ return get_prop_by_string(data, key)
142
+
143
+
144
+ def is_present(data: Data, key: str) -> bool:
145
+ """Whether a key exists at all, which is distinct from holding None."""
146
+ if "." not in key:
147
+ return key in data
148
+ props = key.split(".")
149
+ obj: Any = data
150
+ for p in props[:-1]:
151
+ if not is_object(obj):
152
+ return False
153
+ obj = obj.get(p)
154
+ return is_object(obj) and props[-1] in obj
155
+
156
+
157
+ def resolve(data: Data, key: str, array_indexing: bool = True) -> Any:
158
+ """Return the value a rule key selects."""
159
+ if array_indexing and has_index_syntax(key):
160
+ value, _ = resolve_indexed_path(data, key)
161
+ return value
162
+ return lookup(data, key)
File without changes