Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions packages/atproto_client/models/string_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
)
LANG_RE = re.compile(r'^(i|[a-z]{2,3})(-[A-Za-z0-9-]+)?$')
RKEY_RE = re.compile(r'^[A-Za-z0-9._:~-]{1,512}$')
TID_RE = re.compile(rf'^[2-7a-z]{{{TID_LENGTH}}}$')
TID_RE = re.compile(rf'^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{{{TID_LENGTH - 1}}}$')
CID_RE = re.compile(r'^[A-Za-z0-9+]{8,}$')
AT_URI_RE = re.compile(
r'^at://' # Must start with at://
Expand Down Expand Up @@ -424,9 +424,10 @@ def validate_tid(v: str, _: ValidationInfo) -> str:

- Exactly 13 characters

- Only lowercase letters and numbers 2-7
- Only lowercase letters and numbers 2-7 (base32-sortable alphabet)

- First byte's high bit (0x40) must be 0
- First character must be one of 234567abcdefghij so that the high bit
Comment thread
jacoblapenna marked this conversation as resolved.
Outdated
of the underlying 64-bit integer is 0

Args:
v: The TID to validate (e.g. 3jxtb5w2hkt2m)
Expand All @@ -437,7 +438,7 @@ def validate_tid(v: str, _: ValidationInfo) -> str:
Raises:
ValueError: If TID format is invalid
"""
if not TID_RE.match(v) or (ord(v[0]) & 0x40):
if not TID_RE.match(v):
raise ValueError(f'Invalid TID: must be exactly {TID_LENGTH} lowercase letters/numbers')
return v

Expand Down
18 changes: 18 additions & 0 deletions tests/test_atproto_client/models/tests/test_string_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,24 @@ def test_string_format_validation_with_valid(field_name: str, valid_value: str)
assert validated == valid_value


@pytest.mark.parametrize('valid_tid', ['a222222222222', 'j222222222222', 'aaaaaaaaaaaaa', '3jzfcijpj2z2a'])
Comment thread
jacoblapenna marked this conversation as resolved.
Outdated
Comment thread
jacoblapenna marked this conversation as resolved.
Outdated
def test_tid_valid_first_letters(valid_tid: str) -> None:
"""Test that TIDs starting with a-j (high bit of the 64-bit value is 0) are accepted.

The interop test files do not cover valid TIDs starting with a letter.
"""
TidTypeAdapter = TypeAdapter(string_formats.Tid)
assert TidTypeAdapter.validate_python(valid_tid, context={_OPT_IN_KEY: True}) == valid_tid


@pytest.mark.parametrize('invalid_tid', ['k222222222222', 'zzzzzzzzzzzzz', '1222222222222', '8222222222222'])
Comment thread
jacoblapenna marked this conversation as resolved.
Outdated
def test_tid_invalid_first_letters(invalid_tid: str) -> None:
"""Test that TIDs starting with k-z or non-base32-sortable chars are rejected."""
TidTypeAdapter = TypeAdapter(string_formats.Tid)
with pytest.raises(ValidationError):
TidTypeAdapter.validate_python(invalid_tid, context={_OPT_IN_KEY: True})


@pytest.mark.parametrize(
'valid_value',
[
Expand Down
Loading