Skip to content

Commit

Permalink
Bump pydantic to 2.10.0 and remove Base64 workaround (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
facutuesca authored Nov 20, 2024
1 parent cfb3d4d commit 5a50381
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 36 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies = [
"cryptography",
"packaging",
"pyasn1 ~= 0.6",
"pydantic",
"pydantic >= 2.10.0",
"sigstore~=3.4",
"sigstore-protobuf-specs",
]
Expand Down
26 changes: 1 addition & 25 deletions src/pypi_attestations/_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from packaging.utils import parse_sdist_filename, parse_wheel_filename
from pyasn1.codec.der.decoder import decode as der_decode
from pyasn1.type.char import UTF8String
from pydantic import Base64Encoder, BaseModel, ConfigDict, EncodedBytes, Field, field_validator
from pydantic import Base64Bytes, BaseModel, ConfigDict, Field, field_validator
from pydantic.alias_generators import to_snake
from pydantic_core import ValidationError
from sigstore._utils import _sha256_streaming
Expand All @@ -38,30 +38,6 @@
from sigstore.verify.policy import VerificationPolicy


class Base64EncoderSansNewline(Base64Encoder):
r"""A Base64Encoder that doesn't insert newlines when encoding.
Pydantic's Base64Bytes type inserts newlines b'\n' every 76 characters because they
use `base64.encodebytes()` instead of `base64.b64encode()`. Pydantic maintainers
have stated that they won't fix this, and that users should work around it by
defining their own Base64 type with a custom encoder.
See https://github.com/pydantic/pydantic/issues/9072 for more details.
"""

@classmethod
def encode(cls, value: bytes) -> bytes:
"""Encode bytes to base64."""
return base64.b64encode(value)

@classmethod
def decode(cls, value: bytes) -> bytes:
"""Decode base64 bytes."""
return base64.b64decode(value, validate=True)


Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64EncoderSansNewline)]


class Distribution(BaseModel):
"""Represents a Python package distribution.
Expand Down
16 changes: 6 additions & 10 deletions test/test_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import pretend
import pytest
import sigstore
from pydantic import BaseModel, TypeAdapter, ValidationError
from pydantic import Base64Bytes, BaseModel, TypeAdapter, ValidationError
from sigstore.dsse import DigestSet, StatementBuilder, Subject
from sigstore.models import Bundle
from sigstore.oidc import IdentityToken
Expand Down Expand Up @@ -614,18 +614,14 @@ def test_version(self) -> None:


class DummyModel(BaseModel):
base64_bytes: impl.Base64Bytes
base64_bytes: Base64Bytes


class TestBase64Bytes:
# See the docstrings for `_impl.Base64Bytes` for more details
def test_decoding(self) -> None:
# This raises when using our custom type. When using Pydantic's Base64Bytes,
# this succeeds
# The exception message is different in Python 3.9 vs >=3.10
with pytest.raises(ValueError, match="Non-base64 digit found|Only base64 data is allowed"):
DummyModel(base64_bytes=b"a\n\naaa")

# Regression test for an issue with pydantic < 2.10.0
# The Base64Bytes Pydantic type should not insert newlines
# when encoding to base64.
# See https://github.com/pydantic/pydantic/issues/9072
def test_encoding(self) -> None:
model = DummyModel(base64_bytes=b"aaaa" * 76)
assert "\\n" not in model.model_dump_json()

0 comments on commit 5a50381

Please sign in to comment.