valid8r 0.2.6__py3-none-any.whl → 0.2.8__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,246 @@
1
+ Metadata-Version: 2.4
2
+ Name: valid8r
3
+ Version: 0.2.8
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
+ - **Structured Results**: Network parsers return rich dataclasses with parsed components
36
+
37
+ ## Available Parsers
38
+
39
+ ### Basic Types
40
+ - **Numbers**: `parse_int`, `parse_float`, `parse_complex`, `parse_decimal`
41
+ - **Text**: `parse_bool` (flexible true/false parsing)
42
+ - **Dates**: `parse_date` (ISO 8601 format)
43
+ - **UUIDs**: `parse_uuid` (with optional version validation)
44
+
45
+ ### Collections
46
+ - **Lists**: `parse_list` (with element parser)
47
+ - **Dictionaries**: `parse_dict` (with key/value parsers)
48
+ - **Sets**: `parse_set` (with element parser)
49
+
50
+ ### Network & Communication
51
+ - **IP Addresses**: `parse_ipv4`, `parse_ipv6`, `parse_ip` (either v4 or v6)
52
+ - **Networks**: `parse_cidr` (IPv4/IPv6 CIDR notation)
53
+ - **Phone Numbers**: `parse_phone` → PhoneNumber (NANP validation)
54
+ - **URLs**: `parse_url` → UrlParts (scheme, host, port, path, query, etc.)
55
+ - **Email**: `parse_email` → EmailAddress (normalized case)
56
+
57
+ ### Advanced
58
+ - **Enums**: `parse_enum` (type-safe enum parsing)
59
+ - **Custom**: `create_parser`, `make_parser`, `validated_parser` (parser factories)
60
+
61
+ ## Installation
62
+
63
+ ```bash
64
+ pip install valid8r
65
+ ```
66
+
67
+ ## Quick Start
68
+
69
+ ```python
70
+ from valid8r import (
71
+ parsers,
72
+ prompt,
73
+ validators,
74
+ )
75
+
76
+ # Simple validation
77
+ age = prompt.ask(
78
+ "Enter your age: ",
79
+ parser=parsers.parse_int,
80
+ validator=validators.minimum(0) & validators.maximum(120)
81
+ )
82
+
83
+ print(f"Your age is {age}")
84
+ ```
85
+
86
+ ### IP parsing helpers
87
+
88
+ ```python
89
+ from valid8r.core.maybe import Success, Failure
90
+ from valid8r.core import parsers
91
+
92
+ # IPv4 / IPv6 / generic IP
93
+ for text in ["192.168.0.1", "::1", " 10.0.0.1 "]:
94
+ match parsers.parse_ip(text):
95
+ case Success(addr):
96
+ print("Parsed:", addr)
97
+ case Failure(err):
98
+ print("Error:", err)
99
+
100
+ # CIDR (strict by default)
101
+ match parsers.parse_cidr("10.0.0.0/8"):
102
+ case Success(net):
103
+ print("Network:", net) # 10.0.0.0/8
104
+ case Failure(err):
105
+ print("Error:", err)
106
+
107
+ # Non-strict masks host bits
108
+ match parsers.parse_cidr("10.0.0.1/24", strict=False):
109
+ case Success(net):
110
+ assert str(net) == "10.0.0.0/24"
111
+ ```
112
+
113
+ ### URL and Email helpers
114
+
115
+ ```python
116
+ from valid8r.core.maybe import Success, Failure
117
+ from valid8r.core import parsers
118
+
119
+ # URL parsing with structured result (UrlParts)
120
+ match parsers.parse_url("https://alice:pw@example.com:8443/x?q=1#top"):
121
+ case Success(u):
122
+ print(f"Scheme: {u.scheme}") # https
123
+ print(f"Host: {u.host}") # example.com
124
+ print(f"Port: {u.port}") # 8443
125
+ print(f"Path: {u.path}") # /x
126
+ print(f"Query: {u.query}") # {'q': '1'}
127
+ print(f"Fragment: {u.fragment}") # top
128
+ case Failure(err):
129
+ print("Error:", err)
130
+
131
+ # Email parsing with normalized case (EmailAddress)
132
+ match parsers.parse_email("First.Last+tag@Example.COM"):
133
+ case Success(e):
134
+ print(f"Local: {e.local}") # First.Last+tag
135
+ print(f"Domain: {e.domain}") # example.com (normalized)
136
+ case Failure(err):
137
+ print("Error:", err)
138
+ ```
139
+
140
+ ### Phone Number Parsing
141
+
142
+ ```python
143
+ from valid8r.core.maybe import Success, Failure
144
+ from valid8r.core import parsers
145
+
146
+ # Phone number parsing with NANP validation (PhoneNumber)
147
+ match parsers.parse_phone("+1 (555) 123-4567"):
148
+ case Success(phone):
149
+ print(f"Country: {phone.country_code}") # 1
150
+ print(f"Area: {phone.area_code}") # 555
151
+ print(f"Exchange: {phone.exchange}") # 123
152
+ print(f"Subscriber: {phone.subscriber}") # 4567
153
+
154
+ # Format for display using properties
155
+ print(f"E.164: {phone.e164}") # +15551234567
156
+ print(f"National: {phone.national}") # (555) 123-4567
157
+ case Failure(err):
158
+ print("Error:", err)
159
+
160
+ # Also accepts various formats
161
+ for number in ["5551234567", "(555) 123-4567", "555-123-4567"]:
162
+ result = parsers.parse_phone(number)
163
+ assert result.is_success()
164
+ ```
165
+
166
+ ## Testing Support
167
+
168
+ Valid8r includes comprehensive testing utilities to help you verify your validation logic:
169
+
170
+ ```python
171
+ from valid8r import Maybe, validators, parsers, prompt
172
+ from valid8r.testing import (
173
+ MockInputContext,
174
+ assert_maybe_success,
175
+ assert_maybe_failure,
176
+ )
177
+
178
+ def validate_age(age: int) -> Maybe[int]:
179
+ """Validate age is between 0 and 120."""
180
+ return (validators.minimum(0) & validators.maximum(120))(age)
181
+
182
+ # Test validation functions with assert helpers
183
+ result = validate_age(42)
184
+ assert assert_maybe_success(result, 42)
185
+
186
+ result = validate_age(-5)
187
+ assert assert_maybe_failure(result, "at least 0")
188
+
189
+ # Test prompts with mock input
190
+ with MockInputContext(["yes", "42", "invalid", "25"]):
191
+ # First prompt
192
+ result = prompt.ask("Continue? ", parser=parsers.parse_bool)
193
+ assert result.value_or(False) == True
194
+
195
+ # Second prompt
196
+ age = prompt.ask(
197
+ "Age? ",
198
+ parser=parsers.parse_int,
199
+ validator=validate_age
200
+ )
201
+ assert age == 42
202
+
203
+ # Third prompt will fail, fourth succeeds
204
+ age = prompt.ask(
205
+ "Age again? ",
206
+ parser=parsers.parse_int,
207
+ retries=1 # Retry once after failure
208
+ )
209
+ assert age == 25
210
+ ```
211
+
212
+ ### Testing Utilities Reference
213
+
214
+ - **`assert_maybe_success(result, expected_value)`**: Assert that a Maybe is Success with the expected value
215
+ - **`assert_maybe_failure(result, error_substring)`**: Assert that a Maybe is Failure containing the error substring
216
+ - **`MockInputContext(inputs)`**: Context manager for mocking user input in tests
217
+
218
+ For more examples, see the [documentation](https://valid8r.readthedocs.io/).
219
+
220
+ ## Development
221
+
222
+ This project uses Poetry for dependency management and Tox for testing.
223
+
224
+ ### Setup
225
+
226
+ ```bash
227
+ # Install Poetry
228
+ curl -sSL https://install.python-poetry.org | python3 -
229
+
230
+ # Install dependencies
231
+ poetry install
232
+ ```
233
+
234
+ ### Running Tests
235
+
236
+ ```bash
237
+ # Run all tests
238
+ poetry run tox
239
+
240
+ # Run BDD tests
241
+ poetry run tox -e bdd
242
+ ```
243
+
244
+ ## License
245
+ MIT
246
+
@@ -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,7 @@ 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.6.dist-info/METADATA,sha256=nBaDsuI3amt2zftTARhWM55pqCsDRPRhF1sabab1sUA,4015
15
- valid8r-0.2.6.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
16
- valid8r-0.2.6.dist-info/entry_points.txt,sha256=H_24A4zUgnKIXAIRosJliIcntyqMfmcgKh5_Prl7W18,79
17
- valid8r-0.2.6.dist-info/RECORD,,
14
+ valid8r-0.2.8.dist-info/METADATA,sha256=W8dh72aZE4kVsq7xPM6yQoJlVhvWdgQS1gDCUbIS_VQ,7148
15
+ valid8r-0.2.8.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
16
+ valid8r-0.2.8.dist-info/entry_points.txt,sha256=H_24A4zUgnKIXAIRosJliIcntyqMfmcgKh5_Prl7W18,79
17
+ valid8r-0.2.8.dist-info/RECORD,,
@@ -1,168 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: valid8r
3
- Version: 0.2.6
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
-