valid8r.core.parsers

String parsing functions with Maybe monad error handling.

Classes

UrlParts

Structured URL components.

EmailAddress

Structured email address.

PhoneNumber

Structured North American phone number (NANP).

Functions

parse_str(input_value[, error_message, strip, allow_empty])

Parse and validate input is a string type.

parse_int(input_value[, error_message])

Parse a string to an integer.

parse_float(input_value[, error_message])

Parse a string to a floating-point number.

parse_bool(input_value[, error_message])

Parse a string to a boolean value.

parse_date(input_value[, date_format, error_message])

Parse a string to a date object.

parse_datetime(input_value[, error_message])

Parse a string to a timezone-aware datetime object.

parse_timedelta(input_value[, error_message])

Parse a string to a timedelta object.

parse_complex(input_value[, error_message])

Parse a string to a complex number.

parse_decimal(input_value[, error_message])

Parse a string to a Decimal for precise decimal arithmetic.

parse_enum(input_value, enum_class[, error_message])

Parse a string to an enum member.

parse_list(input_value[, element_parser, separator, ...])

Parse a string to a list using the specified element parser and separator.

parse_dict(input_value[, key_parser, value_parser, ...])

Parse a string to a dictionary using the specified parsers and separators.

parse_set(input_value[, element_parser, separator, ...])

Parse a string to a set using the specified element parser and separator.

parse_int_with_validation(input_value[, min_value, ...])

Parse a string to an integer with range validation.

parse_list_with_validation(input_value[, ...])

Parse a string to a list with length validation.

parse_dict_with_validation(input_value[, key_parser, ...])

Parse a string to a dictionary with required keys validation.

create_parser(convert_func[, error_message])

Create a parser function from a conversion function.

make_parser(…)

Create a parser function from a conversion function with a decorator.

validated_parser(convert_func, validator[, error_message])

Create a parser with a built-in validator.

parse_uuid(text[, version, strict])

Parse a string to a UUID.

parse_ipv4(text)

Parse an IPv4 address string.

parse_ipv6(text)

Parse an IPv6 address string.

parse_ip(text)

Parse a string as either an IPv4 or IPv6 address.

parse_cidr(text, *[, strict])

Parse a CIDR network string (IPv4 or IPv6).

parse_url(text, *[, allowed_schemes, require_host])

Parse a URL with light validation.

parse_email(text)

Parse a bare email address of the form local@domain.

parse_phone(text, *[, region, strict])

Parse a North American phone number (NANP format).

parse_slug(text, *[, min_length, max_length])

Parse a URL-safe slug (lowercase letters, numbers, hyphens only).

parse_json(text)

Parse a JSON string into a Python object.

parse_base64(text)

Parse and decode a base64-encoded string.

parse_jwt(text)

Parse and validate a JWT (JSON Web Token) structure.

parse_path(text, *[, expand_user, resolve, error_message])

Parse a string into a pathlib.Path object.

Module Contents

valid8r.core.parsers.parse_str(input_value, error_message=None, *, strip=True, allow_empty=True)[source]

Parse and validate input is a string type.

Validates that the input is actually a string type (isinstance check). By default, strips leading and trailing whitespace for consistency with other parsers like parse_int and parse_float.

This function provides type validation at the parser layer, ensuring the input is a string before any content validation is applied. It complements the validator layer which handles content rules like non-empty, length constraints, or pattern matching.

Parameters:
  • input_value (object) – Value to validate (any type accepted, only str passes).

  • error_message (str | None) – Optional custom error message for type validation failures. If not provided, generates descriptive error based on actual type received.

  • strip (bool) – If True (default), strip leading and trailing whitespace from the input. This provides consistency with other parsers like parse_int. Set to False to preserve whitespace exactly as provided.

  • allow_empty (bool) – If True (default), allow empty strings to pass validation. Set to False to reject empty strings (after stripping if strip=True). For content validation, prefer chaining with non_empty_string() validator.

Returns:

Success(str) if input is a valid string, Failure(str) with

descriptive error message otherwise.

Return type:

Maybe[str]

Examples

>>> parse_str("hello")
Success('hello')
>>> parse_str("  hello  ")
Success('hello')
>>> parse_str("  hello  ", strip=False)
Success('  hello  ')
>>> parse_str("").is_success()
True
>>> parse_str("", allow_empty=False).is_failure()
True
>>> parse_str(None).is_failure()
True
>>> parse_str(42).is_failure()
True
>>> parse_str("hello").bind(lambda s: Maybe.success(s.upper()))
Success('HELLO')
Design Notes:
  • Strips whitespace by default (consistent with parse_int, parse_float)

  • Empty strings allowed by default (type validation, not content validation)

  • Use strip=False when whitespace is semantically meaningful

  • Chain with validators for content rules: parse_str(x).bind(non_empty_string())

  • No DoS protection needed (isinstance is O(1), no expensive operations)

valid8r.core.parsers.parse_int(input_value, error_message=None)[source]

Parse a string to an integer.

