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 3 months ago
committed by talklittle
parent
commit
e06fd4c0c2
  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 import pre_load, Schema, validates
from marshmallow.exceptions import ValidationError from marshmallow.exceptions import ValidationError
from marshmallow.fields import DateTime from marshmallow.fields import DateTime
from marshmallow.types import UnknownOption
from tildes.schemas.context import TildesSchemaContext, TildesContext from tildes.schemas.context import TildesSchemaContext, TildesContext
from tildes.schemas.fields import Ltree, Markdown, SimpleString from tildes.schemas.fields import Ltree, Markdown, SimpleString
@ -42,7 +43,9 @@ class GroupSchema(Schema):
sidebar_markdown = Markdown(allow_none=True) sidebar_markdown = Markdown(allow_none=True)
@pre_load @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.""" """Prepare the path value before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if not TildesSchemaContext.get(default=TildesContext()).get( if not TildesSchemaContext.get(default=TildesContext()).get(
@ -60,8 +63,9 @@ class GroupSchema(Schema):
return new_data return new_data
@validates("path") @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.""" """Validate the path field, raising an error if an issue exists."""
# pylint: disable=unused-argument
# check each element for length and against validity regex # check each element for length and against validity regex
path_elements = value.path.split(".") path_elements = value.path.split(".")
for element in path_elements: for element in path_elements:
@ -72,7 +76,9 @@ class GroupSchema(Schema):
raise ValidationError("Path element %s is invalid" % element) raise ValidationError("Path element %s is invalid" % element)
@pre_load @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.""" """Prepare the sidebar_markdown value before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "sidebar_markdown" not in data: 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 import pre_load, Schema, validates_schema, ValidationError
from marshmallow.fields import Boolean, Integer from marshmallow.fields import Boolean, Integer
from marshmallow.types import UnknownOption
from marshmallow.validate import Range from marshmallow.validate import Range
from tildes.enums import TopicSortOption from tildes.enums import TopicSortOption
@ -21,7 +22,9 @@ class PaginatedListingSchema(Schema):
per_page = Integer(validate=Range(min=1, max=100), load_default=50) per_page = Integer(validate=Range(min=1, max=100), load_default=50)
@validates_schema @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.""" """Fail validation if both after and before were specified."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if data.get("after") and data.get("before"): if data.get("after") and data.get("before"):
@ -40,7 +43,7 @@ class TopicListingSchema(PaginatedListingSchema):
@pre_load @pre_load
def reset_rank_start_on_first_page( def reset_rank_start_on_first_page(
self, data: dict, many: bool, partial: Any
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> dict: ) -> dict:
"""Reset rank_start to 1 if this is a first page (no before/after).""" """Reset rank_start to 1 if this is a first page (no before/after)."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -66,7 +69,7 @@ class MixedListingSchema(PaginatedListingSchema):
@pre_load @pre_load
def set_anchor_type_from_before_or_after( 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: ) -> dict:
"""Set the anchor_type if before or after has a special value indicating type. """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 import pre_load, Schema, validates, validates_schema, ValidationError
from marshmallow.fields import DateTime, List, Nested, String, URL 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.lib.url_transform import apply_url_transformations
from tildes.schemas.fields import Enum, ID36, Markdown, SimpleString from tildes.schemas.fields import Enum, ID36, Markdown, SimpleString
@ -36,7 +37,9 @@ class TopicSchema(Schema):
group = Nested(GroupSchema, dump_only=True) group = Nested(GroupSchema, dump_only=True)
@pre_load @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.""" """Prepare the title before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "title" not in data: if "title" not in data:
@ -56,7 +59,9 @@ class TopicSchema(Schema):
return new_data return new_data
@pre_load @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.""" """Prepare the tags before they're validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "tags" not in data: if "tags" not in data:
@ -98,7 +103,7 @@ class TopicSchema(Schema):
return new_data return new_data
@validates("tags") @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. """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 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 between groups and tags. For example, a popular tag in a group could be
converted into a sub-group easily. converted into a sub-group easily.
""" """
# pylint: disable=unused-argument
group_schema = GroupSchema(partial=True) group_schema = GroupSchema(partial=True)
for tag in value: for tag in value:
try: try:
@ -115,7 +121,9 @@ class TopicSchema(Schema):
raise ValidationError("Tag %s is invalid" % tag) from exc raise ValidationError("Tag %s is invalid" % tag) from exc
@pre_load @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.""" """Prepare the markdown value before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "markdown" not in data: if "markdown" not in data:
@ -130,7 +138,9 @@ class TopicSchema(Schema):
return new_data return new_data
@pre_load @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.""" """Prepare the link value before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "link" not in data: if "link" not in data:
@ -157,7 +167,9 @@ class TopicSchema(Schema):
return new_data return new_data
@validates_schema @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.""" """Fail validation unless at least one of link or markdown were set."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "link" not in data and "markdown" not in data: 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 import post_dump, pre_load, Schema, validates, validates_schema
from marshmallow.exceptions import ValidationError from marshmallow.exceptions import ValidationError
from marshmallow.fields import DateTime, Email, String from marshmallow.fields import DateTime, Email, String
from marshmallow.types import UnknownOption
from marshmallow.validate import Length, Regexp from marshmallow.validate import Length, Regexp
from tildes.lib.password import is_breached_password from tildes.lib.password import is_breached_password
@ -78,7 +79,7 @@ class UserSchema(Schema):
@validates_schema @validates_schema
def username_pass_not_substrings( def username_pass_not_substrings(
self, data: dict, many: bool, partial: Any
self, data: dict, many: bool, partial: Any, unknown: UnknownOption
) -> None: ) -> None:
"""Ensure the username isn't in the password and vice versa.""" """Ensure the username isn't in the password and vice versa."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
@ -97,11 +98,12 @@ class UserSchema(Schema):
raise ValidationError("Username cannot contain password") raise ValidationError("Username cannot contain password")
@validates("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. """Validate that the password is not in the breached-passwords list.
Requires check_breached_passwords be True in the schema's context. Requires check_breached_passwords be True in the schema's context.
""" """
# pylint: disable=unused-argument
if not TildesSchemaContext.get(default=TildesContext()).get( if not TildesSchemaContext.get(default=TildesContext()).get(
"check_breached_passwords" "check_breached_passwords"
): ):
@ -114,7 +116,9 @@ class UserSchema(Schema):
) )
@pre_load @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. """Trim leading/trailing whitespace around the username.
Requires username_trim_whitespace be True in the schema's context. Requires username_trim_whitespace be True in the schema's context.
@ -135,7 +139,9 @@ class UserSchema(Schema):
return new_data return new_data
@pre_load @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.""" """Prepare the email address value before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "email_address" not in data: if "email_address" not in data:
@ -153,7 +159,9 @@ class UserSchema(Schema):
return new_data return new_data
@pre_load @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.""" """Prepare the bio_markdown value before it's validated."""
# pylint: disable=unused-argument # pylint: disable=unused-argument
if "bio_markdown" not in data: if "bio_markdown" not in data:

Loading…
Cancel
Save