Browse Source

Add new arguments to Marshmallow decorated methods

"unknown" and "data_key" added in Marshmallow 4.0.0

https://marshmallow.readthedocs.io/en/latest/marshmallow.decorators.html
merge-requests/171/head
Andrew Shu 1 month ago
parent
commit
25b7603a2a
  1. 12
      tildes/tildes/schemas/group.py
  2. 9
      tildes/tildes/schemas/listing.py
  3. 24
      tildes/tildes/schemas/topic.py
  4. 18
      tildes/tildes/schemas/user.py

12
tildes/tildes/schemas/group.py

@ -10,6 +10,7 @@ import sqlalchemy_utils
from marshmallow import pre_load, Schema, validates
from marshmallow.exceptions import ValidationError
from marshmallow.fields import DateTime
from marshmallow.types import UnknownOption
from tildes.schemas.context import TildesSchemaContext, TildesContext
from tildes.schemas.fields import Ltree, Markdown, SimpleString
@ -42,7 +43,9 @@ class GroupSchema(Schema):
sidebar_markdown = Markdown(allow_none=True)
@pre_load
def prepare_path(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_path(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the path value before it's validated."""
# pylint: disable=unused-argument
if not TildesSchemaContext.get(default=TildesContext()).get(
@ -60,8 +63,9 @@ class GroupSchema(Schema):
return new_data
@validates("path")
def validate_path(self, value: sqlalchemy_utils.Ltree) -> None:
def validate_path(self, value: sqlalchemy_utils.Ltree, data_key: str) -> None:
"""Validate the path field, raising an error if an issue exists."""
# pylint: disable=unused-argument
# check each element for length and against validity regex
path_elements = value.path.split(".")
for element in path_elements:
@ -72,7 +76,9 @@ class GroupSchema(Schema):
raise ValidationError("Path element %s is invalid" % element)
@pre_load
def prepare_sidebar_markdown(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_sidebar_markdown(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the sidebar_markdown value before it's validated."""
# pylint: disable=unused-argument
if "sidebar_markdown" not in data:

9
tildes/tildes/schemas/listing.py

@ -7,6 +7,7 @@ from typing import Any
from marshmallow import pre_load, Schema, validates_schema, ValidationError
from marshmallow.fields import Boolean, Integer
from marshmallow.types import UnknownOption
from marshmallow.validate import Range
from tildes.enums import TopicSortOption
@ -21,7 +22,9 @@ class PaginatedListingSchema(Schema):
per_page = Integer(validate=Range(min=1, max=100), load_default=50)
@validates_schema
def either_after_or_before(self, data: dict, many: bool, partial: Any) -> None:
def either_after_or_before(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> None:
"""Fail validation if both after and before were specified."""
# pylint: disable=unused-argument
if data.get("after") and data.get("before"):
@ -40,7 +43,7 @@ class TopicListingSchema(PaginatedListingSchema):
@pre_load
def reset_rank_start_on_first_page(
self, data: dict, many: bool, partial: Any
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Reset rank_start to 1 if this is a first page (no before/after)."""
# pylint: disable=unused-argument
@ -66,7 +69,7 @@ class MixedListingSchema(PaginatedListingSchema):
@pre_load
def set_anchor_type_from_before_or_after(
self, data: dict, many: bool, partial: Any
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Set the anchor_type if before or after has a special value indicating type.

24
tildes/tildes/schemas/topic.py

@ -9,6 +9,7 @@ from urllib.parse import urlparse
from marshmallow import pre_load, Schema, validates, validates_schema, ValidationError
from marshmallow.fields import DateTime, List, Nested, String, URL
from marshmallow.types import UnknownOption
from tildes.lib.url_transform import apply_url_transformations
from tildes.schemas.fields import Enum, ID36, Markdown, SimpleString
@ -36,7 +37,9 @@ class TopicSchema(Schema):
group = Nested(GroupSchema, dump_only=True)
@pre_load
def prepare_title(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_title(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the title before it's validated."""
# pylint: disable=unused-argument
if "title" not in data:
@ -56,7 +59,9 @@ class TopicSchema(Schema):
return new_data
@pre_load
def prepare_tags(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_tags(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the tags before they're validated."""
# pylint: disable=unused-argument
if "tags" not in data:
@ -98,7 +103,7 @@ class TopicSchema(Schema):
return new_data
@validates("tags")
def validate_tags(self, value: list[str]) -> None:
def validate_tags(self, value: list[str], data_key: str) -> None:
"""Validate the tags field, raising an error if an issue exists.
Note that tags are validated by ensuring that each tag would be a valid group
@ -107,6 +112,7 @@ class TopicSchema(Schema):
between groups and tags. For example, a popular tag in a group could be
converted into a sub-group easily.
"""
# pylint: disable=unused-argument
group_schema = GroupSchema(partial=True)
for tag in value:
try:
@ -115,7 +121,9 @@ class TopicSchema(Schema):
raise ValidationError("Tag %s is invalid" % tag) from exc
@pre_load
def prepare_markdown(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_markdown(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the markdown value before it's validated."""
# pylint: disable=unused-argument
if "markdown" not in data:
@ -130,7 +138,9 @@ class TopicSchema(Schema):
return new_data
@pre_load
def prepare_link(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_link(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the link value before it's validated."""
# pylint: disable=unused-argument
if "link" not in data:
@ -157,7 +167,9 @@ class TopicSchema(Schema):
return new_data
@validates_schema
def link_or_markdown(self, data: dict, many: bool, partial: Any) -> None:
def link_or_markdown(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> None:
"""Fail validation unless at least one of link or markdown were set."""
# pylint: disable=unused-argument
if "link" not in data and "markdown" not in data:

18
tildes/tildes/schemas/user.py

@ -9,6 +9,7 @@ from typing import Any
from marshmallow import post_dump, pre_load, Schema, validates, validates_schema
from marshmallow.exceptions import ValidationError
from marshmallow.fields import DateTime, Email, String
from marshmallow.types import UnknownOption
from marshmallow.validate import Length, Regexp
from tildes.lib.password import is_breached_password
@ -78,7 +79,7 @@ class UserSchema(Schema):
@validates_schema
def username_pass_not_substrings(
self, data: dict, many: bool, partial: Any
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> None:
"""Ensure the username isn't in the password and vice versa."""
# pylint: disable=unused-argument
@ -97,11 +98,12 @@ class UserSchema(Schema):
raise ValidationError("Username cannot contain password")
@validates("password")
def password_not_breached(self, value: str) -> None:
def password_not_breached(self, value: str, data_key: str) -> None:
"""Validate that the password is not in the breached-passwords list.
Requires check_breached_passwords be True in the schema's context.
"""
# pylint: disable=unused-argument
if not TildesSchemaContext.get(default=TildesContext()).get(
"check_breached_passwords"
):
@ -114,7 +116,9 @@ class UserSchema(Schema):
)
@pre_load
def username_trim_whitespace(self, data: dict, many: bool, partial: Any) -> dict:
def username_trim_whitespace(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Trim leading/trailing whitespace around the username.
Requires username_trim_whitespace be True in the schema's context.
@ -135,7 +139,9 @@ class UserSchema(Schema):
return new_data
@pre_load
def prepare_email_address(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_email_address(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the email address value before it's validated."""
# pylint: disable=unused-argument
if "email_address" not in data:
@ -153,7 +159,9 @@ class UserSchema(Schema):
return new_data
@pre_load
def prepare_bio_markdown(self, data: dict, many: bool, partial: Any) -> dict:
def prepare_bio_markdown(
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict:
"""Prepare the bio_markdown value before it's validated."""
# pylint: disable=unused-argument
if "bio_markdown" not in data:

Loading…
Cancel
Save