Converts string representations of integers to Python int values. Handles whitespace trimming and accepts whole numbers in float notation (e.g., “42.0”).

Parameters:
  • input_value (str) – String to parse (leading/trailing whitespace is stripped)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(int) if parsing succeeds, Failure(str) with error message otherwise

Return type:

Maybe[int]

Examples

>>> parse_int("42")
Success(42)
>>> parse_int("  -17  ")
Success(-17)
>>> parse_int("42.0")
Success(42)
>>> parse_int("42.5").is_failure()
True
>>> parse_int("not a number").is_failure()
True
valid8r.core.parsers.parse_float(input_value, error_message=None)[source]

Parse a string to a floating-point number.

Converts string representations of numbers to Python float values. Handles whitespace trimming and scientific notation.

Parameters:
  • input_value (str) – String to parse (leading/trailing whitespace is stripped)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(float) if parsing succeeds, Failure(str) with error message otherwise

Return type:

Maybe[float]

Examples

>>> parse_float("3.14")
Success(3.14)
>>> parse_float("  -2.5  ")
Success(-2.5)
>>> parse_float("1e-3")
Success(0.001)
>>> parse_float("not a number").is_failure()
True
valid8r.core.parsers.parse_bool(input_value, error_message=None)[source]

Parse a string to a boolean value.

Accepts various common representations of true/false values. Case-insensitive and handles whitespace.

Recognized true values: ‘true’, ‘t’, ‘yes’, ‘y’, ‘1’ Recognized false values: ‘false’, ‘f’, ‘no’, ‘n’, ‘0’

Parameters:
  • input_value (str) – String to parse (leading/trailing whitespace is stripped, case-insensitive)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(bool) if parsing succeeds, Failure(str) with error message otherwise

Return type:

Maybe[bool]

Examples

>>> parse_bool("true")
Success(True)
>>> parse_bool("YES")
Success(True)
>>> parse_bool("n")
Success(False)
>>> parse_bool("  0  ")
Success(False)
>>> parse_bool("maybe").is_failure()
True
valid8r.core.parsers.parse_date(input_value, date_format=None, error_message=None)[source]

Parse a string to a date object.

Parses date strings using ISO 8601 format (YYYY-MM-DD) by default, or a custom format if specified.

Parameters:
  • input_value (str) – String to parse (leading/trailing whitespace is stripped)

  • date_format (str | None) – Optional strftime format string (e.g., ‘%Y-%m-%d’, ‘%m/%d/%Y’)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(date) if parsing succeeds, Failure(str) with error message otherwise

Return type:

Maybe[date]

Examples

>>> parse_date("2025-01-15")
Success(datetime.date(2025, 1, 15))
>>> parse_date("01/15/2025", date_format="%m/%d/%Y")
Success(datetime.date(2025, 1, 15))
>>> parse_date("invalid").is_failure()
True
valid8r.core.parsers.parse_datetime(input_value, error_message=None)[source]

Parse a string to a timezone-aware datetime object.

Parses datetime strings in ISO 8601 format with timezone information. Requires timezone to be specified (naive datetimes are rejected).

Supports: - Z suffix for UTC (e.g., ‘2024-01-01T12:00:00Z’) - Explicit UTC offset (e.g., ‘2024-01-01T12:00:00+00:00’) - Positive/negative timezone offsets (e.g., ‘2024-01-01T12:00:00+05:30’, ‘2024-01-01T12:00:00-08:00’) - Fractional seconds (e.g., ‘2024-01-01T12:00:00.123456Z’)

Parameters:
  • input_value (str | None) – String to parse (leading/trailing whitespace is stripped)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(datetime) if parsing succeeds with timezone info,

Failure(str) with error message otherwise

Return type:

Maybe[datetime]

Examples

>>> parse_datetime("2024-01-01T12:00:00Z")
Success(datetime.datetime(2024, 1, 1, 12, 0, tzinfo=datetime.timezone.utc))
>>> parse_datetime("2024-01-01T12:00:00+05:30")
Success(datetime.datetime(2024, 1, 1, 12, 0, tzinfo=datetime.timezone(datetime.timedelta(seconds=19800))))
>>> parse_datetime("2024-01-01T12:00:00").is_failure()
True
valid8r.core.parsers.parse_timedelta(input_value, error_message=None)[source]

Parse a string to a timedelta object.

Parses duration strings in multiple formats: - Simple format: ‘90m’, ‘2h’, ’45s’, ‘3d’ - Combined format: ‘1h 30m’, ‘1d 2h 30m 45s’, ‘1h30m’ (no spaces) - ISO 8601 duration: ‘PT1H30M’, ‘P1DT2H’, ‘PT45S’

Supported units: - d: days - h: hours - m: minutes - s: seconds

Parameters:
  • input_value (str | None) – String to parse (leading/trailing whitespace is stripped)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(timedelta) if parsing succeeds,

Failure(str) with error message otherwise

Return type:

Maybe[timedelta]

Examples

