readable-regex 0.2.0__tar.gz

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,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .pytest_cache/
9
+ .ruff_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Derwin Emmanuel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: readable-regex
3
+ Version: 0.2.0
4
+ Summary: A fluent, chainable API for building regular expressions that read like English
5
+ Project-URL: Homepage, https://github.com/molestreettechllc-dev/readable-regex
6
+ Project-URL: Documentation, https://molestreettechllc-dev.github.io/readable-regex/
7
+ Project-URL: Repository, https://github.com/molestreettechllc-dev/readable-regex
8
+ Project-URL: Issues, https://github.com/molestreettechllc-dev/readable-regex/issues
9
+ Author: Derwin Emmanuel
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: builder,fluent,readable,regex,regular-expressions
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: Text Processing
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+
28
+ # readable-regex
29
+
30
+ A fluent, chainable Python API for building regular expressions that read like English.
31
+
32
+ **[Documentation](https://molestreettechllc-dev.github.io/readable-regex/)**
33
+
34
+ ## Install
35
+
36
+ ```bash
37
+ pip install readable-regex
38
+ ```
39
+
40
+ ## Quick Start
41
+
42
+ ```python
43
+ from readable_regex import regex
44
+
45
+ # Email pattern
46
+ regex.words.then('@').words.then('.').words.test("user@example.com") # True
47
+
48
+ # Phone number
49
+ regex.digit.exactly(3).then('-').digit.exactly(3).then('-').digit.exactly(4).test("123-456-7890") # True
50
+
51
+ # Extract all numbers
52
+ regex.digits.find_all("Order #42 has 3 items totaling $129") # ['42', '3', '129']
53
+ ```
54
+
55
+ ## Vocabulary
56
+
57
+ The API uses a **plural convention**: singular = exactly one, plural = one or more.
58
+
59
+ ### Items — what you match
60
+
61
+ | Singular | Plural (1+) | Regex |
62
+ |---|---|---|
63
+ | `digit` | `digits` | `\d` / `\d+` |
64
+ | `word` | `words` | `\w` / `\w+` |
65
+ | `letter` | `letters` | `[a-zA-Z]` / `[a-zA-Z]+` |
66
+ | `whitespace` | `whitespaces` | `\s` / `\s+` |
67
+ | `any_char` | `any_chars` | `.` / `.+` |
68
+ | `then('text')` | — | escaped literal |
69
+ | `any_of('a', 'b')` | — | `[ab]` or `(?:foo\|bar)` |
70
+
71
+ ### Modifiers — how you constrain
72
+
73
+ | Modifier | Example | Effect |
74
+ |---|---|---|
75
+ | `exactly(n)` | `digit.exactly(3)` | `\d{3}` |
76
+ | `between(n, m)` | `digit.between(1, 3)` | `\d{1,3}` |
77
+ | `optional` | `digit.optional` | `\d?` |
78
+ | `zero_or_more` | `digit.zero_or_more` | `\d*` |
79
+ | `starts_with(text?)` | `starts_with('Hello')` | `^Hello` |
80
+ | `ends_with(text?)` | `ends_with('!')` | `!$` |
81
+ | `ignore_case` | — | case-insensitive flag |
82
+ | `multiline` | — | multiline flag |
83
+ | `exclude.digits` | — | `\D+` (negated class) |
84
+ | `excluding('_')` | `words.excluding('_')` | `[^\W_]+` |
85
+ | `capture(builder)` | `capture(regex.words)` | `(\w+)` |
86
+
87
+ ### Execution — terminal methods
88
+
89
+ | Method | Returns |
90
+ |---|---|
91
+ | `test(text)` | `bool` |
92
+ | `search(text)` | `re.Match \| None` |
93
+ | `match(text)` | `re.Match \| None` |
94
+ | `find_all(text)` | `list[str]` |
95
+ | `replace(text, repl)` | `str` |
96
+ | `split(text)` | `list[str]` |
97
+ | `compile()` | `re.Pattern` (cached) |
98
+ | `.pattern` | raw regex string |
99
+
100
+ ## Examples
101
+
102
+ ### Email validation
103
+
104
+ ```python
105
+ email = regex.words.then('@').words.then('.').words
106
+ email.test("user@example.com") # True
107
+ email.test("bad@@address") # False
108
+ email.pattern # '\w+@\w+\.\w+'
109
+ ```
110
+
111
+ ### Phone number
112
+
113
+ ```python
114
+ phone = (
115
+ regex
116
+ .digit.exactly(3).then('-')
117
+ .digit.exactly(3).then('-')
118
+ .digit.exactly(4)
119
+ )
120
+ phone.test("123-456-7890") # True
121
+ phone.pattern # '\d{3}\-\d{3}\-\d{4}'
122
+ ```
123
+
124
+ ### IP address
125
+
126
+ ```python
127
+ ip = (
128
+ regex
129
+ .digit.between(1, 3).then('.')
130
+ .digit.between(1, 3).then('.')
131
+ .digit.between(1, 3).then('.')
132
+ .digit.between(1, 3)
133
+ )
134
+ ip.test("192.168.1.1") # True
135
+ ```
136
+
137
+ ### Capturing groups
138
+
139
+ ```python
140
+ kv = regex.capture(regex.words).then('=').capture(regex.any_chars)
141
+ m = kv.search("color=blue")
142
+ m.group(1) # 'color'
143
+ m.group(2) # 'blue'
144
+ ```
145
+
146
+ ### Case-insensitive matching
147
+
148
+ ```python
149
+ greeting = regex.starts_with('hello').ignore_case
150
+ greeting.test("HELLO world") # True
151
+ greeting.test("hey there") # False
152
+ ```
153
+
154
+ ### Search and replace
155
+
156
+ ```python
157
+ regex.digits.replace("My SSN is 123-45-6789", "***")
158
+ # 'My SSN is ***-***-***'
159
+ ```
160
+
161
+ ### Splitting text
162
+
163
+ ```python
164
+ regex.then(',').whitespace.zero_or_more.split("a, b,c, d")
165
+ # ['a', 'b', 'c', 'd']
166
+ ```
167
+
168
+ ### Negated classes
169
+
170
+ ```python
171
+ regex.exclude.digits.find_all("a1b2c3") # ['a', 'b', 'c']
172
+ regex.words.excluding('_').pattern # '[^\W_]+'
173
+ ```
174
+
175
+ ### Immutable builder — safe reuse
176
+
177
+ ```python
178
+ base = regex.starts_with('LOG-')
179
+ errors = base.then('ERROR').any_chars
180
+ warns = base.then('WARN').any_chars
181
+
182
+ errors.test("LOG-ERROR disk full") # True
183
+ warns.test("LOG-WARN low memory") # True
184
+ base.pattern # '^LOG\-' (unchanged)
185
+ ```
186
+
187
+ ## Requirements
188
+
189
+ - Python 3.10+
190
+ - Zero runtime dependencies
191
+
192
+ ## License
193
+
194
+ MIT
@@ -0,0 +1,167 @@
1
+ # readable-regex
2
+
3
+ A fluent, chainable Python API for building regular expressions that read like English.
4
+
5
+ **[Documentation](https://molestreettechllc-dev.github.io/readable-regex/)**
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install readable-regex
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ from readable_regex import regex
17
+
18
+ # Email pattern
19
+ regex.words.then('@').words.then('.').words.test("user@example.com") # True
20
+
21
+ # Phone number
22
+ regex.digit.exactly(3).then('-').digit.exactly(3).then('-').digit.exactly(4).test("123-456-7890") # True
23
+
24
+ # Extract all numbers
25
+ regex.digits.find_all("Order #42 has 3 items totaling $129") # ['42', '3', '129']
26
+ ```
27
+
28
+ ## Vocabulary
29
+
30
+ The API uses a **plural convention**: singular = exactly one, plural = one or more.
31
+
32
+ ### Items — what you match
33
+
34
+ | Singular | Plural (1+) | Regex |
35
+ |---|---|---|
36
+ | `digit` | `digits` | `\d` / `\d+` |
37
+ | `word` | `words` | `\w` / `\w+` |
38
+ | `letter` | `letters` | `[a-zA-Z]` / `[a-zA-Z]+` |
39
+ | `whitespace` | `whitespaces` | `\s` / `\s+` |
40
+ | `any_char` | `any_chars` | `.` / `.+` |
41
+ | `then('text')` | — | escaped literal |
42
+ | `any_of('a', 'b')` | — | `[ab]` or `(?:foo\|bar)` |
43
+
44
+ ### Modifiers — how you constrain
45
+
46
+ | Modifier | Example | Effect |
47
+ |---|---|---|
48
+ | `exactly(n)` | `digit.exactly(3)` | `\d{3}` |
49
+ | `between(n, m)` | `digit.between(1, 3)` | `\d{1,3}` |
50
+ | `optional` | `digit.optional` | `\d?` |
51
+ | `zero_or_more` | `digit.zero_or_more` | `\d*` |
52
+ | `starts_with(text?)` | `starts_with('Hello')` | `^Hello` |
53
+ | `ends_with(text?)` | `ends_with('!')` | `!$` |
54
+ | `ignore_case` | — | case-insensitive flag |
55
+ | `multiline` | — | multiline flag |
56
+ | `exclude.digits` | — | `\D+` (negated class) |
57
+ | `excluding('_')` | `words.excluding('_')` | `[^\W_]+` |
58
+ | `capture(builder)` | `capture(regex.words)` | `(\w+)` |
59
+
60
+ ### Execution — terminal methods
61
+
62
+ | Method | Returns |
63
+ |---|---|
64
+ | `test(text)` | `bool` |
65
+ | `search(text)` | `re.Match \| None` |
66
+ | `match(text)` | `re.Match \| None` |
67
+ | `find_all(text)` | `list[str]` |
68
+ | `replace(text, repl)` | `str` |
69
+ | `split(text)` | `list[str]` |
70
+ | `compile()` | `re.Pattern` (cached) |
71
+ | `.pattern` | raw regex string |
72
+
73
+ ## Examples
74
+
75
+ ### Email validation
76
+
77
+ ```python
78
+ email = regex.words.then('@').words.then('.').words
79
+ email.test("user@example.com") # True
80
+ email.test("bad@@address") # False
81
+ email.pattern # '\w+@\w+\.\w+'
82
+ ```
83
+
84
+ ### Phone number
85
+
86
+ ```python
87
+ phone = (
88
+ regex
89
+ .digit.exactly(3).then('-')
90
+ .digit.exactly(3).then('-')
91
+ .digit.exactly(4)
92
+ )
93
+ phone.test("123-456-7890") # True
94
+ phone.pattern # '\d{3}\-\d{3}\-\d{4}'
95
+ ```
96
+
97
+ ### IP address
98
+
99
+ ```python
100
+ ip = (
101
+ regex
102
+ .digit.between(1, 3).then('.')
103
+ .digit.between(1, 3).then('.')
104
+ .digit.between(1, 3).then('.')
105
+ .digit.between(1, 3)
106
+ )
107
+ ip.test("192.168.1.1") # True
108
+ ```
109
+
110
+ ### Capturing groups
111
+
112
+ ```python
113
+ kv = regex.capture(regex.words).then('=').capture(regex.any_chars)
114
+ m = kv.search("color=blue")
115
+ m.group(1) # 'color'
116
+ m.group(2) # 'blue'
117
+ ```
118
+
119
+ ### Case-insensitive matching
120
+
121
+ ```python
122
+ greeting = regex.starts_with('hello').ignore_case
123
+ greeting.test("HELLO world") # True
124
+ greeting.test("hey there") # False
125
+ ```
126
+
127
+ ### Search and replace
128
+
129
+ ```python
130
+ regex.digits.replace("My SSN is 123-45-6789", "***")
131
+ # 'My SSN is ***-***-***'
132
+ ```
133
+
134
+ ### Splitting text
135
+
136
+ ```python
137
+ regex.then(',').whitespace.zero_or_more.split("a, b,c, d")
138
+ # ['a', 'b', 'c', 'd']
139
+ ```
140
+
141
+ ### Negated classes
142
+
143
+ ```python
144
+ regex.exclude.digits.find_all("a1b2c3") # ['a', 'b', 'c']
145
+ regex.words.excluding('_').pattern # '[^\W_]+'
146
+ ```
147
+
148
+ ### Immutable builder — safe reuse
149
+
150
+ ```python
151
+ base = regex.starts_with('LOG-')
152
+ errors = base.then('ERROR').any_chars
153
+ warns = base.then('WARN').any_chars
154
+
155
+ errors.test("LOG-ERROR disk full") # True
156
+ warns.test("LOG-WARN low memory") # True
157
+ base.pattern # '^LOG\-' (unchanged)
158
+ ```
159
+
160
+ ## Requirements
161
+
162
+ - Python 3.10+
163
+ - Zero runtime dependencies
164
+
165
+ ## License
166
+
167
+ MIT