valid8r 0.2.7__py3-none-any.whl → 0.3.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.

Potentially problematic release.


This version of valid8r might be problematic. Click here for more details.

valid8r/core/parsers.py CHANGED
@@ -1106,6 +1106,42 @@ def parse_url(
1106
1106
  - Unsupported URL scheme
1107
1107
  - URL requires host
1108
1108
  - Invalid host
1109
+
1110
+ Args:
1111
+ text: The URL string to parse
1112
+ allowed_schemes: Iterable of allowed scheme names (default: ('http', 'https'))
1113
+ require_host: Whether to require a host in the URL (default: True)
1114
+
1115
+ Returns:
1116
+ Maybe[UrlParts]: Success with UrlParts containing parsed components, or Failure with error message
1117
+
1118
+ Examples:
1119
+ >>> from valid8r.core.parsers import parse_url
1120
+ >>> from valid8r.core.maybe import Success
1121
+ >>>
1122
+ >>> # Parse a complete URL
1123
+ >>> result = parse_url('https://user:pass@api.example.com:8080/v1/users?active=true#section')
1124
+ >>> isinstance(result, Success)
1125
+ True
1126
+ >>> url = result.value
1127
+ >>> url.scheme
1128
+ 'https'
1129
+ >>> url.host
1130
+ 'api.example.com'
1131
+ >>> url.port
1132
+ 8080
1133
+ >>> url.path
1134
+ '/v1/users'
1135
+ >>> url.query
1136
+ 'active=true'
1137
+ >>> url.fragment
1138
+ 'section'
1139
+ >>>
1140
+ >>> # Access credentials
1141
+ >>> url.username
1142
+ 'user'
1143
+ >>> url.password
1144
+ 'pass'
1109
1145
  """
1110
1146
  if not isinstance(text, str):
1111
1147
  return Maybe.failure('Input must be a string')
@@ -1189,6 +1225,22 @@ def parse_email(text: str) -> Maybe[EmailAddress]:
1189
1225
 
1190
1226
  Returns:
1191
1227
  Maybe[EmailAddress]: Success with EmailAddress or Failure with error message
1228
+
1229
+ Examples:
1230
+ >>> from valid8r.core.parsers import parse_email
1231
+ >>> from valid8r.core.maybe import Success
1232
+ >>>
1233
+ >>> # Parse an email with case normalization
1234
+ >>> result = parse_email('User.Name+tag@Example.COM')
1235
+ >>> isinstance(result, Success)
1236
+ True
1237
+ >>> email = result.value
1238
+ >>> # Local part preserves original case
1239
+ >>> email.local
1240
+ 'User.Name+tag'
1241
+ >>> # Domain is normalized to lowercase
1242
+ >>> email.domain
1243
+ 'example.com'
1192
1244
  """
1193
1245
  if not isinstance(text, str):
1194
1246
  return Maybe.failure('Input must be a string')
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.4
2
+ Name: valid8r
3
+ Version: 0.3.0
4
+ Summary: Clean, flexible input validation for Python applications
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: validation,input,cli,maybe-monad,parsing,functional-programming
8
+ Author: Mike Lane
9
+ Author-email: mikelane@gmail.com
10
+ Requires-Python: >=3.11,<4.0
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Software Development :: Quality Assurance
22
+ Classifier: Typing :: Typed
23
+ Requires-Dist: email-validator (>=2.3.0,<3.0.0)
24
+ Requires-Dist: pydantic (>=2.0)
25
+ Requires-Dist: pydantic-core (>=2.27.0,<3.0.0)
26
+ Requires-Dist: uuid-utils (>=0.11.0,<0.12.0)
27
+ Project-URL: Documentation, https://valid8r.readthedocs.io/
28
+ Project-URL: Homepage, https://valid8r.readthedocs.io/
29
+ Project-URL: Repository, https://github.com/mikelane/valid8r
30
+ Description-Content-Type: text/markdown
31
+
32
+ # Valid8r
33
+
34
+ [![PyPI version](https://img.shields.io/pypi/v/valid8r.svg)](https://pypi.org/project/valid8r/)
35
+ [![Python versions](https://img.shields.io/pypi/pyversions/valid8r.svg)](https://pypi.org/project/valid8r/)
36
+ [![License](https://img.shields.io/github/license/mikelane/valid8r.svg)](https://github.com/mikelane/valid8r/blob/main/LICENSE)
37
+ [![CI Status](https://img.shields.io/github/actions/workflow/status/mikelane/valid8r/ci.yml?branch=main)](https://github.com/mikelane/valid8r/actions)
38
+ [![Documentation](https://img.shields.io/readthedocs/valid8r.svg)](https://valid8r.readthedocs.io/)
39
+
40
+ A clean, flexible input validation library for Python applications.
41
+
42
+ ## Features
43
+
44
+ - **Clean Type Parsing**: Parse strings to various Python types with robust error handling
45
+ - **Flexible Validation**: Chain validators and create custom validation rules
46
+ - **Monadic Error Handling**: Use Maybe monad for clean error propagation
47
+ - **Input Prompting**: Prompt users for input with built-in validation
48
+ - **Structured Results**: Network parsers return rich dataclasses with parsed components
49
+
50
+ ## Available Parsers
51
+
52
+ ### Basic Types
53
+ - **Numbers**: `parse_int`, `parse_float`, `parse_complex`, `parse_decimal`
54
+ - **Text**: `parse_bool` (flexible true/false parsing)
55
+ - **Dates**: `parse_date` (ISO 8601 format)
56
+ - **UUIDs**: `parse_uuid` (with optional version validation)
57
+
58
+ ### Collections
59
+ - **Lists**: `parse_list` (with element parser)
60
+ - **Dictionaries**: `parse_dict` (with key/value parsers)
61
+ - **Sets**: `parse_set` (with element parser)
62
+
63
+ ### Network & Communication
64
+ - **IP Addresses**: `parse_ipv4`, `parse_ipv6`, `parse_ip` (either v4 or v6)
65
+ - **Networks**: `parse_cidr` (IPv4/IPv6 CIDR notation)
66
+ - **Phone Numbers**: `parse_phone` → PhoneNumber (NANP validation)
67
+ - **URLs**: `parse_url` → UrlParts (scheme, host, port, path, query, etc.)
68
+ - **Email**: `parse_email` → EmailAddress (normalized case)
69
+
70
+ ### Advanced
71
+ - **Enums**: `parse_enum` (type-safe enum parsing)
72
+ - **Custom**: `create_parser`, `make_parser`, `validated_parser` (parser factories)
73
+
74
+ ## Installation
75
+
76
+ **Requirements**: Python 3.11 or higher
77
+
78
+ ```bash
79
+ pip install valid8r
80
+ ```
81
+
82
+ Valid8r supports Python 3.11, 3.12, 3.13, and 3.14.
83
+
84
+ ## Quick Start
85
+
86
+ ```python
87
+ from valid8r import (
88
+ parsers,
89
+ prompt,
90
+ validators,
91
+ )
92
+
93
+ # Simple validation
94
+ age = prompt.ask(
95
+ "Enter your age: ",
96
+ parser=parsers.parse_int,
97
+ validator=validators.minimum(0) & validators.maximum(120)
98
+ )
99
+
100
+ print(f"Your age is {age}")
101
+ ```
102
+
103
+ ### IP parsing helpers
104
+
105
+ ```python
106
+ from valid8r.core.maybe import Success, Failure
107
+ from valid8r.core import parsers
108
+
109
+ # IPv4 / IPv6 / generic IP
110
+ for text in ["192.168.0.1", "::1", " 10.0.0.1 "]:
111
+ match parsers.parse_ip(text):
112
+ case Success(addr):
113
+ print("Parsed:", addr)
114
+ case Failure(err):
115
+ print("Error:", err)
116
+
117
+ # CIDR (strict by default)
118
+ match parsers.parse_cidr("10.0.0.0/8"):
119
+ case Success(net):
120
+ print("Network:", net) # 10.0.0.0/8
121
+ case Failure(err):
122
+ print("Error:", err)
123
+
124
+ # Non-strict masks host bits
125
+ match parsers.parse_cidr("10.0.0.1/24", strict=False):
126
+ case Success(net):
127
+ assert str(net) == "10.0.0.0/24"
128
+ ```
129
+
130
+ ### URL and Email helpers
131
+
132
+ ```python
133
+ from valid8r.core.maybe import Success, Failure
134
+ from valid8r.core import parsers
135
+
136
+ # URL parsing with structured result (UrlParts)
137
+ match parsers.parse_url("https://alice:pw@example.com:8443/x?q=1#top"):
138
+ case Success(u):
139
+ print(f"Scheme: {u.scheme}") # https
140
+ print(f"Host: {u.host}") # example.com
141
+ print(f"Port: {u.port}") # 8443
142
+ print(f"Path: {u.path}") # /x
143
+ print(f"Query: {u.query}") # {'q': '1'}
144
+ print(f"Fragment: {u.fragment}") # top
145
+ case Failure(err):
146
+ print("Error:", err)
147
+
148
+ # Email parsing with normalized case (EmailAddress)
149
+ match parsers.parse_email("First.Last+tag@Example.COM"):
150
+ case Success(e):
151
+ print(f"Local: {e.local}") # First.Last+tag
152
+ print(f"Domain: {e.domain}") # example.com (normalized)
153
+ case Failure(err):
154
+ print("Error:", err)
155
+ ```
156
+
157
+ ### Phone Number Parsing
158
+
159
+ ```python
160
+ from valid8r.core.maybe import Success, Failure
161
+ from valid8r.core import parsers
162
+
163
+ # Phone number parsing with NANP validation (PhoneNumber)
164
+ match parsers.parse_phone("+1 (555) 123-4567"):
165
+ case Success(phone):
166
+ print(f"Country: {phone.country_code}") # 1
167
+ print(f"Area: {phone.area_code}") # 555
168
+ print(f"Exchange: {phone.exchange}") # 123
169
+ print(f"Subscriber: {phone.subscriber}") # 4567
170
+
171
+ # Format for display using properties
172
+ print(f"E.164: {phone.e164}") # +15551234567
173
+ print(f"National: {phone.national}") # (555) 123-4567
174
+ case Failure(err):
175
+ print("Error:", err)
176
+
177
+ # Also accepts various formats
178
+ for number in ["5551234567", "(555) 123-4567", "555-123-4567"]:
179
+ result = parsers.parse_phone(number)
180
+ assert result.is_success()
181
+ ```
182
+
183
+ ## Testing Support
184
+
185
+ Valid8r includes comprehensive testing utilities to help you verify your validation logic:
186
+
187
+ ```python
188
+ from valid8r import Maybe, validators, parsers, prompt
189
+ from valid8r.testing import (
190
+ MockInputContext,
191
+ assert_maybe_success,
192
+ assert_maybe_failure,
193
+ )
194
+
195
+ def validate_age(age: int) -> Maybe[int]:
196
+ """Validate age is between 0 and 120."""
197
+ return (validators.minimum(0) & validators.maximum(120))(age)
198
+
199
+ # Test validation functions with assert helpers
200
+ result = validate_age(42)
201
+ assert assert_maybe_success(result, 42)
202
+
203
+ result = validate_age(-5)
204
+ assert assert_maybe_failure(result, "at least 0")
205
+
206
+ # Test prompts with mock input
207
+ with MockInputContext(["yes", "42", "invalid", "25"]):
208
+ # First prompt
209
+ result = prompt.ask("Continue? ", parser=parsers.parse_bool)
210
+ assert result.value_or(False) == True
211
+
212
+ # Second prompt
213
+ age = prompt.ask(
214
+ "Age? ",
215
+ parser=parsers.parse_int,
216
+ validator=validate_age
217
+ )
218
+ assert age == 42
219
+
220
+ # Third prompt will fail, fourth succeeds
221
+ age = prompt.ask(
222
+ "Age again? ",
223
+ parser=parsers.parse_int,
224
+ retries=1 # Retry once after failure
225
+ )
226
+ assert age == 25
227
+ ```
228
+
229
+ ### Testing Utilities Reference
230
+
231
+ - **`assert_maybe_success(result, expected_value)`**: Assert that a Maybe is Success with the expected value
232
+ - **`assert_maybe_failure(result, error_substring)`**: Assert that a Maybe is Failure containing the error substring
233
+ - **`MockInputContext(inputs)`**: Context manager for mocking user input in tests
234
+
235
+ For more examples, see the [documentation](https://valid8r.readthedocs.io/).
236
+
237
+ ## Development
238
+
239
+ This project uses Poetry for dependency management and Tox for testing.
240
+
241
+ ### Setup
242
+
243
+ ```bash
244
+ # Install Poetry
245
+ curl -sSL https://install.python-poetry.org | python3 -
246
+
247
+ # Install dependencies
248
+ poetry install
249
+ ```
250
+
251
+ ### Running Tests
252
+
253
+ ```bash
254
+ # Run all tests
255
+ poetry run tox
256
+
257
+ # Run BDD tests
258
+ poetry run tox -e bdd
259
+ ```
260
+
261
+ ## License
262
+
263
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
264
+
265
+ Copyright (c) 2025 Mike Lane
266
+
@@ -2,7 +2,7 @@ valid8r/__init__.py,sha256=Yg46vPVbLZvty-FSXkiJ3IkB6hg20pYcp6nWFidIfkk,447
2
2
  valid8r/core/__init__.py,sha256=ASOdzqCtpZHbHjjYMZkb78Z-nKxtD26ruTY0bd43ImA,520
3
3
  valid8r/core/combinators.py,sha256=KvRiDEqoZgH58cBYPO6SW9pdtkyijk0lS8aGSB5DbO4,2349
4
4
  valid8r/core/maybe.py,sha256=xT1xbiLVKohZ2aeaDZoKjT0W6Vk_PPwKbZXpIfsP7hc,4359
5
- valid8r/core/parsers.py,sha256=XlP9u0obe060wOJ29S5czCAQaJLObI4XxMax84R2m4k,43732
5
+ valid8r/core/parsers.py,sha256=ur2wS5_-YHAYaJVwi1SJv6P0TRHp6W799VVqrwV45rg,45319
6
6
  valid8r/core/validators.py,sha256=oCrRQ2wIPNkqQXy-hJ7sQ9mJAvxtEtGhoy7WvehWqTc,5756
7
7
  valid8r/prompt/__init__.py,sha256=XYB3NEp-tmqT6fGmETVEeXd7Urj0M4ijlwdRAjj-rG8,175
8
8
  valid8r/prompt/basic.py,sha256=fLWuN-oiVZyaLdbcW5GHWpoGQ82RG0j-1n7uMYDfOb8,6008
@@ -11,7 +11,8 @@ valid8r/testing/__init__.py,sha256=8mk54zt0Ai2dK0a3GMOTfDPsVQWXaS6uvQJDrkRV9hs,7
11
11
  valid8r/testing/assertions.py,sha256=9KGz1JooCoyikyxMX7VuXB9VYAtj-4H_LPYFGdvS-ps,1820
12
12
  valid8r/testing/generators.py,sha256=kAV6NRO9x1gPy0BfGs07ETVxjpTIxOZyV9wH2BA1nHA,8791
13
13
  valid8r/testing/mock_input.py,sha256=9GRT7h0PCh9Dea-OcQ5Uls7YqhsTdqMWuX6I6ZlW1aw,2334
14
- valid8r-0.2.7.dist-info/METADATA,sha256=2HYM89KzfHKowmAWx9w7zVaaF-q7fJVdOnYRzFMADtQ,4015
15
- valid8r-0.2.7.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
16
- valid8r-0.2.7.dist-info/entry_points.txt,sha256=H_24A4zUgnKIXAIRosJliIcntyqMfmcgKh5_Prl7W18,79
17
- valid8r-0.2.7.dist-info/RECORD,,
14
+ valid8r-0.3.0.dist-info/METADATA,sha256=kG-JA1RazBz128ngLs7kgQUYtZ1NcAP519Y4as1Oq0M,8326
15
+ valid8r-0.3.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
16
+ valid8r-0.3.0.dist-info/entry_points.txt,sha256=H_24A4zUgnKIXAIRosJliIcntyqMfmcgKh5_Prl7W18,79
17
+ valid8r-0.3.0.dist-info/licenses/LICENSE,sha256=JpEmJvRYOTIUt0UjgvpDrd3U94Wnbt_Grr5z-xU2jtk,1066
18
+ valid8r-0.3.0.dist-info/RECORD,,
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mike Lane
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.
@@ -1,168 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: valid8r
3
- Version: 0.2.7
4
- Summary: Clean, flexible input validation for Python applications
5
- License: MIT
6
- Keywords: validation,input,cli,maybe-monad
7
- Author: Mike Lane
8
- Author-email: mikelane@gmail.com
9
- Requires-Python: >=3.11,<4.0
10
- Classifier: Development Status :: 3 - Alpha
11
- Classifier: Intended Audience :: Developers
12
- Classifier: License :: OSI Approved :: MIT License
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
- Classifier: Programming Language :: Python :: 3.14
18
- Requires-Dist: email-validator (>=2.3.0,<3.0.0)
19
- Requires-Dist: pydantic (>=2.0)
20
- Requires-Dist: pydantic-core (>=2.27.0,<3.0.0)
21
- Requires-Dist: uuid-utils (>=0.11.0,<0.12.0)
22
- Project-URL: Repository, https://github.com/mikelane/valid8r
23
- Description-Content-Type: text/markdown
24
-
25
- # Valid8r
26
-
27
- A clean, flexible input validation library for Python applications.
28
-
29
- ## Features
30
-
31
- - **Clean Type Parsing**: Parse strings to various Python types with robust error handling
32
- - **Flexible Validation**: Chain validators and create custom validation rules
33
- - **Monadic Error Handling**: Use Maybe monad for clean error propagation
34
- - **Input Prompting**: Prompt users for input with built-in validation
35
-
36
- ## Installation
37
-
38
- ```bash
39
- pip install valid8r
40
- ```
41
-
42
- ## Quick Start
43
-
44
- ```python
45
- from valid8r import (
46
- parsers,
47
- prompt,
48
- validators,
49
- )
50
-
51
- # Simple validation
52
- age = prompt.ask(
53
- "Enter your age: ",
54
- parser=parsers.parse_int,
55
- validator=validators.minimum(0) & validators.maximum(120)
56
- )
57
-
58
- print(f"Your age is {age}")
59
- ```
60
-
61
- ### IP parsing helpers
62
-
63
- ```python
64
- from valid8r.core.maybe import Success, Failure
65
- from valid8r.core import parsers
66
-
67
- # IPv4 / IPv6 / generic IP
68
- for text in ["192.168.0.1", "::1", " 10.0.0.1 "]:
69
- match parsers.parse_ip(text):
70
- case Success(addr):
71
- print("Parsed:", addr)
72
- case Failure(err):
73
- print("Error:", err)
74
-
75
- # CIDR (strict by default)
76
- match parsers.parse_cidr("10.0.0.0/8"):
77
- case Success(net):
78
- print("Network:", net) # 10.0.0.0/8
79
- case Failure(err):
80
- print("Error:", err)
81
-
82
- # Non-strict masks host bits
83
- match parsers.parse_cidr("10.0.0.1/24", strict=False):
84
- case Success(net):
85
- assert str(net) == "10.0.0.0/24"
86
- ```
87
-
88
- ### URL and Email helpers
89
-
90
- ```python
91
- from valid8r.core.maybe import Success, Failure
92
- from valid8r.core import parsers
93
-
94
- # URL parsing
95
- match parsers.parse_url("https://alice:pw@example.com:8443/x?q=1#top"):
96
- case Success(u):
97
- print(u.scheme, u.username, u.password, u.host, u.port)
98
- case Failure(err):
99
- print("Error:", err)
100
-
101
- # Email parsing
102
- match parsers.parse_email("First.Last+tag@Example.COM"):
103
- case Success(e):
104
- print(e.local, e.domain) # First.Last+tag example.com
105
- case Failure(err):
106
- print("Error:", err)
107
- ```
108
-
109
- ## Testing Support
110
-
111
- Valid8r includes testing utilities to help you verify your validation logic:
112
-
113
- ```python
114
- from valid8r import (
115
- Maybe,
116
- validators,
117
- parsers,
118
- prompt,
119
- )
120
-
121
- from valid8r.testing import (
122
- MockInputContext,
123
- assert_maybe_success,
124
- )
125
-
126
- def validate_age(age: int) -> Maybe[int]:
127
- return validators.minimum(0) & validators.maximum(120)(age)
128
-
129
- # Test prompts with mock input
130
- with MockInputContext(["yes"]):
131
- result = prompt.ask("Continue? ", parser=parsers.parse_bool)
132
- assert result.is_success()
133
- assert result.value_or(False) == True
134
-
135
- # Test validation functions
136
- result = validate_age(42)
137
- assert assert_maybe_success(result, 42)
138
- ```
139
-
140
- For more information, see the [Testing with Valid8r](docs/user_guide/testing.rst) guide.
141
-
142
- ## Development
143
-
144
- This project uses Poetry for dependency management and Tox for testing.
145
-
146
- ### Setup
147
-
148
- ```bash
149
- # Install Poetry
150
- curl -sSL https://install.python-poetry.org | python3 -
151
-
152
- # Install dependencies
153
- poetry install
154
- ```
155
-
156
- ### Running Tests
157
-
158
- ```bash
159
- # Run all tests
160
- poetry run tox
161
-
162
- # Run BDD tests
163
- poetry run tox -e bdd
164
- ```
165
-
166
- ## License
167
- MIT
168
-