>>> parse_timedelta("90m")
Success(datetime.timedelta(seconds=5400))
>>> parse_timedelta("1h 30m")
Success(datetime.timedelta(seconds=5400))
>>> parse_timedelta("PT1H30M")
Success(datetime.timedelta(seconds=5400))
>>> parse_timedelta("-90m").is_failure()
True
valid8r.core.parsers.parse_complex(input_value, error_message=None)[source]

Parse a string to a complex number.

Accepts various complex number representations including both ‘j’ and ‘i’ notation. Handles parentheses and spaces in the input.

Parameters:
  • input_value (str) – String to parse (whitespace is stripped, both ‘i’ and ‘j’ accepted)

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success(complex) if parsing succeeds, Failure(str) with error message otherwise

Return type:

Maybe[complex]

Examples

>>> parse_complex("3+4j")
Success((3+4j))
>>> parse_complex("3 + 4i")
Success((3+4j))
>>> parse_complex("(2-3j)")
Success((2-3j))
>>> parse_complex("5j")
Success(5j)
>>> parse_complex("invalid").is_failure()
True
valid8r.core.parsers.parse_decimal(input_value, error_message=None)[source]

Parse a string to a Decimal for precise decimal arithmetic.

Uses Python’s Decimal type for arbitrary-precision decimal arithmetic, avoiding floating-point rounding errors. Ideal for financial calculations.

Parameters:
  • input_value (str) – String representation of a decimal number

  • error_message (str | None) – Optional custom error message

Returns:

Success with Decimal value or Failure with an error message

Return type:

Maybe[Decimal]

Examples

>>> parse_decimal("3.14159")
Success(Decimal('3.14159'))
>>> parse_decimal("  0.1  ")
Success(Decimal('0.1'))
>>> parse_decimal("-99.99")
Success(Decimal('-99.99'))
>>> parse_decimal("not a number").is_failure()
True
valid8r.core.parsers.parse_enum(input_value, enum_class, error_message=None)[source]

Parse a string to an enum member.

Matches input against enum member values and names (case-insensitive for names). Handles whitespace trimming and supports enums with empty string values.

Parameters:
  • input_value (str) – String to parse (whitespace is stripped for non-exact matches)

  • enum_class (type[E]) – The Enum class to parse into

  • error_message (str | None) – Optional custom error message for parsing failures

Returns:

Success with enum member if valid, Failure(str) with error message otherwise

Return type:

Maybe[object]

Examples

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 'red'
...     GREEN = 'green'
...     BLUE = 'blue'
>>> parse_enum("red", Color)
Success(<Color.RED: 'red'>)
>>> parse_enum("RED", Color)
Success(<Color.RED: 'red'>)
>>> parse_enum("  green  ", Color)
Success(<Color.GREEN: 'green'>)
>>> parse_enum("yellow", Color).is_failure()
True
valid8r.core.parsers.parse_list(input_value, element_parser=None, separator=',', error_message=None)[source]

Parse a string to a list using the specified element parser and separator.

Splits the input string by the separator and parses each element using the element parser. If no element parser is provided, elements are returned as trimmed strings.

Parameters:
  • input_value (str) – The string to parse

  • element_parser (collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]] | None) – A function that parses individual elements (default: strips whitespace)

  • separator (str) – The string that separates elements (default: ‘,’)

  • error_message (str | None) – Custom error message for parsing failures

Returns:

Success with parsed list or Failure with error message

Return type:

Maybe[list[T]]

Examples

>>> parse_list("a,b,c")
Success(['a', 'b', 'c'])
>>> parse_list("1, 2, 3", element_parser=parse_int)
Success([1, 2, 3])
>>> parse_list("apple|banana|cherry", separator="|")
Success(['apple', 'banana', 'cherry'])
>>> parse_list("1,2,invalid", element_parser=parse_int).is_failure()
True
valid8r.core.parsers.parse_dict(input_value, key_parser=None, value_parser=None, pair_separator=',', key_value_separator=':', error_message=None)[source]

Parse a string to a dictionary using the specified parsers and separators.

Splits the input string by pair_separator, then splits each pair by key_value_separator. Parses keys and values using the provided parsers (defaults to trimmed strings).

Parameters:
  • input_value (str) – The string to parse

  • key_parser (collections.abc.Callable[[str], valid8r.core.maybe.Maybe[K]] | None) – A function that parses keys (default: strips whitespace)

  • value_parser (collections.abc.Callable[[str], valid8r.core.maybe.Maybe[V]] | None) – A function that parses values (default: strips whitespace)

  • pair_separator (str) – The string that separates key-value pairs (default: ‘,’)

  • key_value_separator (str) – The string that separates keys from values (default: ‘:’)

  • error_message (str | None) – Custom error message for parsing failures

Returns:

Success with parsed dictionary or Failure with error message

Return type:

Maybe[dict[K, V]]

Examples

