Skip to content

Commit

Permalink
make it look nice
Browse files Browse the repository at this point in the history
try again

jeez

try to fix spacing
  • Loading branch information
zzstoatzz committed Dec 13, 2024
1 parent 965c3c4 commit a92a97e
Showing 1 changed file with 108 additions and 86 deletions.
194 changes: 108 additions & 86 deletions packages/atproto_client/models/string_formats.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import re
from datetime import datetime
from functools import wraps
from typing import Callable, Mapping, Set, Union, cast
from urllib.parse import urlparse

Expand Down Expand Up @@ -53,15 +52,24 @@


def only_validate_if_strict(validate_fn: Callable[..., str]) -> Callable[..., str]:
"""Skip pydantic validation if not opting into strict validation via context."""
"""Skip pydantic validation if not opting into strict validation via context.
:param validate_fn: The validation function to conditionally apply
:type validate_fn: Callable[..., str]
:return: A wrapped validation function that only validates in strict mode
:rtype: Callable[..., str]
"""

# Wrapper could be likely be generalized to support other signatures
@wraps(validate_fn)
def wrapper(v: str, info: ValidationInfo) -> str:
if info and isinstance(info.context, Mapping) and info.context.get(_OPT_IN_KEY, False):
return cast(core_schema.NoInfoValidatorFunction, validate_fn)(v)
return v

# Preserve the original function's name and docstring without
# requiring the decorated function to be passed `info`
wrapper.__name__ = validate_fn.__name__
wrapper.__doc__ = validate_fn.__doc__

return wrapper


