valid8r 0.2.7__tar.gz → 0.3.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.

Potentially problematic release.


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

valid8r-0.3.0/LICENSE ADDED
@@ -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.
valid8r-0.3.0/PKG-INFO ADDED
@@ -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
+
@@ -0,0 +1,234 @@
1
+ # Valid8r
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/valid8r.svg)](https://pypi.org/project/valid8r/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/valid8r.svg)](https://pypi.org/project/valid8r/)
5
+ [![License](https://img.shields.io/github/license/mikelane/valid8r.svg)](https://github.com/mikelane/valid8r/blob/main/LICENSE)
6
+ [![CI Status](https://img.shields.io/github/actions/workflow/status/mikelane/valid8r/ci.yml?branch=main)](https://github.com/mikelane/valid8r/actions)
7
+ [![Documentation](https://img.shields.io/readthedocs/valid8r.svg)](https://valid8r.readthedocs.io/)
8
+
9
+ A clean, flexible input validation library for Python applications.
10
+
11
+ ## Features
12
+
13
+ - **Clean Type Parsing**: Parse strings to various Python types with robust error handling
14
+ - **Flexible Validation**: Chain validators and create custom validation rules
15
+ - **Monadic Error Handling**: Use Maybe monad for clean error propagation
16
+ - **Input Prompting**: Prompt users for input with built-in validation
17
+ - **Structured Results**: Network parsers return rich dataclasses with parsed components
18
+
19
+ ## Available Parsers
20
+
21
+ ### Basic Types
22
+ - **Numbers**: `parse_int`, `parse_float`, `parse_complex`, `parse_decimal`
23
+ - **Text**: `parse_bool` (flexible true/false parsing)
24
+ - **Dates**: `parse_date` (ISO 8601 format)
25
+ - **UUIDs**: `parse_uuid` (with optional version validation)
26
+
27
+ ### Collections
28
+ - **Lists**: `parse_list` (with element parser)
29
+ - **Dictionaries**: `parse_dict` (with key/value parsers)
30
+ - **Sets**: `parse_set` (with element parser)
31
+
32
+ ### Network & Communication
33
+ - **IP Addresses**: `parse_ipv4`, `parse_ipv6`, `parse_ip` (either v4 or v6)
34
+ - **Networks**: `parse_cidr` (IPv4/IPv6 CIDR notation)
35
+ - **Phone Numbers**: `parse_phone` → PhoneNumber (NANP validation)
36
+ - **URLs**: `parse_url` → UrlParts (scheme, host, port, path, query, etc.)
37
+ - **Email**: `parse_email` → EmailAddress (normalized case)
38
+
39
+ ### Advanced
40
+ - **Enums**: `parse_enum` (type-safe enum parsing)
41
+ - **Custom**: `create_parser`, `make_parser`, `validated_parser` (parser factories)
42
+
43
+ ## Installation
44
+
45
+ **Requirements**: Python 3.11 or higher
46
+
47
+ ```bash
48
+ pip install valid8r
49
+ ```
50
+
51
+ Valid8r supports Python 3.11, 3.12, 3.13, and 3.14.
52
+
53
+ ## Quick Start
54
+
55
+ ```python
56
+ from valid8r import (
57
+ parsers,
58
+ prompt,
59
+ validators,
60
+ )
61
+
62
+ # Simple validation
63
+ age = prompt.ask(
64
+ "Enter your age: ",
65
+ parser=parsers.parse_int,
66
+ validator=validators.minimum(0) & validators.maximum(120)
67
+ )
68
+
69
+ print(f"Your age is {age}")
70
+ ```
71
+
72
+ ### IP parsing helpers
73
+
74
+ ```python
75
+ from valid8r.core.maybe import Success, Failure
76
+ from valid8r.core import parsers
77
+
78
+ # IPv4 / IPv6 / generic IP
79
+ for text in ["192.168.0.1", "::1", " 10.0.0.1 "]:
80
+ match parsers.parse_ip(text):
81
+ case Success(addr):
82
+ print("Parsed:", addr)
83
+ case Failure(err):
84
+ print("Error:", err)
85
+
86
+ # CIDR (strict by default)
87
+ match parsers.parse_cidr("10.0.0.0/8"):
88
+ case Success(net):
89
+ print("Network:", net) # 10.0.0.0/8
90
+ case Failure(err):
91
+ print("Error:", err)
92
+
93
+ # Non-strict masks host bits
94
+ match parsers.parse_cidr("10.0.0.1/24", strict=False):
95
+ case Success(net):
96
+ assert str(net) == "10.0.0.0/24"
97
+ ```
98
+
99
+ ### URL and Email helpers
100
+
101
+ ```python
102
+ from valid8r.core.maybe import Success, Failure
103
+ from valid8r.core import parsers
104
+
105
+ # URL parsing with structured result (UrlParts)
106
+ match parsers.parse_url("https://alice:pw@example.com:8443/x?q=1#top"):
107
+ case Success(u):
108
+ print(f"Scheme: {u.scheme}") # https
109
+ print(f"Host: {u.host}") # example.com
110
+ print(f"Port: {u.port}") # 8443
111
+ print(f"Path: {u.path}") # /x
112
+ print(f"Query: {u.query}") # {'q': '1'}
113
+ print(f"Fragment: {u.fragment}") # top
114
+ case Failure(err):
115
+ print("Error:", err)
116
+
117
+ # Email parsing with normalized case (EmailAddress)
118
+ match parsers.parse_email("First.Last+tag@Example.COM"):
119
+ case Success(e):
120
+ print(f"Local: {e.local}") # First.Last+tag
121
+ print(f"Domain: {e.domain}") # example.com (normalized)
122
+ case Failure(err):
123
+ print("Error:", err)
124
+ ```
125
+
126
+ ### Phone Number Parsing
127
+
128
+ ```python
129
+ from valid8r.core.maybe import Success, Failure
130
+ from valid8r.core import parsers
131
+
132
+ # Phone number parsing with NANP validation (PhoneNumber)
133
+ match parsers.parse_phone("+1 (555) 123-4567"):
134
+ case Success(phone):
135
+ print(f"Country: {phone.country_code}") # 1
136
+ print(f"Area: {phone.area_code}") # 555
137
+ print(f"Exchange: {phone.exchange}") # 123
138
+ print(f"Subscriber: {phone.subscriber}") # 4567
139
+
140
+ # Format for display using properties
141
+ print(f"E.164: {phone.e164}") # +15551234567
142
+ print(f"National: {phone.national}") # (555) 123-4567
143
+ case Failure(err):
144
+ print("Error:", err)
145
+
146
+ # Also accepts various formats
147
+ for number in ["5551234567", "(555) 123-4567", "555-123-4567"]:
148
+ result = parsers.parse_phone(number)
149
+ assert result.is_success()
150
+ ```
151
+
152
+ ## Testing Support
153
+
154
+ Valid8r includes comprehensive testing utilities to help you verify your validation logic:
155
+
156
+ ```python
157
+ from valid8r import Maybe, validators, parsers, prompt
158
+ from valid8r.testing import (
159
+ MockInputContext,
160
+ assert_maybe_success,
161
+ assert_maybe_failure,
162
+ )
163
+
164
+ def validate_age(age: int) -> Maybe[int]:
165
+ """Validate age is between 0 and 120."""
166
+ return (validators.minimum(0) & validators.maximum(120))(age)
167
+
168
+ # Test validation functions with assert helpers
169
+ result = validate_age(42)
170
+ assert assert_maybe_success(result, 42)
171
+
172
+ result = validate_age(-5)
173
+ assert assert_maybe_failure(result, "at least 0")
174
+
175
+ # Test prompts with mock input
176
+ with MockInputContext(["yes", "42", "invalid", "25"]):
177
+ # First prompt
178
+ result = prompt.ask("Continue? ", parser=parsers.parse_bool)
179
+ assert result.value_or(False) == True
180
+
181
+ # Second prompt
182
+ age = prompt.ask(
183
+ "Age? ",
184
+ parser=parsers.parse_int,
185
+ validator=validate_age
186
+ )
187
+ assert age == 42
188
+
189
+ # Third prompt will fail, fourth succeeds
190
+ age = prompt.ask(
191
+ "Age again? ",
192
+ parser=parsers.parse_int,
193
+ retries=1 # Retry once after failure
194
+ )
195
+ assert age == 25
196
+ ```
197
+
198
+ ### Testing Utilities Reference
199
+
200
+ - **`assert_maybe_success(result, expected_value)`**: Assert that a Maybe is Success with the expected value
201
+ - **`assert_maybe_failure(result, error_substring)`**: Assert that a Maybe is Failure containing the error substring
202
+ - **`MockInputContext(inputs)`**: Context manager for mocking user input in tests
203
+
204
+ For more examples, see the [documentation](https://valid8r.readthedocs.io/).
205
+
206
+ ## Development
207
+
208
+ This project uses Poetry for dependency management and Tox for testing.
209
+
210
+ ### Setup
211
+
212
+ ```bash
213
+ # Install Poetry
214
+ curl -sSL https://install.python-poetry.org | python3 -
215
+
216
+ # Install dependencies
217
+ poetry install
218
+ ```
219
+
220
+ ### Running Tests
221
+
222
+ ```bash
223
+ # Run all tests
224
+ poetry run tox
225
+
226
+ # Run BDD tests
227
+ poetry run tox -e bdd
228
+ ```
229
+
230
+ ## License
231
+
232
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
233
+
234
+ Copyright (c) 2025 Mike Lane
@@ -1,19 +1,27 @@
1
1
  [tool.poetry]
2
2
  name = "valid8r"
3
- version = "0.2.7"
3
+ version = "0.3.0"
4
4
  description = "Clean, flexible input validation for Python applications"
5
5
  authors = ["Mike Lane <mikelane@gmail.com>"]
6
6
  license = "MIT"
7
7
  readme = "README.md"
8
+ homepage = "https://valid8r.readthedocs.io/"
8
9
  repository = "https://github.com/mikelane/valid8r"
9
- keywords = ["validation", "input", "cli", "maybe-monad"]
10
+ documentation = "https://valid8r.readthedocs.io/"
11
+ keywords = ["validation", "input", "cli", "maybe-monad", "parsing", "functional-programming"]
10
12
  classifiers = [
11
13
  "Development Status :: 3 - Alpha",
12
14
  "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: OS Independent",
13
17
  "Programming Language :: Python :: 3",
14
18
  "Programming Language :: Python :: 3.11",
15
19
  "Programming Language :: Python :: 3.12",
16
20
  "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ "Topic :: Software Development :: Quality Assurance",
24
+ "Typing :: Typed",
17
25
  ]
18
26
  packages = [{include = "valid8r"}]
19
27
  include = [{ path = "valid8r/py.typed", format = "wheel" }]
@@ -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')
valid8r-0.2.7/PKG-INFO DELETED
@@ -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
-
valid8r-0.2.7/README.md DELETED
@@ -1,143 +0,0 @@
1
- # Valid8r
2
-
3
- A clean, flexible input validation library for Python applications.
4
-
5
- ## Features
6
-
7
- - **Clean Type Parsing**: Parse strings to various Python types with robust error handling
8
- - **Flexible Validation**: Chain validators and create custom validation rules
9
- - **Monadic Error Handling**: Use Maybe monad for clean error propagation
10
- - **Input Prompting**: Prompt users for input with built-in validation
11
-
12
- ## Installation
13
-
14
- ```bash
15
- pip install valid8r
16
- ```
17
-
18
- ## Quick Start
19
-
20
- ```python
21
- from valid8r import (
22
- parsers,
23
- prompt,
24
- validators,
25
- )
26
-
27
- # Simple validation
28
- age = prompt.ask(
29
- "Enter your age: ",
30
- parser=parsers.parse_int,
31
- validator=validators.minimum(0) & validators.maximum(120)
32
- )
33
-
34
- print(f"Your age is {age}")
35
- ```
36
-
37
- ### IP parsing helpers
38
-
39
- ```python
40
- from valid8r.core.maybe import Success, Failure
41
- from valid8r.core import parsers
42
-
43
- # IPv4 / IPv6 / generic IP
44
- for text in ["192.168.0.1", "::1", " 10.0.0.1 "]:
45
- match parsers.parse_ip(text):
46
- case Success(addr):
47
- print("Parsed:", addr)
48
- case Failure(err):
49
- print("Error:", err)
50
-
51
- # CIDR (strict by default)
52
- match parsers.parse_cidr("10.0.0.0/8"):
53
- case Success(net):
54
- print("Network:", net) # 10.0.0.0/8
55
- case Failure(err):
56
- print("Error:", err)
57
-
58
- # Non-strict masks host bits
59
- match parsers.parse_cidr("10.0.0.1/24", strict=False):
60
- case Success(net):
61
- assert str(net) == "10.0.0.0/24"
62
- ```
63
-
64
- ### URL and Email helpers
65
-
66
- ```python
67
- from valid8r.core.maybe import Success, Failure
68
- from valid8r.core import parsers
69
-
70
- # URL parsing
71
- match parsers.parse_url("https://alice:pw@example.com:8443/x?q=1#top"):
72
- case Success(u):
73
- print(u.scheme, u.username, u.password, u.host, u.port)
74
- case Failure(err):
75
- print("Error:", err)
76
-
77
- # Email parsing
78
- match parsers.parse_email("First.Last+tag@Example.COM"):
79
- case Success(e):
80
- print(e.local, e.domain) # First.Last+tag example.com
81
- case Failure(err):
82
- print("Error:", err)
83
- ```
84
-
85
- ## Testing Support
86
-
87
- Valid8r includes testing utilities to help you verify your validation logic:
88
-
89
- ```python
90
- from valid8r import (
91
- Maybe,
92
- validators,
93
- parsers,
94
- prompt,
95
- )
96
-
97
- from valid8r.testing import (
98
- MockInputContext,
99
- assert_maybe_success,
100
- )
101
-
102
- def validate_age(age: int) -> Maybe[int]:
103
- return validators.minimum(0) & validators.maximum(120)(age)
104
-
105
- # Test prompts with mock input
106
- with MockInputContext(["yes"]):
107
- result = prompt.ask("Continue? ", parser=parsers.parse_bool)
108
- assert result.is_success()
109
- assert result.value_or(False) == True
110
-
111
- # Test validation functions
112
- result = validate_age(42)
113
- assert assert_maybe_success(result, 42)
114
- ```
115
-
116
- For more information, see the [Testing with Valid8r](docs/user_guide/testing.rst) guide.
117
-
118
- ## Development
119
-
120
- This project uses Poetry for dependency management and Tox for testing.
121
-
122
- ### Setup
123
-
124
- ```bash
125
- # Install Poetry
126
- curl -sSL https://install.python-poetry.org | python3 -
127
-
128
- # Install dependencies
129
- poetry install
130
- ```
131
-
132
- ### Running Tests
133
-
134
- ```bash
135
- # Run all tests
136
- poetry run tox
137
-
138
- # Run BDD tests
139
- poetry run tox -e bdd
140
- ```
141
-
142
- ## License
143
- MIT
File without changes
File without changes
File without changes
File without changes