>>> parse_dict("a:1,b:2,c:3")
Success({'a': '1', 'b': '2', 'c': '3'})
>>> parse_dict("x:10, y:20", value_parser=parse_int)
Success({'x': 10, 'y': 20})
>>> parse_dict("name=Alice|age=30", pair_separator="|", key_value_separator="=")
Success({'name': 'Alice', 'age': '30'})
>>> parse_dict("a:1,b:invalid", value_parser=parse_int).is_failure()
True
valid8r.core.parsers.parse_set(input_value, element_parser=None, separator=None, error_message=None)[source]

Parse a string to a set using the specified element parser and separator.

Splits the input string by the separator and parses each element using the element parser. Automatically removes duplicate values. If no element parser is provided, elements are returned as trimmed strings.

Parameters:
  • input_value (str) – The string to parse

  • element_parser (collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]] | None) – A function that parses individual elements (default: strips whitespace)

  • separator (str | None) – The string that separates elements (default: ‘,’)

  • error_message (str | None) – Custom error message for parsing failures

Returns:

Success with parsed set or Failure with error message

Return type:

Maybe[set[T]]

Examples

>>> result = parse_set("a,b,c")
>>> result.is_success()
True
>>> sorted(result.value_or(set()))
['a', 'b', 'c']
>>> result = parse_set("1, 2, 3, 2, 1", element_parser=parse_int)
>>> sorted(result.value_or(set()))
[1, 2, 3]
>>> result = parse_set("red|blue|green|red", separator="|")
>>> sorted(result.value_or(set()))
['blue', 'green', 'red']
>>> parse_set("1,2,invalid", element_parser=parse_int).is_failure()
True
valid8r.core.parsers.parse_int_with_validation(input_value, min_value=None, max_value=None, error_message=None)[source]

Parse a string to an integer with range validation.

Combines parsing and validation in a single step. First parses the string to an integer, then validates it falls within the specified range.

Parameters:
  • input_value (str) – The string to parse

  • min_value (int | None) – Minimum allowed value (inclusive)

  • max_value (int | None) – Maximum allowed value (inclusive)

  • error_message (str | None) – Custom error message for parsing failures

Returns:

Success with validated integer or Failure with error message

Return type:

Maybe[int]

Examples

>>> parse_int_with_validation("42", min_value=0, max_value=100)
Success(42)
>>> parse_int_with_validation("5", min_value=10).is_failure()
True
>>> parse_int_with_validation("150", max_value=100).is_failure()
True
>>> parse_int_with_validation("50", min_value=0, max_value=100)
Success(50)
valid8r.core.parsers.parse_list_with_validation(input_value, element_parser=None, separator=',', min_length=None, max_length=None, error_message=None)[source]

Parse a string to a list with length validation.

Combines parsing and validation in a single step. First parses the string to a list, then validates it has an acceptable number of elements.

Parameters:
  • input_value (str) – The string to parse

  • element_parser (collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]] | None) – A function that parses individual elements

  • separator (str) – The string that separates elements

  • min_length (int | None) – Minimum allowed list length

  • max_length (int | None) – Maximum allowed list length

  • error_message (str | None) – Custom error message for parsing failures

Returns:

Success with validated list or Failure with error message

Return type:

Maybe[list[T]]

Examples

>>> parse_list_with_validation("a,b,c", min_length=2, max_length=5)
Success(['a', 'b', 'c'])
>>> parse_list_with_validation("1,2", element_parser=parse_int, min_length=3).is_failure()
True
>>> parse_list_with_validation("1,2,3,4,5,6", max_length=5).is_failure()
True
>>> parse_list_with_validation("10,20,30", element_parser=parse_int, min_length=1)
Success([10, 20, 30])
valid8r.core.parsers.parse_dict_with_validation(input_value, key_parser=None, value_parser=None, pair_separator=',', key_value_separator=':', required_keys=None, error_message=None)[source]

Parse a string to a dictionary with required keys validation.

Combines parsing and validation in a single step. First parses the string to a dictionary, then validates that all required keys are present.

Parameters:
Returns:

Success with validated dictionary or Failure with error message

Return type:

Maybe[dict[K, V]]

Examples

>>> parse_dict_with_validation("name:Alice,age:30", required_keys=["name", "age"])
Success({'name': 'Alice', 'age': '30'})
>>> parse_dict_with_validation("name:Bob", required_keys=["name", "age"]).is_failure()
True
>>> result = parse_dict_with_validation("x:10,y:20", value_parser=parse_int, required_keys=["x"])
>>> result.value_or({})
{'x': 10, 'y': 20}
valid8r.core.parsers.create_parser(convert_func, error_message=None)[source]

Create a parser function from a conversion function.

This factory takes a function that converts strings to values and wraps it in error handling logic to return Maybe instances.

Parameters:
  • convert_func (collections.abc.Callable[[str], T]) – A function that converts strings to values of type T

  • error_message (str | None) – Optional custom error message for failures

Returns:

A parser function that returns Maybe[T]

Return type:

collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]]

Example

>>> from decimal import Decimal
>>> parse_decimal = create_parser(Decimal, "Invalid decimal format")
>>> result = parse_decimal("3.14")
>>> result.is_success()
True
valid8r.core.parsers.make_parser(func: collections.abc.Callable[[str], T]) collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]][source]
valid8r.core.parsers.make_parser() collections.abc.Callable[[collections.abc.Callable[[str], T]], collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]]]

