diff --git a/pydantic_extra_types/s3.py b/pydantic_extra_types/s3.py index b58068c..eaf1254 100644 --- a/pydantic_extra_types/s3.py +++ b/pydantic_extra_types/s3.py @@ -40,14 +40,16 @@ class TestModel(BaseModel): ``` """ - patt: ClassVar[str] = r'^s3://([^/]+)/(.*?([^/]+)/?)$' + patt: ClassVar[re.Pattern[str]] = re.compile(r'^s3://([^/]+)/(.*?([^/]+)/?)$') def __init__(self, value: str) -> None: self.value = value - groups: tuple[str, str, str] = re.match(self.patt, self.value).groups() # type: ignore - self.bucket: str = groups[0] - self.key: str = groups[1] - self.last_key: str = groups[2] + match = self.patt.match(self.value) + if match is None: + raise ValueError(f'Invalid S3 path: {value!r}') + self.bucket: str = match.group(1) + self.key: str = match.group(2) + self.last_key: str = match.group(3) def __str__(self) -> str: # pragma: no cover return self.value @@ -65,5 +67,4 @@ def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaH return core_schema.with_info_after_validator_function( cls._validate, core_schema.str_schema(pattern=cls.patt), - field_name=cls.__class__.__name__, ) diff --git a/tests/test_s3.py b/tests/test_s3.py index abfb1e4..c636b0e 100644 --- a/tests/test_s3.py +++ b/tests/test_s3.py @@ -150,3 +150,17 @@ def test_s3(raw: str, bucket: str, key: str, last_key: str): def test_wrong_s3(): with pytest.raises(ValidationError): S3Check(path='s3/ok') + + +@pytest.mark.parametrize( + 'invalid_path', + [ + 's3/ok', + 'not-an-s3-path', + 's3://bucket-only', + ], +) +def test_wrong_s3_raises_value_error(invalid_path: str): + """Test that invalid S3 paths raise ValueError.""" + with pytest.raises(ValueError, match='Invalid S3 path'): + S3Path(invalid_path)