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 19, 2025
2 parents f8b86b9 + eabaa83 commit 5a8fa1a
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ Features:
- Typing: Improve type coverage of `marshmallow.Schema.SchemaMeta` (:pr:`2761`).
- Typing: `marshmallow.Schema.loads` parameter allows `bytes` and `bytesarray` (:pr:`2769`).

Bug fixes:

- Respect ``data_key`` when schema validators raise a `ValidationError <marshmallow.exceptions.ValidationError>`
with a ``field_name`` argument (:issue:`2170`). Thanks :user:`matejsp` for reporting.

Documentation:

- Add :doc:`upgrading guides <upgrading>` for 3.24 and 3.26 (:pr:`2780`).
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ include = [
exclude = ["docs/_build/"]

[tool.ruff]
src = ["src"]
src = ["src", "tests", "examples"]
fix = true
show-fixes = true
output-format = "full"
Expand Down
37 changes: 28 additions & 9 deletions src/marshmallow/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
VALIDATES_SCHEMA,
)
from marshmallow.error_store import ErrorStore
from marshmallow.exceptions import StringNotCollectionError, ValidationError
from marshmallow.exceptions import SCHEMA, StringNotCollectionError, ValidationError
from marshmallow.orderedset import OrderedSet
from marshmallow.utils import get_value, is_collection, set_value

Expand Down Expand Up @@ -765,16 +765,16 @@ def loads(

def _run_validator(
self,
validator_func,
validator_func: types.SchemaValidator,
output,
*,
original_data,
error_store,
many,
partial,
unknown,
pass_original,
index=None,
error_store: ErrorStore,
many: bool,
partial: bool | types.StrSequenceOrSet | None,
unknown: types.UnknownOption | None,
pass_original: bool,
index: int | None = None,
):
try:
if pass_original: # Pass original, raw data (before unmarshalling)
Expand All @@ -784,7 +784,26 @@ def _run_validator(
else:
validator_func(output, partial=partial, many=many, unknown=unknown)
except ValidationError as err:
error_store.store_error(err.messages, err.field_name, index=index)
field_name = err.field_name
data_key: str
if field_name == SCHEMA:
data_key = SCHEMA
else:
field_obj: Field | None = None
try:
field_obj = self.fields[field_name]
except KeyError:
if field_name in self.declared_fields:
field_obj = self.declared_fields[field_name]
if field_obj:
data_key = (
field_obj.data_key
if field_obj.data_key is not None
else field_name
)
else:
data_key = field_name
error_store.store_error(err.messages, data_key, index=index)

def validate(
self,
Expand Down
12 changes: 12 additions & 0 deletions src/marshmallow/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@
UnknownOption: TypeAlias = typing.Literal["exclude", "include", "raise"]


class SchemaValidator(typing.Protocol):
def __call__(
self,
output: typing.Any,
original_data: typing.Any = ...,
*,
partial: bool | StrSequenceOrSet | None = None,
unknown: UnknownOption | None = None,
many: bool = False,
) -> None: ...


class RenderModule(typing.Protocol):
def dumps(
self, obj: typing.Any, *args: typing.Any, **kwargs: typing.Any
Expand Down
28 changes: 28 additions & 0 deletions tests/test_decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,34 @@ def validate_many(self, data, many, **kwargs):
assert "bar" in errors[0]
assert "_schema" not in errors

# /~https://github.com/marshmallow-code/marshmallow/issues/2170
def test_data_key_is_used_in_errors_dict(self):
class MySchema(Schema):
foo = fields.Int(data_key="fooKey")

@validates("foo")
def validate_foo(self, value, **kwargs):
raise ValidationError("from validates")

@validates_schema(skip_on_field_errors=False)
def validate_schema(self, data, **kwargs):
raise ValidationError("from validates_schema str", field_name="foo")

@validates_schema(skip_on_field_errors=False)
def validate_schema2(self, data, **kwargs):
raise ValidationError({"fooKey": "from validates_schema dict"})

with pytest.raises(ValidationError) as excinfo:
MySchema().load({"fooKey": 42})
exc = excinfo.value
assert exc.messages == {
"fooKey": [
"from validates",
"from validates_schema str",
"from validates_schema dict",
]
}


def test_decorator_error_handling():
class ExampleSchema(Schema):
Expand Down

0 comments on commit 5a8fa1a

Please sign in to comment.