Create a parser function from a conversion function with a decorator.

Example

@make_parser def parse_decimal(s: str) -> Decimal:

return Decimal(s)

# Or with parentheses @make_parser() def parse_decimal(s: str) -> Decimal:

return Decimal(s)

result = parse_decimal(“123.45”) # Returns Maybe[Decimal]

valid8r.core.parsers.validated_parser(convert_func, validator, error_message=None)[source]

Create a parser with a built-in validator.

This combines parsing and validation in a single function.

Parameters:
Returns:

A parser function that returns Maybe[T]

Return type:

collections.abc.Callable[[str], valid8r.core.maybe.Maybe[T]]

Example

>>> from decimal import Decimal
>>> from valid8r.core.validators import minimum, maximum
>>> # Create a parser for positive decimals
>>> valid_range = lambda x: minimum(0)(x).bind(lambda y: maximum(100)(y))
>>> parse_percent = validated_parser(Decimal, valid_range)
>>> result = parse_percent("42.5")
>>> result.is_success()
True
valid8r.core.parsers.parse_uuid(text, version=None, strict=True)[source]

Parse a string to a UUID.

Uses uuid-utils to parse and validate UUIDs across versions 1, 3, 4, 5, 6, 7, and 8 when available. When version is provided, validates the parsed UUID version. In strict mode (default), a mismatch yields a Failure; otherwise, the mismatch is ignored and the UUID is returned.

Parameters:
  • text (str) – The input string to parse as UUID.

  • version (int | None) – Optional expected UUID version to validate against.

  • strict (bool) – Whether to enforce the expected version when provided.

Returns:

Success with a UUID object or Failure with an error message.

Return type:

Maybe[UUID]

valid8r.core.parsers.parse_ipv4(text)[source]

Parse an IPv4 address string.

Validates and parses IPv4 addresses in dotted-decimal notation. Trims surrounding whitespace.

Parameters:

text (str) – String containing an IPv4 address (whitespace is stripped)

Returns:

Success(IPv4Address) if valid, Failure(str) with error message otherwise

Return type:

Maybe[IPv4Address]

Examples

>>> parse_ipv4("192.168.1.1")
Success(IPv4Address('192.168.1.1'))
>>> parse_ipv4("  10.0.0.1  ")
Success(IPv4Address('10.0.0.1'))
>>> parse_ipv4("256.1.1.1").is_failure()
True
>>> parse_ipv4("not an ip").is_failure()
True
valid8r.core.parsers.parse_ipv6(text)[source]

Parse an IPv6 address string.

Validates and parses IPv6 addresses in standard notation. Rejects scope IDs (e.g., %eth0). Trims surrounding whitespace.

Parameters:

text (str) – String containing an IPv6 address (whitespace is stripped)

Returns:

Success(IPv6Address) if valid, Failure(str) with error message otherwise

Return type:

Maybe[IPv6Address]

Examples

>>> parse_ipv6("::1")
Success(IPv6Address('::1'))
>>> parse_ipv6("2001:0db8:85a3::8a2e:0370:7334")
Success(IPv6Address('2001:db8:85a3::8a2e:370:7334'))
>>> parse_ipv6("  fe80::1  ")
Success(IPv6Address('fe80::1'))
>>> parse_ipv6("192.168.1.1").is_failure()
True
valid8r.core.parsers.parse_ip(text)[source]

Parse a string as either an IPv4 or IPv6 address.

Automatically detects and parses either IPv4 or IPv6 addresses. Trims surrounding whitespace.

Parameters:

text (str) – String containing an IP address (IPv4 or IPv6, whitespace is stripped)

Returns:

Success with IPv4Address or IPv6Address if valid,

Failure(str) with error message otherwise

Return type:

Maybe[IPv4Address | IPv6Address]

Examples

>>> result = parse_ip("192.168.1.1")
>>> result.is_success()
True
>>> result = parse_ip("::1")
>>> result.is_success()
True
>>> parse_ip("  10.0.0.1  ")
Success(IPv4Address('10.0.0.1'))
>>> parse_ip("not an ip").is_failure()
True
valid8r.core.parsers.parse_cidr(text, *, strict=True)[source]

Parse a CIDR network string (IPv4 or IPv6).

Validates and parses network addresses in CIDR notation (e.g., 192.168.1.0/24). By default, validates that host bits are not set (strict mode). With strict=False, host bits are masked to the network address.

Parameters:
  • text (str) – String containing a CIDR network (whitespace is stripped)

  • strict (bool) – If True, reject networks with host bits set; if False, mask them (default: True)

Returns:

Success with IPv4Network or IPv6Network if valid,

Failure(str) with error message otherwise

Return type:

Maybe[IPv4Network | IPv6Network]

Examples