Expand All @@ -70,20 +78,22 @@ def validate_handle(v: str) -> str:
"""Validate an AT Protocol handle.
A handle must be a valid domain name with:
- 2+ segments separated by dots
- ASCII alphanumeric characters and hyphens only
- 1-63 chars per segment
- Max 253 chars total
- Last segment cannot start with a digit
Args:
v: The handle to validate (e.g. "alice.bsky.social")
- Max 253 chars total
Returns:
str: The validated handle
- Last segment cannot start with a digit
Raises:
ValueError: If handle format is invalid
:param v: The handle to validate (e.g. alice.bsky.social)
:type v: str
:return: The validated handle
:rtype: str
:raises ValueError: If handle format is invalid
"""
# Check ASCII first
if not v.isascii():
Expand All @@ -103,21 +113,24 @@ def validate_did(v: str) -> str:
"""Validate an AT Protocol DID.
A DID must follow the pattern:
- Format: did:method:identifier
- Method must be lowercase letters
- Identifier allows alphanumeric chars, dots, underscores, hyphens, and percent
- Max 2KB length
- No /?#[]@ characters allowed
- Valid percent-encoding if used
Args:
v: The DID to validate (e.g. "did:plc:z72i7hdynmk6r22z27h6tvur")
- No /?#[]@ characters allowed
Returns:
str: The validated DID
- Valid percent-encoding if used
Raises:
ValueError: If DID format is invalid
:param v: The DID to validate (e.g. did:plc:z72i7hdynmk6r22z27h6tvur)
:type v: str
:return: The validated DID
:rtype: str
:raises ValueError: If DID format is invalid
"""
# Check for invalid characters
if any(c in v for c in '/?#[]@'):
Expand All @@ -142,22 +155,26 @@ def validate_nsid(v: str) -> str:
"""Validate an AT Protocol NSID (Namespaced Identifier).
An NSID must have:
- 3+ segments separated by dots
- Reversed domain name (lowercase alphanumeric + hyphen)
- Name segment (letters only)
- Max 317 chars total
- No segments ending in numbers
- No @_*#! special characters
- Max 63 chars per segment
Args:
v: The NSID to validate (e.g. "app.bsky.feed.post")
- No @_*#! special characters
Returns:
str: The validated NSID
- Max 63 chars per segment
Raises:
ValueError: If NSID format is invalid
:param v: The NSID to validate (e.g. app.bsky.feed.post)
:type v: str
:return: The validated NSID
:rtype: str
:raises ValueError: If NSID format is invalid
"""
if (
not atproto_core_validate_nsid(v, soft_fail=True)
Expand All @@ -177,17 +194,16 @@ def validate_language(v: str) -> str:
"""Validate an ISO language code.
Must match pattern:
- 2-3 letter language code or 'i'
- Optional subtag with alphanumeric chars and hyphens
Args:
v: The language code to validate (e.g. "en" or "en-US")
- 2-3 letter language code or 'i'
Returns:
str: The validated language code
- Optional subtag with alphanumeric chars and hyphens
Raises:
ValueError: If language code format is invalid
:param v: The language code to validate (e.g. en or en-US)
:type v: str
:return: The validated language code
:rtype: str
:raises ValueError: If language code format is invalid
"""
if not LANG_RE.match(v):
raise ValueError('Invalid language code: must be ISO language code (e.g. en or en-US)')
Expand All @@ -199,18 +215,18 @@ def validate_record_key(v: str) -> str:
"""Validate an AT Protocol record key.
A record key must:
- Be 1-512 characters
- Contain only alphanumeric chars, dots, underscores, colons, tildes, or hyphens
- Not be "." or ".."
Args:
v: The record key to validate (e.g. "3jxtb5w2hkt2m")
- Contain only alphanumeric chars, dots, underscores, colons, tildes, or hyphens
Returns:
str: The validated record key
- Not be "." or ".."
Raises:
ValueError: If record key format is invalid
:param v: The record key to validate (e.g. 3jxtb5w2hkt2m)
:type v: str
:return: The validated record key
:rtype: str
:raises ValueError: If record key format is invalid
"""
if v in INVALID_RECORD_KEYS or not RKEY_RE.match(v):
raise ValueError(
Expand All @@ -224,17 +240,16 @@ def validate_cid(v: str) -> str:
"""Validate a Content Identifier (CID).
Must be:
- Minimum 8 characters
- Alphanumeric characters and plus signs only
Args:
v: The CID to validate
- Minimum 8 characters
Returns:
str: The validated CID
- Alphanumeric characters and plus signs only
Raises:
ValueError: If CID format is invalid
:param v: The CID to validate (e.g. bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi)
:type v: str
:return: The validated CID
:rtype: str
:raises ValueError: If CID format is invalid
"""
if not CID_RE.match(v):
raise ValueError('Invalid CID: must be a valid Content Identifier with minimum length 8')
Expand All @@ -246,20 +261,22 @@ def validate_at_uri(v: str) -> str:
"""Validate an AT Protocol URI.
Must follow pattern:
- Starts with "at://"
- Starts with at://
- Contains handle or DID
- Optional /collection/record-key path
- Max 8KB length
- No query parameters or fragments
Args:
v: The AT-URI to validate (e.g. "at://alice.bsky.social/app.bsky.feed.post/3jxtb5w2hkt2m")
- Max 8KB length
Returns:
str: The validated AT-URI
- No query parameters or fragments
Raises:
ValueError: If AT-URI format is invalid
:param v: The AT-URI to validate (e.g. at://alice.bsky.social/app.bsky.feed.post/3jxtb5w2hkt2m)
:type v: str
:return: The validated AT-URI
:rtype: str
:raises ValueError: If AT-URI format is invalid
"""
if len(v) >= MAX_AT_URI_LENGTH:
raise ValueError(f'Invalid AT-URI: must be under {MAX_AT_URI_LENGTH} chars')
Expand All @@ -275,21 +292,24 @@ def validate_datetime(v: str) -> str:
"""Validate an ISO 8601/RFC 3339 datetime string.
Requirements:
- Must use uppercase 'T' as time separator
- Must use uppercase T as time separator
- Must include seconds (HH:MM:SS)
- Must have timezone (Z or ±HH:MM)
- No -00:00 timezone allowed
- Valid fractional seconds format if used
- No whitespace allowed
Args:
v: The datetime string to validate (e.g. "2024-11-24T06:02:00Z")
- Valid fractional seconds format if used
Returns:
str: The validated datetime string
- No whitespace allowed
Raises:
ValueError: If datetime format is invalid
:param v: The datetime string to validate (e.g. 2024-11-24T06:02:00Z)
:type v: str
:return: The validated datetime string
:rtype: str
:raises ValueError: If datetime format is invalid
"""
# Must contain uppercase T and Z if used
if v != v.strip():
Expand Down Expand Up @@ -327,18 +347,18 @@ def validate_tid(v: str) -> str:
"""Validate an AT Protocol TID (Temporal ID).
Must be:
- Exactly 13 characters
- Only lowercase letters and numbers 2-7
- First byte's high bit (0x40) must be 0
Args:
v: The TID to validate (e.g. "3jxtb5w2hkt2m")
- Only lowercase letters and numbers 2-7
Returns:
str: The validated TID
- First byte's high bit (0x40) must be 0
Raises:
ValueError: If TID format is invalid
:param v: The TID to validate (e.g. 3jxtb5w2hkt2m)
:type v: str
:return: The validated TID
:rtype: str
:raises ValueError: If TID format is invalid
"""
if not TID_RE.match(v) or (ord(v[0]) & 0x40):
raise ValueError(f'Invalid TID: must be exactly {TID_LENGTH} lowercase letters/numbers')
Expand All @@ -350,20 +370,22 @@ def validate_uri(v: str) -> str:
"""Validate a standard URI.
Requirements:
- Must have a scheme starting with a letter
- Must have authority (netloc) or path/query/fragment
- Max 8KB length
- No spaces allowed
- Must follow RFC-3986 format
Args:
v: The URI to validate (e.g. "https://example.com/path")
- No spaces allowed
Returns:
str: The validated URI
- Must follow RFC-3986 format
Raises:
ValueError: If URI format is invalid
:param v: The URI to validate (e.g. https://example.com/path)
:type v: str
:return: The validated URI
:rtype: str
:raises ValueError: If URI format is invalid
"""
if len(v) >= MAX_URI_LENGTH or ' ' in v:
raise ValueError(f'Invalid URI: must be under {MAX_URI_LENGTH} chars and not contain spaces')
Expand Down

0 comments on commit a92a97e

Please sign in to comment.