Skip to content

Commit

Permalink
Merge branch '3.x-line' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
sloria committed Jan 23, 2025
2 parents 944f6de + c4284da commit f279186
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 6 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,4 @@ Contributors (chronological)
- Peter C `@somethingnew2-0 <https://github.com/somethingnew2-0>`_
- Marcel Jackwerth `@mrcljx` <https://github.com/mrcljx>`_
- Fares Abubaker `@Fares-Abubaker <https://github.com/Fares-Abubaker>`_
- Nicolas Simonds `@0xDEC0DE <https://github.com/0xDEC0DE>`_
4 changes: 3 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Previously-deprecated APIs have been removed, including:
- ``Field.fail``, which was replaced by ``Field.make_error`` in 3.0.0.
- `json_module` class Meta option (deprecated in 3.0.0b3). Use `render_module` instead.

3.26.0 (unreleased)
3.26.0 (2025-01-22)
*******************

Features:
Expand All @@ -137,6 +137,8 @@ Bug fixes:
- Correctly handle multiple `@post_load <marshmallow.post_load>` methods where one method appends to
the data and another passes ``pass_original=True`` (:issue:`1755`).
Thanks :user:`ghostwheel42` for reporting.
- ``URL`` fields now properly validate ``file`` paths (:issue:`2249`).
Thanks :user:`0xDEC0DE` for reporting and fixing.

Documentation:

Expand Down
17 changes: 12 additions & 5 deletions src/marshmallow/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ def _repr_args(self) -> str:
return f"validators={self.validators!r}"

def __call__(self, value: typing.Any) -> typing.Any:
errors = []
kwargs = {}
errors: list[str | dict] = []
kwargs: dict[str, typing.Any] = {}
for validator in self.validators:
try:
validator(value)
Expand All @@ -79,8 +79,7 @@ def __call__(self, value: typing.Any) -> typing.Any:
if isinstance(err.messages, dict):
errors.append(err.messages)
else:
# FIXME : Get rid of cast
errors.extend(typing.cast(list, err.messages))
errors.extend(err.messages)
if errors:
raise ValidationError(errors, **kwargs)
return value
Expand Down Expand Up @@ -203,6 +202,7 @@ def __call__(self, value: str) -> str:
raise ValidationError(message)

# Check first if the scheme is valid
scheme = None
if "://" in value:
scheme = value.split("://")[0].lower()
if scheme not in self.schemes:
Expand All @@ -212,7 +212,14 @@ def __call__(self, value: str) -> str:
relative=self.relative, absolute=self.absolute, require_tld=self.require_tld
)

if not regex.search(value):
# Hostname is optional for file URLS. If absent it means `localhost`.
# Fill it in for the validation if needed
if scheme == "file" and value.startswith("file:///"):
matched = regex.search(value.replace("file:///", "file://localhost/", 1))
else:
matched = regex.search(value)

if not matched:
raise ValidationError(message)

return value
Expand Down
34 changes: 34 additions & 0 deletions tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,40 @@ def test_url_custom_scheme():
assert validator(url) == url


@pytest.mark.parametrize(
"valid_url",
(
"file:///tmp/tmp1234",
"file://localhost/tmp/tmp1234",
"file:///C:/Users/test/file.txt",
"file://localhost/C:/Program%20Files/file.exe",
"file:///home/user/documents/test.pdf",
"file:///tmp/test%20file.txt",
"file:///",
"file://localhost/",
),
)
def test_url_accepts_valid_file_urls(valid_url):
validator = validate.URL(schemes={"file"})
assert validator(valid_url) == valid_url


@pytest.mark.parametrize(
"invalid_url",
(
"file://",
"file:/tmp/file.txt",
"file:tmp/file.txt",
"file://hostname/path",
"file:///tmp/test file.txt",
),
)
def test_url_rejects_invalid_file_urls(invalid_url):
validator = validate.URL(schemes={"file"})
with pytest.raises(ValidationError, match="Not a valid URL."):
assert validator(invalid_url)


def test_url_relative_and_custom_schemes():
validator = validate.URL(relative=True)
# By default, ws not allowed
Expand Down

0 comments on commit f279186

Please sign in to comment.