>>> parse_cidr("192.168.1.0/24")
Success(IPv4Network('192.168.1.0/24'))
>>> parse_cidr("10.0.0.0/8")
Success(IPv4Network('10.0.0.0/8'))
>>> parse_cidr("2001:db8::/32")
Success(IPv6Network('2001:db8::/32'))
>>> # Strict mode rejects host bits
>>> parse_cidr("192.168.1.5/24").is_failure()
True
>>> # Non-strict mode masks host bits
>>> result = parse_cidr("192.168.1.5/24", strict=False)
>>> str(result.value_or(None))
'192.168.1.0/24'
class valid8r.core.parsers.UrlParts[source]

Structured URL components.

scheme[source]

Lowercased scheme (e.g. “http”).

username[source]

Username from userinfo, if present.

password[source]

Password from userinfo, if present.

host[source]

Lowercased host or IPv6 literal without brackets, or None when not provided and not required.

port[source]

Explicit port if present, otherwise None.

path[source]

Path component as-is (no normalization).

query[source]

Query string without leading ‘?’.

fragment[source]

Fragment without leading ‘#’.

Examples

>>> from valid8r.core.maybe import Success
>>> match parse_url('https://alice:pw@example.com:8443/x?q=1#top'):
...     case Success(u):
...         (u.scheme, u.username, u.password, u.host, u.port, u.path, u.query, u.fragment)
...     case _:
...         ()
('https', 'alice', 'pw', 'example.com', 8443, '/x', 'q=1', 'top')
scheme: str[source]
username: str | None[source]
password: str | None[source]
host: str | None[source]
port: int | None[source]
path: str[source]
query: str[source]
fragment: str[source]
class valid8r.core.parsers.EmailAddress[source]

Structured email address.

local[source]

Local part (preserves original case).

domain[source]

Domain part lowercased.

Examples

>>> from valid8r.core.maybe import Success
>>> match parse_email('First.Last+tag@Example.COM'):
...     case Success(addr):
...         (addr.local, addr.domain)
...     case _:
...         ()
('First.Last+tag', 'example.com')
local: str[source]
domain: str[source]
class valid8r.core.parsers.PhoneNumber[source]

Structured North American phone number (NANP).

Represents a parsed and validated phone number in the North American Numbering Plan (United States, Canada, and other NANP territories).

area_code[source]

Three-digit area code (NPA).

exchange[source]

Three-digit exchange code (NXX).

subscriber[source]

Four-digit subscriber number.

country_code[source]

Country code (always ‘1’ for NANP).

region[source]

Two-letter region code (‘US’, ‘CA’, etc.).

extension[source]

Optional extension number.

Examples

>>> from valid8r.core.maybe import Success
>>> match parse_phone('(415) 555-2671'):
...     case Success(phone):
...         (phone.area_code, phone.exchange, phone.subscriber)
...     case _:
...         ()
('415', '555', '2671')
area_code: str[source]
exchange: str[source]
subscriber: str[source]
country_code: str[source]
region: str[source]
extension: str | None[source]
property e164: str[source]

E.164 international format (+14155552671).

The E.164 format is the international standard for phone numbers. It includes the country code prefix and no formatting separators.

Returns:

Phone number in E.164 format, with extension if present.

Return type:

str

property national: str[source]

National format ((415) 555-2671).

The national format is the standard format for displaying phone numbers within a country, without the country code.

Returns:

Phone number in national format, with extension if present.

Return type:

str

property international: str[source]

International format (+1 415-555-2671).

The international format includes the country code and uses dashes as separators.

Returns:

Phone number in international format, with extension if present.

Return type:

str

property raw_digits: str[source]

Raw digits with country code (14155552671).

Returns all digits including the country code, with no formatting. Does not include the extension.

Returns:

All digits as a string without any formatting.

Return type:

str

valid8r.core.parsers.parse_url(text, *, allowed_schemes=('http', 'https'), require_host=True)[source]

Parse a URL with light validation.

Rules: - Trim surrounding whitespace only - Require scheme in allowed_schemes (defaults to http/https) - If require_host, netloc must include a valid host (hostname, IPv4, or bracketed IPv6) - Lowercase scheme and host; do not modify path/query/fragment

Failure messages (exact substrings): - Input must be a string - Input must not be empty - Unsupported URL scheme - URL requires host - Invalid host

Parameters:
  • text (str) – The URL string to parse

  • allowed_schemes (collections.abc.Iterable[str]) – Iterable of allowed scheme names (default: (‘http’, ‘https’))

  • require_host (bool) – Whether to require a host in the URL (default: True)

Returns:

Success with UrlParts containing parsed components, or Failure with error message

Return type:

Maybe[UrlParts]

Examples

>>> from valid8r.core.parsers import parse_url
>>> from valid8r.core.maybe import Success
>>>
>>> # Parse a complete URL
>>> result = parse_url('https://user:pass@api.example.com:8080/v1/users?active=true#section')
>>> isinstance(result, Success)
True
>>> url = result.value
>>> url.scheme
'https'
>>> url.host
'api.example.com'
>>> url.port
8080
>>> url.path
'/v1/users'
>>> url.query
'active=true'
>>> url.fragment
'section'
>>>
>>> # Access credentials
>>> url.username
'user'
>>> url.password
'pass'
valid8r.core.parsers.parse_email(text)[source]

Parse a bare email address of the form local@domain.

Uses the email-validator library for RFC 5322 compliant validation. Domain names are normalized to lowercase, local parts preserve their case.

Requires the email-validator library to be installed. If not available, returns a Failure indicating the library is required.

Rules: - Trim surrounding whitespace - Full RFC 5322 email validation - Supports internationalized domains (IDNA) - Domain is lowercased in the result; local part preserves case

Failure messages: - Input must be a string - Input must not be empty - email-validator library is required but not installed - Various RFC-compliant validation error messages from email-validator

Parameters:

text (str) – The email address string to parse

Returns:

Success with EmailAddress or Failure with error message

Return type:

Maybe[EmailAddress]

Examples

>>> from valid8r.core.parsers import parse_email
>>> from valid8r.core.maybe import Success
>>>
>>> # Parse an email with case normalization
>>> result = parse_email('User.Name+tag@Example.COM')
>>> isinstance(result, Success)
True
>>> email = result.value
>>> # Local part preserves original case
>>> email.local
'User.Name+tag'
>>> # Domain is normalized to lowercase
>>> email.domain
'example.com'
valid8r.core.parsers.parse_phone(text, *, region='US', strict=False)[source]

Parse a North American phone number (NANP format).

Parses phone numbers in the North American Numbering Plan format (US, Canada, etc.). Supports various formatting styles and validates area codes and exchanges.

Rules: - Accepts 10-digit or 11-digit (with country code 1) phone numbers - Strips all non-digit characters except extension markers - Validates area code (NPA): cannot start with 0 or 1, cannot be 555 - Validates exchange (NXX): cannot start with 0 or 1, cannot be 555 or 911 - Supports extensions with markers: x, ext, extension, comma - In strict mode, requires formatting characters (not just digits) - Defaults to US region unless specified

Failure messages: - Phone number cannot be empty - Phone number must have exactly 10 digits (after country code) - Invalid area code (starts with 0/1 or reserved) - Invalid exchange (starts with 0/1, reserved, or emergency) - Only North American phone numbers are supported - Invalid format (contains non-digit/non-separator characters) - Strict mode requires formatting characters - Invalid extension (non-numeric or too long)

Parameters:
  • text (str | None) – The phone number string to parse

  • region (str) – Two-letter region code (default: ‘US’)

  • strict (bool) – If True, requires formatting characters (default: False)

Returns:

Success with PhoneNumber or Failure with error message

Return type:

Maybe[PhoneNumber]

Examples

>>> match parse_phone('(415) 555-2671'):
...     case Success(phone):
...         phone.area_code
...     case _:
...         None
'415'
>>> match parse_phone('415-555-2671 x123'):
...     case Success(phone):
...         phone.extension
...     case _:
...         None
'123'
>>> match parse_phone('+1 604 555 1234', region='CA'):
...     case Success(phone):
...         phone.region
...     case _:
...         None
'CA'
valid8r.core.parsers.parse_slug(text, *, min_length=None, max_length=None)[source]

Parse a URL-safe slug (lowercase letters, numbers, hyphens only).

A valid slug contains only lowercase letters, numbers, and hyphens. Cannot start/end with hyphen or have consecutive hyphens.

Parameters:
  • text (str) – String to validate as slug

  • min_length (int | None) – Minimum length (optional)

  • max_length (int | None) – Maximum length (optional)

Returns:

Success with slug or Failure with error

Return type:

Maybe[str]

Examples

>>> from valid8r.core.parsers import parse_slug
>>>
>>> # Valid slugs
>>> parse_slug('hello-world').value_or(None)
'hello-world'
>>> parse_slug('blog-post-123').value_or(None)
'blog-post-123'
>>> parse_slug('a').value_or(None)
'a'
>>>
>>> # With length constraints
>>> parse_slug('hello', min_length=5).value_or(None)
'hello'
>>> parse_slug('hello', max_length=10).value_or(None)
'hello'
>>>
>>> # Invalid slugs
>>> parse_slug('').is_failure()
True
>>> parse_slug('Hello-World').is_failure()
True
>>> parse_slug('hello_world').is_failure()
True
>>> parse_slug('-hello').is_failure()
True
>>> parse_slug('hello-').is_failure()
True
>>> parse_slug('hello--world').is_failure()
True
>>>
>>> # Length constraint failures
>>> parse_slug('hi', min_length=5).is_failure()
True
>>> parse_slug('very-long-slug', max_length=5).is_failure()
True
valid8r.core.parsers.parse_json(text)[source]

Parse a JSON string into a Python object.

Supports all JSON types: objects, arrays, strings, numbers, booleans, null.

Parameters:

text (str) – JSON-formatted string

Returns:

Success with parsed object or Failure with error

Return type:

Maybe[object]

Examples

>>> from valid8r.core.parsers import parse_json
>>>
>>> # JSON objects
>>> parse_json('{"name": "Alice", "age": 30}').value_or(None)
{'name': 'Alice', 'age': 30}
>>>
>>> # JSON arrays
>>> parse_json('[1, 2, 3, 4, 5]').value_or(None)
[1, 2, 3, 4, 5]
>>>
>>> # JSON primitives
>>> parse_json('"hello world"').value_or(None)
'hello world'
>>> parse_json('42').value_or(None)
42
>>> parse_json('true').value_or(None)
True
>>> parse_json('false').value_or(None)
False
>>> parse_json('null').value_or(None)
>>>
>>> # Invalid JSON
>>> parse_json('').is_failure()
True
>>> parse_json('{invalid}').is_failure()
True
>>> parse_json('{"name": "Alice"').is_failure()
True
valid8r.core.parsers.parse_base64(text)[source]

Parse and decode a base64-encoded string.

Accepts both standard and URL-safe base64, with or without padding. Handles whitespace and newlines within the base64 string.

Parameters:

text (str) – Base64-encoded string

Returns:

Success with decoded bytes or Failure with error

Return type:

Maybe[bytes]

Examples

>>> from valid8r.core.parsers import parse_base64
>>>
>>> # Standard base64 with padding
>>> parse_base64('SGVsbG8gV29ybGQ=').value_or(None)
b'Hello World'
>>>
>>> # Standard base64 without padding
>>> parse_base64('SGVsbG8gV29ybGQ').value_or(None)
b'Hello World'
>>>
>>> # URL-safe base64 (hyphens and underscores)
>>> parse_base64('A-A=').is_success()
True
>>> parse_base64('Pz8_').is_success()
True
>>>
>>> # Base64 with whitespace (automatically stripped)
>>> parse_base64(' SGVsbG8gV29ybGQ= ').value_or(None)
b'Hello World'
>>>
>>> # Invalid base64
>>> parse_base64('').is_failure()
True
>>> parse_base64('Not@Valid!').is_failure()
True
>>> parse_base64('====').is_failure()
True
valid8r.core.parsers.parse_jwt(text)[source]

Parse and validate a JWT (JSON Web Token) structure.

Validates that the JWT has exactly three parts (header.payload.signature) separated by dots, and that each part is valid base64url. Also validates that header and payload are valid JSON.

Note: This function validates JWT structure only. It does NOT verify the cryptographic signature. Use a dedicated JWT library (e.g., PyJWT) for signature verification and claims validation.

Parameters:

text (str) – JWT string to validate

Returns:

Success with original JWT or Failure with error

Return type:

Maybe[str]

Examples

>>> from valid8r.core.parsers import parse_jwt
>>>
>>> # Valid JWT (structure only - signature not verified)
>>> jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.sig'
>>> parse_jwt(jwt).is_success()
True
>>>
>>> # JWT with whitespace (automatically stripped)
>>> parse_jwt(' ' + jwt + ' ').is_success()
True
>>>
>>> # Invalid: empty string
>>> parse_jwt('').is_failure()
True
>>>
>>> # Invalid: wrong number of parts
>>> parse_jwt('header.payload').is_failure()
True
>>> parse_jwt('a.b.c.d').is_failure()
True
>>>
>>> # Invalid: non-base64url encoding
>>> parse_jwt('not-base64!.eyJzdWIiOiIxMjM0In0.sig').is_failure()
True
>>>
>>> # Invalid: non-JSON header/payload
>>> parse_jwt('bm90anNvbg==.eyJzdWIiOiIxMjM0In0.sig').is_failure()
True
valid8r.core.parsers.parse_path(text, *, expand_user=False, resolve=False, error_message=None)[source]

Parse a string into a pathlib.Path object.

Converts string representations of filesystem paths to Python Path objects. Handles cross-platform path formats, optional home directory expansion, and optional resolution to absolute paths.

Parameters:
  • text (str | None) – The path string to parse (leading/trailing whitespace is stripped)

  • expand_user (bool) – If True, expand ~ to user’s home directory (default: False)

  • resolve (bool) – If True, resolve to absolute path following symlinks (default: False)

  • error_message (str | None) – Custom error message for invalid input (optional)

Returns:

Success(Path) if parsing succeeds, Failure(str) with error message otherwise

Return type:

Maybe[Path]

Examples

>>> parse_path('/home/user/file.txt')
Success(PosixPath('/home/user/file.txt'))
>>> parse_path('data/file.txt')
Success(PosixPath('data/file.txt'))
>>> parse_path('')
Failure('Path cannot be empty')
>>> parse_path(None)
Failure('Path cannot be empty')

Notes

  • Path normalization (collapsing redundant separators) happens automatically

  • This parser does NOT validate path existence - use validators for that

  • Use expand_user=True to expand ~ to the user’s home directory

  • Use resolve=True to convert relative paths to absolute paths

  • The resolve option will follow symlinks and normalize the path

  • Input length is limited to 4096 characters to prevent DoS attacks