Repository: marshmallow-code/marshmallow
Pull request #2788 (2025-01-18 17:47:22)

Enable all ruff rules except those explicitly ignored


Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b1b64b8d..b7f373b3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,12 +2,12 @@ ci:
   autoupdate_schedule: monthly
 repos:
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.8.5
+  rev: v0.9.2
   hooks:
     - id: ruff
     - id: ruff-format
 - repo: https://github.com/python-jsonschema/check-jsonschema
-  rev: 0.30.0
+  rev: 0.31.0
   hooks:
     - id: check-github-workflows
     - id: check-readthedocs
diff --git a/docs/conf.py b/docs/conf.py
index ce68fdec..6e9354a4 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -22,7 +22,7 @@ source_suffix = ".rst"
 master_doc = "index"
 
 project = "marshmallow"
-copyright = "Steven Loria and contributors"
+copyright = "Steven Loria and contributors"  # noqa: A001
 
 version = release = importlib.metadata.version("marshmallow")
 
diff --git a/examples/flask_example.py b/examples/flask_example.py
index 12cdb2c0..015d5485 100644
--- a/examples/flask_example.py
+++ b/examples/flask_example.py
@@ -41,7 +41,7 @@ class Quote(db.Model):  # type: ignore[name-defined]
     id: Mapped[int] = mapped_column(primary_key=True)
     content: Mapped[str] = mapped_column(nullable=False)
     author_id: Mapped[int] = mapped_column(db.ForeignKey(Author.id))
-    author: Mapped[Author] = relationship(backref=db.backref("quotes", lazy="dynamic"))  # type: ignore[assignment]
+    author: Mapped[Author] = relationship(backref=db.backref("quotes", lazy="dynamic"))
     posted_at: Mapped[datetime.datetime]
 
 
@@ -77,7 +77,7 @@ class QuoteSchema(Schema):
         author_name = data.get("author")
         if author_name:
             first, last = author_name.split(" ")
-            author_dict = dict(first=first, last=last)
+            author_dict = {"first": first, "last": last}
         else:
             author_dict = {}
         data["author"] = author_dict
diff --git a/performance/benchmark.py b/performance/benchmark.py
index 98af1d1a..35af1c71 100644
--- a/performance/benchmark.py
+++ b/performance/benchmark.py
@@ -3,6 +3,7 @@
 Uses the `timeit` module to benchmark serializing an object through marshmallow.
 """
 
+# ruff: noqa: A002, T201
 import argparse
 import cProfile
 import datetime
@@ -81,7 +82,7 @@ class Quote:
         self.col_number = col_number
 
 
-def run_timeit(quotes, iterations, repeat, profile=False):
+def run_timeit(quotes, iterations, repeat, *, profile=False):
     quotes_schema = QuoteSchema(many=True)
     if profile:
         profile = cProfile.Profile()
@@ -100,8 +101,7 @@ def run_timeit(quotes, iterations, repeat, profile=False):
         profile.disable()
         profile.dump_stats("marshmallow.pprof")
 
-    usec = best * 1e6 / iterations / len(quotes)
-    return usec
+    return best * 1e6 / iterations / len(quotes)
 
 
 def main():
@@ -129,21 +129,19 @@ def main():
     )
     args = parser.parse_args()
 
-    quotes = []
-
-    for i in range(args.object_count):
-        quotes.append(
-            Quote(
-                i,
-                Author(i, "Foo", "Bar", 42, 66, "123 Fake St"),
-                "Hello World",
-                datetime.datetime(2019, 7, 4, tzinfo=datetime.timezone.utc),
-                "The World",
-                34,
-                3,
-                70,
-            )
+    quotes = [
+        Quote(
+            i,
+            Author(i, "Foo", "Bar", 42, 66, "123 Fake St"),
+            "Hello World",
+            datetime.datetime(2019, 7, 4, tzinfo=datetime.timezone.utc),
+            "The World",
+            34,
+            3,
+            70,
         )
+        for i in range(args.object_count)
+    ]
 
     print(
         f"Benchmark Result: {run_timeit(quotes, args.iterations, args.repeat, profile=args.profile):.2f} usec/dump"
diff --git a/pyproject.toml b/pyproject.toml
index 9d72765b..6bb927f4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -69,21 +69,60 @@ output-format = "full"
 docstring-code-format = true
 
 [tool.ruff.lint]
-ignore = ["E203", "E266", "E501", "E731"]
-select = [
-  "B",  # flake8-bugbear
-  "E",  # pycodestyle error
-  "F",  # pyflakes
-  "I",  # isort
-  "UP", # pyupgrade
-  "W",  # pycodestyle warning
+# use all checks available in ruff except the ones explicitly ignored below
+select = ["ALL"]
+ignore = [
+  "A005",    # "module {name} shadows a Python standard-library module"
+  "ANN",     # let mypy handle annotation checks
+  "ARG",     # unused arguments are common w/ interfaces
+  "COM",     # let formatter take care commas
+  "C901",    # don't enforce complexity level
+  "D",       # don't require docstrings
+  "DTZ007",  # ignore false positives due to https://github.com/astral-sh/ruff/issues/1306
+  "E501",    # leave line-length enforcement to formatter
+  "EM",      # allow string messages in exceptions
+  "FIX",     # allow "FIX" comments in code
+  "INP001",  # allow Python files outside of packages
+  "N806",    # allow uppercase variable names for variables that are classes
+  "PERF203", # allow try-except within loops
+  "PLR0913", # "Too many arguments"
+  "PLR0912", # "Too many branches"
+  "PLR2004", # "Magic value used in comparison"
+  "PTH",     # don't require using pathlib instead of os
+  "RUF012",  # allow mutable class variables
+  "SIM102",  # Sometimes nested ifs are more readable than if...and...
+  "SIM105",  # "Use `contextlib.suppress(...)` instead of `try`-`except`-`pass`"
+  "SIM108",  # sometimes if-else is more readable than a ternary
+  "TD",      # allow TODO comments to be whatever we want
+  "TRY003",  # allow long messages passed to exceptions
+  "TRY004",  # allow ValueError for invalid argument types
 ]
 
 [tool.ruff.lint.per-file-ignores]
-"tests/*" = ["E721"]
+"tests/*" = [
+  "ARG",     # unused arguments are fine in tests
+  "C408",    # allow dict() instead of dict literal
+  "DTZ",     # allow naive datetimes
+  "FBT003",  # allow boolean positional argument
+  "N803",    # fixture names might be uppercase
+  "PLR0915", # allow lots of statements
+  "PT007",   # ignore false positives due to https://github.com/astral-sh/ruff/issues/14743
+  "PT011",   # don't require match when using pytest.raises
+  "S",       # allow asserts
+  "SIM117",  # allow nested with statements because it's more readable sometimes
+  "SLF001",  # allow private attribute access
+]
+"examples/*" = [
+  "S", # allow asserts
+  "T", # allow prints
+]
+"src/marshmallow/orderedset.py" = [
+  "FBT002", # allow boolean positional argument
+  "T",      # allow prints
+]
 
 [tool.mypy]
-files = ["src", "tests"]
+files = ["src", "tests", "examples"]
 ignore_missing_imports = true
 warn_unreachable = true
 warn_unused_ignores = true
diff --git a/src/marshmallow/__init__.py b/src/marshmallow/__init__.py
index be32c096..85b0f3c0 100644
--- a/src/marshmallow/__init__.py
+++ b/src/marshmallow/__init__.py
@@ -68,14 +68,14 @@ __all__ = [
     "RAISE",
     "Schema",
     "SchemaOpts",
+    "ValidationError",
     "fields",
-    "validates",
-    "validates_schema",
-    "pre_dump",
+    "missing",
     "post_dump",
-    "pre_load",
     "post_load",
     "pprint",
-    "ValidationError",
-    "missing",
+    "pre_dump",
+    "pre_load",
+    "validates",
+    "validates_schema",
 ]
diff --git a/src/marshmallow/class_registry.py b/src/marshmallow/class_registry.py
index ddc1ae29..6c02f9cd 100644
--- a/src/marshmallow/class_registry.py
+++ b/src/marshmallow/class_registry.py
@@ -7,6 +7,7 @@ class:`fields.Nested <marshmallow.fields.Nested>`.
     This module is treated as private API.
     Users should not need to use this module directly.
 """
+# ruff: noqa: ERA001
 
 from __future__ import annotations
 
@@ -49,7 +50,7 @@ def register(classname: str, cls: SchemaType) -> None:
     module = cls.__module__
     # Full module path to the class
     # e.g. user.schemas.UserSchema
-    fullpath = ".".join([module, classname])
+    fullpath = f"{module}.{classname}"
     # If the class is already registered; need to check if the entries are
     # in the same module as cls to avoid having multiple instances of the same
     # class in the registry
@@ -66,18 +67,19 @@ def register(classname: str, cls: SchemaType) -> None:
     else:
         # If fullpath does exist, replace existing entry
         _registry[fullpath] = [cls]
-    return None
 
 
 @typing.overload
-def get_class(classname: str, all: typing.Literal[False] = ...) -> SchemaType: ...
+def get_class(classname: str, *, all: typing.Literal[False] = ...) -> SchemaType: ...
 
 
 @typing.overload
-def get_class(classname: str, all: typing.Literal[True] = ...) -> list[SchemaType]: ...
+def get_class(
+    classname: str, *, all: typing.Literal[True] = ...
+) -> list[SchemaType]: ...
 
 
-def get_class(classname: str, all: bool = False) -> list[SchemaType] | SchemaType:
+def get_class(classname: str, *, all: bool = False) -> list[SchemaType] | SchemaType:  # noqa: A002
     """Retrieve a class from the registry.
 
     :raises: `marshmallow.exceptions.RegistryError` if the class cannot be found
@@ -98,5 +100,4 @@ def get_class(classname: str, all: bool = False) -> list[SchemaType] | SchemaTyp
             "were found. Please use the full, "
             "module-qualified path."
         )
-    else:
-        return _registry[classname][0]
+    return _registry[classname][0]
diff --git a/src/marshmallow/decorators.py b/src/marshmallow/decorators.py
index ad4955d5..eefba6c1 100644
--- a/src/marshmallow/decorators.py
+++ b/src/marshmallow/decorators.py
@@ -93,9 +93,9 @@ def validates(field_name: str) -> Callable[..., Any]:
 
 def validates_schema(
     fn: Callable[..., Any] | None = None,
-    pass_many: bool = False,
-    pass_original: bool = False,
-    skip_on_field_errors: bool = True,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+    pass_original: bool = False,  # noqa: FBT001, FBT002
+    skip_on_field_errors: bool = True,  # noqa: FBT001, FBT002
 ) -> Callable[..., Any]:
     """Register a schema-level validator.
 
@@ -126,7 +126,8 @@ def validates_schema(
 
 
 def pre_dump(
-    fn: Callable[..., Any] | None = None, pass_many: bool = False
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
 ) -> Callable[..., Any]:
     """Register a method to invoke before serializing an object. The method
     receives the object to be serialized and returns the processed object.
@@ -143,8 +144,8 @@ def pre_dump(
 
 def post_dump(
     fn: Callable[..., Any] | None = None,
-    pass_many: bool = False,
-    pass_original: bool = False,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+    pass_original: bool = False,  # noqa: FBT001, FBT002
 ) -> Callable[..., Any]:
     """Register a method to invoke after serializing an object. The method
     receives the serialized object and returns the processed object.
@@ -163,7 +164,8 @@ def post_dump(
 
 
 def pre_load(
-    fn: Callable[..., Any] | None = None, pass_many: bool = False
+    fn: Callable[..., Any] | None = None,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
 ) -> Callable[..., Any]:
     """Register a method to invoke before deserializing an object. The method
     receives the data to be deserialized and returns the processed data.
@@ -181,8 +183,8 @@ def pre_load(
 
 def post_load(
     fn: Callable[..., Any] | None = None,
-    pass_many: bool = False,
-    pass_original: bool = False,
+    pass_many: bool = False,  # noqa: FBT001, FBT002
+    pass_original: bool = False,  # noqa: FBT001, FBT002
 ) -> Callable[..., Any]:
     """Register a method to invoke after deserializing an object. The method
     receives the deserialized data and returns the processed data.
@@ -202,7 +204,10 @@ def post_load(
 
 
 def set_hook(
-    fn: Callable[..., Any] | None, tag: str, many: bool = False, **kwargs: Any
+    fn: Callable[..., Any] | None,
+    tag: str,
+    many: bool = False,  # noqa: FBT001, FBT002
+    **kwargs: Any,
 ) -> Callable[..., Any]:
     """Mark decorated function as a hook to be picked up later.
     You should not need to use this method directly.
diff --git a/src/marshmallow/error_store.py b/src/marshmallow/error_store.py
index 72b70373..61320ab2 100644
--- a/src/marshmallow/error_store.py
+++ b/src/marshmallow/error_store.py
@@ -25,7 +25,7 @@ class ErrorStore:
         self.errors = merge_errors(self.errors, messages)
 
 
-def merge_errors(errors1, errors2):
+def merge_errors(errors1, errors2):  # noqa: PLR0911
     """Deeply merge two error messages.
 
     The format of ``errors1`` and ``errors2`` matches the ``message``
@@ -40,7 +40,7 @@ def merge_errors(errors1, errors2):
             return errors1 + errors2
         if isinstance(errors2, dict):
             return dict(errors2, **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))})
-        return errors1 + [errors2]
+        return [*errors1, errors2]
     if isinstance(errors1, dict):
         if isinstance(errors2, list):
             return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)})
@@ -54,7 +54,7 @@ def merge_errors(errors1, errors2):
             return errors
         return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA), errors2)})
     if isinstance(errors2, list):
-        return [errors1] + errors2
+        return [errors1, *errors2]
     if isinstance(errors2, dict):
         return dict(errors2, **{SCHEMA: merge_errors(errors1, errors2.get(SCHEMA))})
     return [errors1, errors2]
diff --git a/src/marshmallow/fields.py b/src/marshmallow/fields.py
index 8544eb77..0f0e735a 100644
--- a/src/marshmallow/fields.py
+++ b/src/marshmallow/fields.py
@@ -1,3 +1,4 @@
+# ruff: noqa: F841, SLF001
 from __future__ import annotations
 
 import collections
@@ -11,7 +12,6 @@ import typing
 import uuid
 import warnings
 from collections.abc import Mapping as _Mapping
-from enum import Enum as EnumType
 
 from marshmallow import class_registry, types, utils, validate
 from marshmallow.base import FieldABC
@@ -35,47 +35,49 @@ from marshmallow.warnings import (
 )
 
 if typing.TYPE_CHECKING:
+    from enum import Enum as EnumType
+
     from marshmallow.schema import Schema, SchemaMeta
 
 
 __all__ = [
-    "Field",
-    "Raw",
-    "Nested",
-    "Mapping",
-    "Dict",
-    "List",
-    "Tuple",
-    "String",
+    "IP",
+    "URL",
     "UUID",
-    "Number",
-    "Integer",
-    "Decimal",
-    "Boolean",
-    "Float",
-    "DateTime",
-    "NaiveDateTime",
     "AwareDateTime",
-    "Time",
+    "Bool",
+    "Boolean",
+    "Constant",
     "Date",
-    "TimeDelta",
-    "Url",
-    "URL",
+    "DateTime",
+    "Decimal",
+    "Dict",
     "Email",
-    "IP",
-    "IPv4",
-    "IPv6",
+    "Enum",
+    "Field",
+    "Float",
+    "Function",
     "IPInterface",
+    "IPv4",
     "IPv4Interface",
+    "IPv6",
     "IPv6Interface",
-    "Enum",
-    "Method",
-    "Function",
-    "Str",
-    "Bool",
     "Int",
-    "Constant",
+    "Integer",
+    "List",
+    "Mapping",
+    "Method",
+    "NaiveDateTime",
+    "Nested",
+    "Number",
     "Pluck",
+    "Raw",
+    "Str",
+    "String",
+    "Time",
+    "TimeDelta",
+    "Tuple",
+    "Url",
 ]
 
 
@@ -498,7 +500,9 @@ class Nested(Field):
             name = fields.Str()
             # Use lambda functions when you need two-way nesting or self-nesting
             parent = fields.Nested(lambda: ParentSchema(only=("id",)), dump_only=True)
-            siblings = fields.List(fields.Nested(lambda: ChildSchema(only=("id", "name"))))
+            siblings = fields.List(
+                fields.Nested(lambda: ChildSchema(only=("id", "name")))
+            )
 
 
         class ParentSchema(Schema):
@@ -843,7 +847,7 @@ class Tuple(Field):
         super().__init__(**kwargs)
         if not utils.is_collection(tuple_fields):
             raise ValueError(
-                "tuple_fields must be an iterable of Field classes or " "instances."
+                "tuple_fields must be an iterable of Field classes or instances."
             )
 
         try:
@@ -863,9 +867,9 @@ class Tuple(Field):
         super()._bind_to_schema(field_name, schema)
         new_tuple_fields = []
         for field in self.tuple_fields:
-            field = copy.deepcopy(field)
-            field._bind_to_schema(field_name, self)
-            new_tuple_fields.append(field)
+            new_field = copy.deepcopy(field)
+            new_field._bind_to_schema(field_name, self)
+            new_tuple_fields.append(new_field)
 
         self.tuple_fields = new_tuple_fields
 
@@ -1294,7 +1298,7 @@ class DateTime(Field):
         "format": '"{input}" cannot be formatted as a {obj_type}.',
     }
 
-    def __init__(self, format: str | None = None, **kwargs) -> None:
+    def __init__(self, format: str | None = None, **kwargs) -> None:  # noqa: A002
         super().__init__(**kwargs)
         # Allow this to be None. It may be set later in the ``_serialize``
         # or ``_deserialize`` methods. This allows a Schema to dynamically set the
@@ -1352,7 +1356,7 @@ class NaiveDateTime(DateTime):
 
     def __init__(
         self,
-        format: str | None = None,
+        format: str | None = None,  # noqa: A002
         *,
         timezone: dt.timezone | None = None,
         **kwargs,
@@ -1389,7 +1393,7 @@ class AwareDateTime(DateTime):
 
     def __init__(
         self,
-        format: str | None = None,
+        format: str | None = None,  # noqa: A002
         *,
         default_timezone: dt.tzinfo | None = None,
         **kwargs,
@@ -1548,7 +1552,7 @@ class TimeDelta(Field):
             delta = utils.timedelta_to_microseconds(value)
             unit = utils.timedelta_to_microseconds(base_unit)
             return delta // unit
-        assert self.serialization_type is float
+        assert self.serialization_type is float  # noqa: S101
         return value.total_seconds() / base_unit.total_seconds()
 
     def _deserialize(self, value, attr, data, **kwargs):
@@ -1643,16 +1647,15 @@ class Mapping(Field):
         if not self.value_field and not self.key_field:
             return self.mapping_type(value)
 
-        #  Serialize keys
+        # Serialize keys
         if self.key_field is None:
-            keys = {k: k for k in value.keys()}
+            keys = {k: k for k in value}
         else:
             keys = {
-                k: self.key_field._serialize(k, None, None, **kwargs)
-                for k in value.keys()
+                k: self.key_field._serialize(k, None, None, **kwargs) for k in value
             }
 
-        #  Serialize values
+        # Serialize values
         result = self.mapping_type()
         if self.value_field is None:
             for k, v in value.items():
@@ -1672,18 +1675,18 @@ class Mapping(Field):
 
         errors = collections.defaultdict(dict)
 
-        #  Deserialize keys
+        # Deserialize keys
         if self.key_field is None:
-            keys = {k: k for k in value.keys()}
+            keys = {k: k for k in value}
         else:
             keys = {}
-            for key in value.keys():
+            for key in value:
                 try:
                     keys[key] = self.key_field.deserialize(key, **kwargs)
                 except ValidationError as error:
                     errors[key]["key"] = error.messages
 
-        #  Deserialize values
+        # Deserialize values
         result = self.mapping_type()
         if self.value_field is None:
             for k, v in value.items():
diff --git a/src/marshmallow/orderedset.py b/src/marshmallow/orderedset.py
index 7ce0723a..95fbc60f 100644
--- a/src/marshmallow/orderedset.py
+++ b/src/marshmallow/orderedset.py
@@ -45,7 +45,7 @@ class OrderedSet(MutableSet):
 
     def discard(self, key):
         if key in self.map:
-            key, prev, next = self.map.pop(key)
+            key, prev, next = self.map.pop(key)  # noqa: A001
             prev[2] = next
             next[1] = prev
 
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
index 1d73c725..6009a436 100644
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -5,8 +5,10 @@ from __future__ import annotations
 import copy
 import datetime as dt
 import decimal
+import functools
 import inspect
 import json
+import operator
 import typing
 import uuid
 import warnings
@@ -26,7 +28,6 @@ from marshmallow.decorators import (
 )
 from marshmallow.error_store import ErrorStore
 from marshmallow.exceptions import StringNotCollectionError, ValidationError
-from marshmallow.fields import Field
 from marshmallow.orderedset import OrderedSet
 from marshmallow.utils import (
     EXCLUDE,
@@ -41,6 +42,9 @@ from marshmallow.utils import (
 )
 from marshmallow.warnings import RemovedInMarshmallow4Warning
 
+if typing.TYPE_CHECKING:
+    from marshmallow.fields import Field
+
 
 def _get_fields(attrs) -> list[tuple[str, Field]]:
     """Get fields from a class
@@ -64,8 +68,11 @@ def _get_fields_by_mro(klass: SchemaMeta):
     :param klass: Class whose fields to retrieve
     """
     mro = inspect.getmro(klass)
+    # Combine fields from all parents
+    # functools.reduce(operator.iadd, list_of_lists) is faster than sum(list_of_lists, [])
     # Loop over mro in reverse to maintain correct order of fields
-    return sum(
+    return functools.reduce(
+        operator.iadd,
         (
             _get_fields(
                 getattr(base, "_declared_fields", base.__dict__),
@@ -89,7 +96,10 @@ class SchemaMeta(ABCMeta):
     _declared_fields: dict[str, Field]
 
     def __new__(
-        mcs, name: str, bases: tuple[type, ...], attrs: dict[str, typing.Any]
+        mcs,  # noqa: N804
+        name: str,
+        bases: tuple[type, ...],
+        attrs: dict[str, typing.Any],
     ) -> SchemaMeta:
         meta = attrs.get("Meta")
         ordered = getattr(meta, "ordered", False)
@@ -120,7 +130,7 @@ class SchemaMeta(ABCMeta):
         cls_fields += list(klass.opts.include.items())
 
         # Assign _declared_fields on class
-        klass._declared_fields = mcs.get_declared_fields(
+        klass._declared_fields = mcs.get_declared_fields(  # noqa: SLF001
             klass=klass,
             cls_fields=cls_fields,
             inherited_fields=inherited_fields,
@@ -130,7 +140,7 @@ class SchemaMeta(ABCMeta):
 
     @classmethod
     def get_declared_fields(
-        mcs,
+        mcs,  # noqa: N804
         klass: SchemaMeta,
         cls_fields: list[tuple[str, Field]],
         inherited_fields: list[tuple[str, Field]],
@@ -201,7 +211,7 @@ class SchemaMeta(ABCMeta):
 class SchemaOpts:
     """Defines defaults for `marshmallow.Schema.Meta`."""
 
-    def __init__(self, meta: type, ordered: bool = False):
+    def __init__(self, meta: type, ordered: bool = False):  # noqa: FBT001, FBT002
         self.fields = getattr(meta, "fields", ())
         if not isinstance(self.fields, (list, tuple)):
             raise ValueError("`fields` option must be a list or tuple.")
@@ -210,8 +220,7 @@ class SchemaOpts:
             raise ValueError("`additional` option must be a list or tuple.")
         if self.fields and self.additional:
             raise ValueError(
-                "Cannot set both `fields` and `additional` options"
-                " for the same Schema."
+                "Cannot set both `fields` and `additional` options for the same Schema."
             )
         self.exclude = getattr(meta, "exclude", ())
         if not isinstance(self.exclude, (list, tuple)):
@@ -483,8 +492,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         """`dict` type to return when serializing."""
         if self.ordered:
             return OrderedDict
-        else:
-            return dict
+        return dict
 
     @classmethod
     def from_dict(
@@ -515,8 +523,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         Meta = type(
             "GeneratedMeta", (getattr(cls, "Meta", object),), {"register": False}
         )
-        schema_cls = type(name, (cls,), {**fields.copy(), "Meta": Meta})
-        return schema_cls
+        return type(name, (cls,), {**fields.copy(), "Meta": Meta})
 
     ##### Override-able methods #####
 
@@ -533,7 +540,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         .. versionchanged:: 3.0.0rc9
             Receives `many` and `partial` (on deserialization) as keyword arguments.
         """
-        pass
 
     def get_attribute(self, obj: typing.Any, attr: str, default: typing.Any):
         """Defines how to pull values from an object to serialize.
@@ -1087,7 +1093,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
 
         No-op by default.
         """
-        return None
+        return
 
     def _bind_field(self, field_name: str, field_obj: Field) -> None:
         """Bind field to the schema, setting any necessary attributes on the
@@ -1101,7 +1107,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         if field_name in self.dump_only:
             field_obj.dump_only = True
         try:
-            field_obj._bind_to_schema(field_name, self)
+            field_obj._bind_to_schema(field_name, self)  # noqa: SLF001
         except TypeError as error:
             # Field declared as a class, not an instance. Ignore type checking because
             # we handle unsupported arg types, i.e. this is dead code from
@@ -1110,10 +1116,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
                 msg = (
                     f'Field for "{field_name}" must be declared as a '
                     "Field instance, not a class. "
-                    f'Did you mean "fields.{field_obj.__name__}()"?'  # type: ignore
+                    f'Did you mean "fields.{field_obj.__name__}()"?'  # type: ignore[attr-defined]
                 )
                 raise TypeError(msg) from error
-            raise error
+            raise
         self.on_bind_field(field_name, field_obj)
 
     def _invoke_dump_processors(
@@ -1125,10 +1131,9 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
         data = self._invoke_processors(
             tag, pass_many=False, data=data, many=many, original_data=original_data
         )
-        data = self._invoke_processors(
+        return self._invoke_processors(
             tag, pass_many=True, data=data, many=many, original_data=original_data
         )
-        return data
 
     def _invoke_load_processors(
         self,
@@ -1149,7 +1154,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
             original_data=original_data,
             partial=partial,
         )
-        data = self._invoke_processors(
+        return self._invoke_processors(
             tag,
             pass_many=False,
             data=data,
@@ -1157,7 +1162,6 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
             original_data=original_data,
             partial=partial,
         )
-        return data
 
     def _invoke_field_validators(self, *, error_store: ErrorStore, data, many: bool):
         for attr_name, _, validator_kwargs in self._hooks[VALIDATES]:
@@ -1189,7 +1193,7 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
                             index=(idx if self.opts.index_errors else None),
                         )
                         if validated_value is missing:
-                            data[idx].pop(field_name, None)
+                            item.pop(field_name, None)
             else:
                 try:
                     value = data[field_obj.attribute or field_name]
@@ -1272,11 +1276,10 @@ class Schema(base.SchemaABC, metaclass=SchemaMeta):
                     ]
                 else:
                     data = [processor(item, many=many, **kwargs) for item in data]
+            elif pass_original:
+                data = processor(data, original_data, many=many, **kwargs)
             else:
-                if pass_original:
-                    data = processor(data, original_data, many=many, **kwargs)
-                else:
-                    data = processor(data, many=many, **kwargs)
+                data = processor(data, many=many, **kwargs)
         return data
 
 
diff --git a/src/marshmallow/utils.py b/src/marshmallow/utils.py
index 1f262c86..df3cd294 100644
--- a/src/marshmallow/utils.py
+++ b/src/marshmallow/utils.py
@@ -1,5 +1,6 @@
 """Utility methods for marshmallow."""
 
+# ruff: noqa: T201, T203
 from __future__ import annotations
 
 import collections
@@ -137,7 +138,7 @@ _iso8601_time_re = re.compile(
 )
 
 
-def get_fixed_timezone(offset: int | float | dt.timedelta) -> dt.timezone:
+def get_fixed_timezone(offset: float | dt.timedelta) -> dt.timezone:
     """Return a tzinfo instance with a fixed offset from UTC."""
     if isinstance(offset, dt.timedelta):
         offset = offset.total_seconds() // 60
@@ -169,7 +170,7 @@ def from_iso_datetime(value):
         tzinfo = get_fixed_timezone(offset)
     kw = {k: int(v) for k, v in kw.items() if v is not None}
     kw["tzinfo"] = tzinfo
-    return dt.datetime(**kw)
+    return dt.datetime(**kw)  # noqa: DTZ001
 
 
 def from_iso_time(value):
@@ -279,17 +280,15 @@ def get_value(obj, key: int | str, default=missing):
     """
     if not isinstance(key, int) and "." in key:
         return _get_value_for_keys(obj, key.split("."), default)
-    else:
-        return _get_value_for_key(obj, key, default)
+    return _get_value_for_key(obj, key, default)
 
 
 def _get_value_for_keys(obj, keys, default):
     if len(keys) == 1:
         return _get_value_for_key(obj, keys[0], default)
-    else:
-        return _get_value_for_keys(
-            _get_value_for_key(obj, keys[0], default), keys[1:], default
-        )
+    return _get_value_for_keys(
+        _get_value_for_key(obj, keys[0], default), keys[1:], default
+    )
 
 
 def _get_value_for_key(obj, key, default):
@@ -318,7 +317,7 @@ def set_value(dct: dict[str, typing.Any], key: str, value: typing.Any):
         target = dct.setdefault(head, {})
         if not isinstance(target, dict):
             raise ValueError(
-                f"Cannot set {key} in {head} " f"due to existing value: {target}"
+                f"Cannot set {key} in {head} due to existing value: {target}"
             )
         set_value(target, rest, value)
     else:
@@ -360,10 +359,9 @@ def resolve_field_instance(cls_or_instance: type[Field] | Field) -> Field:
         if not issubclass(cls_or_instance, FieldABC):
             raise FieldInstanceResolutionError
         return cls_or_instance()
-    else:
-        if not isinstance(cls_or_instance, FieldABC):
-            raise FieldInstanceResolutionError
-        return cls_or_instance
+    if not isinstance(cls_or_instance, FieldABC):
+        raise FieldInstanceResolutionError
+    return cls_or_instance
 
 
 def timedelta_to_microseconds(value: dt.timedelta) -> int:
diff --git a/src/marshmallow/validate.py b/src/marshmallow/validate.py
index 1d2d8f77..91d51d83 100644
--- a/src/marshmallow/validate.py
+++ b/src/marshmallow/validate.py
@@ -9,10 +9,12 @@ from abc import ABC, abstractmethod
 from itertools import zip_longest
 from operator import attrgetter
 
-from marshmallow import types
 from marshmallow.exceptions import ValidationError
 from marshmallow.warnings import ChangedInMarshmallow4Warning
 
+if typing.TYPE_CHECKING:
+    from marshmallow import types
+
 _T = typing.TypeVar("_T")
 
 
@@ -85,7 +87,7 @@ class And(Validator):
                         ChangedInMarshmallow4Warning,
                         stacklevel=2,
                     )
-                    raise ValidationError(self.error)
+                    raise ValidationError(self.error)  # noqa: TRY301
             except ValidationError as err:
                 kwargs.update(err.kwargs)
                 if isinstance(err.messages, dict):
@@ -115,7 +117,7 @@ class URL(Validator):
             self._memoized = {}
 
         def _regex_generator(
-            self, relative: bool, absolute: bool, require_tld: bool
+            self, *, relative: bool, absolute: bool, require_tld: bool
         ) -> typing.Pattern:
             hostname_variants = [
                 # a normal domain name, expressed in [A-Z0-9] chars with hyphens allowed only in the middle
@@ -169,12 +171,12 @@ class URL(Validator):
             return re.compile("".join(parts), re.IGNORECASE)
 
         def __call__(
-            self, relative: bool, absolute: bool, require_tld: bool
+            self, *, relative: bool, absolute: bool, require_tld: bool
         ) -> typing.Pattern:
             key = (relative, absolute, require_tld)
             if key not in self._memoized:
                 self._memoized[key] = self._regex_generator(
-                    relative, absolute, require_tld
+                    relative=relative, absolute=absolute, require_tld=require_tld
                 )
 
             return self._memoized[key]
@@ -220,7 +222,9 @@ class URL(Validator):
             if scheme not in self.schemes:
                 raise ValidationError(message)
 
-        regex = self._regex(self.relative, self.absolute, self.require_tld)
+        regex = self._regex(
+            relative=self.relative, absolute=self.absolute, require_tld=self.require_tld
+        )
 
         if not regex.search(value):
             raise ValidationError(message)
@@ -318,8 +322,8 @@ class Range(Validator):
 
     def __init__(
         self,
-        min=None,
-        max=None,
+        min=None,  # noqa: A002
+        max=None,  # noqa: A002
         *,
         min_inclusive: bool = True,
         max_inclusive: bool = True,
@@ -387,8 +391,8 @@ class Length(Validator):
 
     def __init__(
         self,
-        min: int | None = None,
-        max: int | None = None,
+        min: int | None = None,  # noqa: A002
+        max: int | None = None,  # noqa: A002
         *,
         equal: int | None = None,
         error: str | None = None,


Test patch:
diff --git a/tests/base.py b/tests/base.py
index b14c7f6b..c5cd644b 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -84,6 +84,7 @@ class User:
     def __init__(
         self,
         name,
+        *,
         age=0,
         id_=None,
         homepage=None,
@@ -123,7 +124,7 @@ class User:
         self.relatives = []
         self.various_data = various_data or {
             "pets": ["cat", "dog"],
-            "address": "1600 Pennsylvania Ave\n" "Washington, DC 20006",
+            "address": "1600 Pennsylvania Ave\nWashington, DC 20006",
         }
 
     @property
@@ -166,6 +167,7 @@ class Uppercased(fields.Field):
     def _serialize(self, value, attr, obj):
         if value:
             return value.upper()
+        return None
 
 
 def get_lowername(obj):
@@ -173,8 +175,7 @@ def get_lowername(obj):
         return missing
     if isinstance(obj, dict):
         return obj.get("name").lower()
-    else:
-        return obj.name.lower()
+    return obj.name.lower()
 
 
 class UserSchema(Schema):
@@ -343,7 +344,7 @@ class BlogSchemaOnlyExclude(BlogSchema):
     user = fields.Nested(UserSchema, only=("name",), exclude=("name", "species"))
 
 
-class mockjson:  # noqa
+class mockjson:  # noqa: N801
     @staticmethod
     def dumps(val):
         return b"{'foo': 42}"
diff --git a/tests/test_decorators.py b/tests/test_decorators.py
index fe4c33dc..a97bf591 100644
--- a/tests/test_decorators.py
+++ b/tests/test_decorators.py
@@ -75,8 +75,11 @@ def test_decorated_processors(partial_val):
     schema = ExampleSchema(partial=partial_val)
 
     # Need to re-create these because the processors will modify in place.
-    make_item = lambda: {"value": 3}
-    make_items = lambda: [make_item(), {"value": 5}]
+    def make_item():
+        return {"value": 3}
+
+    def make_items():
+        return [make_item(), {"value": 5}]
 
     item_dumped = schema.dump(make_item())
     assert item_dumped == {"datum": {"value": "TAG4"}}
@@ -89,7 +92,7 @@ def test_decorated_processors(partial_val):
     assert items_loaded == make_items()
 
 
-#  Regression test for https://github.com/marshmallow-code/marshmallow/issues/347
+# Regression test for https://github.com/marshmallow-code/marshmallow/issues/347
 @pytest.mark.parametrize("unknown", (EXCLUDE, INCLUDE, RAISE))
 def test_decorated_processor_returning_none(unknown):
     class PostSchema(Schema):
@@ -449,7 +452,7 @@ class TestValidatesSchemaDecorator:
 
     @pytest.mark.parametrize("data", ([{"foo": 1, "bar": 2}],))
     @pytest.mark.parametrize(
-        "pass_many,expected_data,expected_original_data",
+        ("pass_many", "expected_data", "expected_original_data"),
         (
             [True, [{"foo": 1}], [{"foo": 1, "bar": 2}]],
             [False, {"foo": 1}, {"foo": 1, "bar": 2}],
@@ -687,7 +690,7 @@ class TestValidatesSchemaDecorator:
         assert "_schema" not in errors
 
 
-def test_decorator_error_handling():  # noqa: C901
+def test_decorator_error_handling():
     class ExampleSchema(Schema):
         foo = fields.Int()
         bar = fields.Int()
@@ -829,7 +832,7 @@ example = Example(nested=[Nested(x) for x in range(1)])
 
 
 @pytest.mark.parametrize(
-    "data,expected_data,expected_original_data",
+    ("data", "expected_data", "expected_original_data"),
     ([example, {"foo": 0}, example.nested[0]],),
 )
 def test_decorator_post_dump_with_nested_original_and_pass_many(
@@ -863,7 +866,7 @@ def test_decorator_post_dump_with_nested_original_and_pass_many(
 
 
 @pytest.mark.parametrize(
-    "data,expected_data,expected_original_data",
+    ("data", "expected_data", "expected_original_data"),
     ([{"nested": [{"foo": 0}]}, {"foo": 0}, {"foo": 0}],),
 )
 def test_decorator_post_load_with_nested_original_and_pass_many(
diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py
index d93a832e..ea0e8eab 100644
--- a/tests/test_deserialization.py
+++ b/tests/test_deserialization.py
@@ -29,14 +29,14 @@ class MockDateTimeOverflowError(dt.datetime):
     """Used to simulate the possible OverflowError of datetime.fromtimestamp"""
 
     def fromtimestamp(self, *args, **kwargs):
-        raise OverflowError()
+        raise OverflowError
 
 
 class MockDateTimeOSError(dt.datetime):
     """Used to simulate the possible OSError of datetime.fromtimestamp"""
 
     def fromtimestamp(self, *args, **kwargs):
-        raise OSError()
+        raise OSError
 
 
 class TestDeserializingNone:
@@ -216,31 +216,38 @@ class TestFieldDeserialization:
 
         m1d = field.deserialize(m1)
         assert isinstance(m1d, decimal.Decimal)
-        assert m1d.is_qnan() and not m1d.is_signed()
+        assert m1d.is_qnan()
+        assert not m1d.is_signed()
 
         m2d = field.deserialize(m2)
         assert isinstance(m2d, decimal.Decimal)
-        assert m2d.is_qnan() and not m2d.is_signed()
+        assert m2d.is_qnan()
+        assert not m2d.is_signed()
 
         m3d = field.deserialize(m3)
         assert isinstance(m3d, decimal.Decimal)
-        assert m3d.is_qnan() and not m3d.is_signed()
+        assert m3d.is_qnan()
+        assert not m3d.is_signed()
 
         m4d = field.deserialize(m4)
         assert isinstance(m4d, decimal.Decimal)
-        assert m4d.is_qnan() and not m4d.is_signed()
+        assert m4d.is_qnan()
+        assert not m4d.is_signed()
 
         m5d = field.deserialize(m5)
         assert isinstance(m5d, decimal.Decimal)
-        assert m5d.is_infinite() and m5d.is_signed()
+        assert m5d.is_infinite()
+        assert m5d.is_signed()
 
         m6d = field.deserialize(m6)
         assert isinstance(m6d, decimal.Decimal)
-        assert m6d.is_infinite() and not m6d.is_signed()
+        assert m6d.is_infinite()
+        assert not m6d.is_signed()
 
         m7d = field.deserialize(m7)
         assert isinstance(m7d, decimal.Decimal)
-        assert m7d.is_zero() and m7d.is_signed()
+        assert m7d.is_zero()
+        assert m7d.is_signed()
 
     def test_decimal_field_special_values_not_permitted(self):
         m1 = "-NaN"
@@ -271,7 +278,8 @@ class TestFieldDeserialization:
 
         m7d = field.deserialize(m7)
         assert isinstance(m7d, decimal.Decimal)
-        assert m7d.is_zero() and m7d.is_signed()
+        assert m7d.is_zero()
+        assert m7d.is_signed()
 
     @pytest.mark.parametrize("allow_nan", (None, False, True))
     @pytest.mark.parametrize("value", ("nan", "-nan", "inf", "-inf"))
@@ -552,7 +560,6 @@ class TestFieldDeserialization:
         [
             ("timestamp", 1384043025, dt.datetime(2013, 11, 10, 0, 23, 45)),
             ("timestamp", "1384043025", dt.datetime(2013, 11, 10, 0, 23, 45)),
-            ("timestamp", 1384043025, dt.datetime(2013, 11, 10, 0, 23, 45)),
             ("timestamp", 1384043025.12, dt.datetime(2013, 11, 10, 0, 23, 45, 120000)),
             (
                 "timestamp",
@@ -591,7 +598,7 @@ class TestFieldDeserialization:
         with pytest.raises(ValidationError, match="Not a valid datetime."):
             field.deserialize(in_value)
 
-    #  Regression test for https://github.com/marshmallow-code/marshmallow/pull/2102
+    # Regression test for https://github.com/marshmallow-code/marshmallow/pull/2102
     @pytest.mark.parametrize("fmt", ["timestamp", "timestamp_ms"])
     @pytest.mark.parametrize(
         "mock_fromtimestamp", [MockDateTimeOSError, MockDateTimeOverflowError]
@@ -845,7 +852,7 @@ class TestFieldDeserialization:
         assert excinfo.value.args[0] == "Not a valid period of time."
 
     @pytest.mark.parametrize("format", (None, "%Y-%m-%d"))
-    def test_date_field_deserialization(self, format):
+    def test_date_field_deserialization(self, format):  # noqa: A002
         field = fields.Date(format=format)
         d = dt.date(2014, 8, 21)
         iso_date = d.isoformat()
@@ -1410,9 +1417,7 @@ class TestFieldDeserialization:
     def test_field_deserialization_with_user_validator_class_that_returns_bool(self):
         class MyValidator:
             def __call__(self, val):
-                if val == "valid":
-                    return True
-                return False
+                return val == "valid"
 
         field = fields.Raw(validate=MyValidator())
         assert field.deserialize("valid") == "valid"
@@ -2055,7 +2060,7 @@ class TestSchemaDeserialization:
         assert data == {"foo": "hi", "bar": "okay", "alpha.beta": "woah!"}
 
 
-validators_gen = (func for func in [lambda x: x <= 24, lambda x: 18 <= x])
+validators_gen = (func for func in [lambda x: x <= 24, lambda x: x >= 18])
 
 validators_gen_float = (func for func in [lambda f: f <= 4.1, lambda f: f >= 1.0])
 
@@ -2075,8 +2080,8 @@ class TestValidation:
     @pytest.mark.parametrize(
         "field",
         [
-            fields.Integer(validate=[lambda x: x <= 24, lambda x: 18 <= x]),
-            fields.Integer(validate=(lambda x: x <= 24, lambda x: 18 <= x)),
+            fields.Integer(validate=[lambda x: x <= 24, lambda x: x >= 18]),
+            fields.Integer(validate=(lambda x: x <= 24, lambda x: x >= 18)),
             fields.Integer(validate=validators_gen),
         ],
     )
@@ -2260,7 +2265,7 @@ class TestValidation:
 
 
 @pytest.mark.parametrize("FieldClass", ALL_FIELDS)
-def test_required_field_failure(FieldClass):  # noqa
+def test_required_field_failure(FieldClass):
     class RequireSchema(Schema):
         age = FieldClass(required=True)
 
diff --git a/tests/test_error_store.py b/tests/test_error_store.py
index 8315bf2a..ba799a10 100644
--- a/tests/test_error_store.py
+++ b/tests/test_error_store.py
@@ -1,4 +1,4 @@
-from collections import namedtuple
+from typing import NamedTuple
 
 from marshmallow import missing
 from marshmallow.error_store import merge_errors
@@ -8,12 +8,14 @@ def test_missing_is_falsy():
     assert bool(missing) is False
 
 
-CustomError = namedtuple("CustomError", ["code", "message"])
+class CustomError(NamedTuple):
+    code: int
+    message: str
 
 
 class TestMergeErrors:
     def test_merging_none_and_string(self):
-        assert "error1" == merge_errors(None, "error1")
+        assert merge_errors(None, "error1") == "error1"
 
     def test_merging_none_and_custom_error(self):
         assert CustomError(123, "error1") == merge_errors(
@@ -21,13 +23,13 @@ class TestMergeErrors:
         )
 
     def test_merging_none_and_list(self):
-        assert ["error1", "error2"] == merge_errors(None, ["error1", "error2"])
+        assert merge_errors(None, ["error1", "error2"]) == ["error1", "error2"]
 
     def test_merging_none_and_dict(self):
-        assert {"field1": "error1"} == merge_errors(None, {"field1": "error1"})
+        assert merge_errors(None, {"field1": "error1"}) == {"field1": "error1"}
 
     def test_merging_string_and_none(self):
-        assert "error1" == merge_errors("error1", None)
+        assert merge_errors("error1", None) == "error1"
 
     def test_merging_custom_error_and_none(self):
         assert CustomError(123, "error1") == merge_errors(
@@ -35,13 +37,13 @@ class TestMergeErrors:
         )
 
     def test_merging_list_and_none(self):
-        assert ["error1", "error2"] == merge_errors(["error1", "error2"], None)
+        assert merge_errors(["error1", "error2"], None) == ["error1", "error2"]
 
     def test_merging_dict_and_none(self):
-        assert {"field1": "error1"} == merge_errors({"field1": "error1"}, None)
+        assert merge_errors({"field1": "error1"}, None) == {"field1": "error1"}
 
     def test_merging_string_and_string(self):
-        assert ["error1", "error2"] == merge_errors("error1", "error2")
+        assert merge_errors("error1", "error2") == ["error1", "error2"]
 
     def test_merging_custom_error_and_string(self):
         assert [CustomError(123, "error1"), "error2"] == merge_errors(
@@ -59,17 +61,19 @@ class TestMergeErrors:
         )
 
     def test_merging_string_and_list(self):
-        assert ["error1", "error2"] == merge_errors("error1", ["error2"])
+        assert merge_errors("error1", ["error2"]) == ["error1", "error2"]
 
     def test_merging_string_and_dict(self):
-        assert {"_schema": "error1", "field1": "error2"} == merge_errors(
-            "error1", {"field1": "error2"}
-        )
+        assert merge_errors("error1", {"field1": "error2"}) == {
+            "_schema": "error1",
+            "field1": "error2",
+        }
 
     def test_merging_string_and_dict_with_schema_error(self):
-        assert {"_schema": ["error1", "error2"], "field1": "error3"} == merge_errors(
-            "error1", {"_schema": "error2", "field1": "error3"}
-        )
+        assert merge_errors("error1", {"_schema": "error2", "field1": "error3"}) == {
+            "_schema": ["error1", "error2"],
+            "field1": "error3",
+        }
 
     def test_merging_custom_error_and_list(self):
         assert [CustomError(123, "error1"), "error2"] == merge_errors(
@@ -91,7 +95,7 @@ class TestMergeErrors:
         )
 
     def test_merging_list_and_string(self):
-        assert ["error1", "error2"] == merge_errors(["error1"], "error2")
+        assert merge_errors(["error1"], "error2") == ["error1", "error2"]
 
     def test_merging_list_and_custom_error(self):
         assert ["error1", CustomError(123, "error2")] == merge_errors(
@@ -99,22 +103,25 @@ class TestMergeErrors:
         )
 
     def test_merging_list_and_list(self):
-        assert ["error1", "error2"] == merge_errors(["error1"], ["error2"])
+        assert merge_errors(["error1"], ["error2"]) == ["error1", "error2"]
 
     def test_merging_list_and_dict(self):
-        assert {"_schema": ["error1"], "field1": "error2"} == merge_errors(
-            ["error1"], {"field1": "error2"}
-        )
+        assert merge_errors(["error1"], {"field1": "error2"}) == {
+            "_schema": ["error1"],
+            "field1": "error2",
+        }
 
     def test_merging_list_and_dict_with_schema_error(self):
-        assert {"_schema": ["error1", "error2"], "field1": "error3"} == merge_errors(
-            ["error1"], {"_schema": "error2", "field1": "error3"}
-        )
+        assert merge_errors(["error1"], {"_schema": "error2", "field1": "error3"}) == {
+            "_schema": ["error1", "error2"],
+            "field1": "error3",
+        }
 
     def test_merging_dict_and_string(self):
-        assert {"_schema": "error2", "field1": "error1"} == merge_errors(
-            {"field1": "error1"}, "error2"
-        )
+        assert merge_errors({"field1": "error1"}, "error2") == {
+            "_schema": "error2",
+            "field1": "error1",
+        }
 
     def test_merging_dict_and_custom_error(self):
         assert {
@@ -123,21 +130,22 @@ class TestMergeErrors:
         } == merge_errors({"field1": "error1"}, CustomError(123, "error2"))
 
     def test_merging_dict_and_list(self):
-        assert {"_schema": ["error2"], "field1": "error1"} == merge_errors(
-            {"field1": "error1"}, ["error2"]
-        )
+        assert merge_errors({"field1": "error1"}, ["error2"]) == {
+            "_schema": ["error2"],
+            "field1": "error1",
+        }
 
     def test_merging_dict_and_dict(self):
-        assert {
+        assert merge_errors(
+            {"field1": "error1", "field2": "error2"},
+            {"field2": "error3", "field3": "error4"},
+        ) == {
             "field1": "error1",
             "field2": ["error2", "error3"],
             "field3": "error4",
-        } == merge_errors(
-            {"field1": "error1", "field2": "error2"},
-            {"field2": "error3", "field3": "error4"},
-        )
+        }
 
     def test_deep_merging_dicts(self):
-        assert {"field1": {"field2": ["error1", "error2"]}} == merge_errors(
+        assert merge_errors(
             {"field1": {"field2": "error1"}}, {"field1": {"field2": "error2"}}
-        )
+        ) == {"field1": {"field2": ["error1", "error2"]}}
diff --git a/tests/test_fields.py b/tests/test_fields.py
index 4c26dfaa..a2763db3 100644
--- a/tests/test_fields.py
+++ b/tests/test_fields.py
@@ -29,7 +29,7 @@ def test_field_aliases(alias, field):
 
 class TestField:
     def test_repr(self):
-        default = "œ∑´"
+        default = "œ∑´"  # noqa: RUF001
         field = fields.Raw(dump_default=default, attribute=None)
         assert repr(field) == (
             f"<fields.Raw(dump_default={default!r}, attribute=None, "
@@ -98,7 +98,7 @@ class TestParentAndName:
         baz = fields.Tuple([fields.Str(), fields.Int()])
         bax = fields.Dict(fields.Str(), fields.Int())
 
-    @pytest.fixture()
+    @pytest.fixture
     def schema(self):
         return self.MySchema()
 
@@ -196,7 +196,7 @@ class TestParentAndName:
 
 class TestMetadata:
     @pytest.mark.parametrize("FieldClass", ALL_FIELDS)
-    def test_extra_metadata_may_be_added_to_field(self, FieldClass):  # noqa
+    def test_extra_metadata_may_be_added_to_field(self, FieldClass):
         with pytest.warns(DeprecationWarning):
             field = FieldClass(description="Just a normal field.")
         assert field.metadata["description"] == "Just a normal field."
@@ -209,7 +209,7 @@ class TestMetadata:
         assert field.metadata == {"description": "foo", "widget": "select"}
 
     @pytest.mark.parametrize("FieldClass", ALL_FIELDS)
-    def test_field_metadata_added_in_deprecated_style_warns(self, FieldClass):  # noqa
+    def test_field_metadata_added_in_deprecated_style_warns(self, FieldClass):
         # just the old style
         with pytest.warns(DeprecationWarning):
             field = FieldClass(description="Just a normal field.")
@@ -286,12 +286,12 @@ class TestErrorMessages:
     class MyField(fields.Field):
         default_error_messages = {"custom": "Custom error message."}
 
-    error_messages = [
+    error_messages = (
         ("required", "Missing data for required field."),
         ("null", "Field may not be null."),
         ("custom", "Custom error message."),
         ("validator_failed", "Invalid value."),
-    ]
+    )
 
     def test_default_error_messages_get_merged_with_parent_error_messages_cstm_msg(
         self,
@@ -315,11 +315,11 @@ class TestErrorMessages:
     def test_fail(self, key, message):
         field = self.MyField()
 
-        with pytest.warns(DeprecationWarning):
-            try:
-                field.fail(key)
-            except ValidationError as error:
-                assert error.args[0] == message
+        with (
+            pytest.warns(DeprecationWarning),
+            pytest.raises(ValidationError, match=message),
+        ):
+            field.fail(key)
 
     def test_make_error_key_doesnt_exist(self):
         with pytest.raises(AssertionError) as excinfo:
diff --git a/tests/test_options.py b/tests/test_options.py
index 98fe4f2d..108d7cae 100644
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -273,8 +273,8 @@ class TestIncludeOption:
             email = fields.Str()
 
         s = AddFieldsChild()
-        assert "email" in s._declared_fields.keys()
-        assert "from" in s._declared_fields.keys()
+        assert "email" in s._declared_fields
+        assert "from" in s._declared_fields
         assert isinstance(s._declared_fields["from"], fields.Str)
 
 
diff --git a/tests/test_registry.py b/tests/test_registry.py
index 11905549..a0b60b65 100644
--- a/tests/test_registry.py
+++ b/tests/test_registry.py
@@ -14,7 +14,7 @@ def test_serializer_has_class_registry():
     assert "MySchema" in class_registry._registry
     assert "MySubSchema" in class_registry._registry
 
-    #  by fullpath
+    # by fullpath
     assert "tests.test_registry.MySchema" in class_registry._registry
     assert "tests.test_registry.MySubSchema" in class_registry._registry
 
@@ -59,25 +59,25 @@ def test_serializer_class_registry_register_same_classname_different_module():
     assert "MyTestRegSchema" in class_registry._registry
     assert len(class_registry._registry.get("MyTestRegSchema")) == 1
     assert "modA.MyTestRegSchema" in class_registry._registry
-    #  storing for classname and fullpath
+    # storing for classname and fullpath
     assert len(class_registry._registry) == reglen + 2
 
     type("MyTestRegSchema", (Schema,), {"__module__": "modB"})
 
     assert "MyTestRegSchema" in class_registry._registry
-    #  aggregating classes with same name from different modules
+    # aggregating classes with same name from different modules
     assert len(class_registry._registry.get("MyTestRegSchema")) == 2
     assert "modB.MyTestRegSchema" in class_registry._registry
-    #  storing for same classname (+0) and different module (+1)
+    # storing for same classname (+0) and different module (+1)
     assert len(class_registry._registry) == reglen + 2 + 1
 
     type("MyTestRegSchema", (Schema,), {"__module__": "modB"})
 
     assert "MyTestRegSchema" in class_registry._registry
-    #  only the class with matching module has been replaced
+    # only the class with matching module has been replaced
     assert len(class_registry._registry.get("MyTestRegSchema")) == 2
     assert "modB.MyTestRegSchema" in class_registry._registry
-    #  only the class with matching module has been replaced (+0)
+    # only the class with matching module has been replaced (+0)
     assert len(class_registry._registry) == reglen + 2 + 1
 
 
@@ -90,18 +90,18 @@ def test_serializer_class_registry_override_if_same_classname_same_module():
     assert len(class_registry._registry.get("MyTestReg2Schema")) == 1
     assert "SameModulePath.MyTestReg2Schema" in class_registry._registry
     assert len(class_registry._registry.get("SameModulePath.MyTestReg2Schema")) == 1
-    #  storing for classname and fullpath
+    # storing for classname and fullpath
     assert len(class_registry._registry) == reglen + 2
 
     type("MyTestReg2Schema", (Schema,), {"__module__": "SameModulePath"})
 
     assert "MyTestReg2Schema" in class_registry._registry
-    #  overriding same class name and same module
+    # overriding same class name and same module
     assert len(class_registry._registry.get("MyTestReg2Schema")) == 1
     assert "SameModulePath.MyTestReg2Schema" in class_registry._registry
-    #  overriding same fullpath
+    # overriding same fullpath
     assert len(class_registry._registry.get("SameModulePath.MyTestReg2Schema")) == 1
-    #  overriding for same classname (+0) and different module (+0)
+    # overriding for same classname (+0) and different module (+0)
     assert len(class_registry._registry) == reglen + 2
 
 
@@ -174,7 +174,7 @@ class FooSerializer(Schema):
 
 def test_multiple_classes_with_same_name_raises_error():
     # Import a class with the same name
-    from .foo_serializer import FooSerializer as FooSerializer1  # noqa
+    from .foo_serializer import FooSerializer as FooSerializer1  # noqa: F401
 
     class MySchema(Schema):
         foo = fields.Nested("FooSerializer")
@@ -189,14 +189,14 @@ def test_multiple_classes_with_same_name_raises_error():
 
 def test_multiple_classes_with_all():
     # Import a class with the same name
-    from .foo_serializer import FooSerializer as FooSerializer1  # noqa
+    from .foo_serializer import FooSerializer as FooSerializer1  # noqa: F401
 
     classes = class_registry.get_class("FooSerializer", all=True)
     assert len(classes) == 2
 
 
 def test_can_use_full_module_path_to_class():
-    from .foo_serializer import FooSerializer as FooSerializer1  # noqa
+    from .foo_serializer import FooSerializer as FooSerializer1  # noqa: F401
 
     # Using full paths is ok
 
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 82fc5c6e..22474bdb 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -2,7 +2,8 @@ import datetime as dt
 import decimal
 import math
 import random
-from collections import OrderedDict, namedtuple
+from collections import OrderedDict
+from typing import NamedTuple
 
 import pytest
 import simplejson as json
@@ -92,18 +93,16 @@ def test_load_validation_error_stores_input_data_and_valid_data():
         "always_valid": dt.datetime.now(dt.timezone.utc).isoformat(),
         "always_invalid": 24,
     }
-    try:
+    with pytest.raises(ValidationError) as excinfo:
         schema.load(input_data)
-    except ValidationError as err:
-        # err.data is the raw input data
-        assert err.data == input_data
-        assert "always_valid" in err.valid_data
-        # err.valid_data contains valid, deserialized data
-        assert isinstance(err.valid_data["always_valid"], dt.datetime)
-        # excludes invalid data
-        assert "always_invalid" not in err.valid_data
-    else:
-        pytest.fail("Data is invalid. Expected a ValidationError to be raised.")
+    err = excinfo.value
+    # err.data is the raw input data
+    assert err.data == input_data
+    assert "always_valid" in err.valid_data
+    # err.valid_data contains valid, deserialized data
+    assert isinstance(err.valid_data["always_valid"], dt.datetime)
+    # excludes invalid data
+    assert "always_invalid" not in err.valid_data
 
 
 def test_load_resets_error_fields():
@@ -1314,7 +1313,7 @@ def test_attribute_collision(attribute):
 
 
 class TestDeeplyNestedLoadOnly:
-    @pytest.fixture()
+    @pytest.fixture
     def schema(self):
         class GrandChildSchema(Schema):
             str_dump_only = fields.String()
@@ -1346,7 +1345,7 @@ class TestDeeplyNestedLoadOnly:
             ),
         )
 
-    @pytest.fixture()
+    @pytest.fixture
     def data(self):
         return dict(
             str_dump_only="Dump Only",
@@ -1394,7 +1393,7 @@ class TestDeeplyNestedLoadOnly:
 
 
 class TestDeeplyNestedListLoadOnly:
-    @pytest.fixture()
+    @pytest.fixture
     def schema(self):
         class ChildSchema(Schema):
             str_dump_only = fields.String()
@@ -1412,7 +1411,7 @@ class TestDeeplyNestedListLoadOnly:
             load_only=("str_load_only", "child.str_load_only"),
         )
 
-    @pytest.fixture()
+    @pytest.fixture
     def data(self):
         return dict(
             str_dump_only="Dump Only",
@@ -1605,8 +1604,10 @@ def test_nested_with_sets():
 
     sch = Outer()
 
-    DataClass = namedtuple("DataClass", ["foo"])
-    data = dict(inners={DataClass(42), DataClass(2)})
+    class Thing(NamedTuple):
+        foo: int
+
+    data = dict(inners={Thing(42), Thing(2)})
     result = sch.dump(data)
     assert len(result["inners"]) == 2
 
@@ -1953,13 +1954,12 @@ class TestNestedSchema:
     def blog(self, user):
         col1 = User(name="Mick", age=123)
         col2 = User(name="Keith", age=456)
-        blog = Blog(
+        return Blog(
             "Monty's blog",
             user=user,
             categories=["humor", "violence"],
             collaborators=[col1, col2],
         )
-        return blog
 
     # regression test for https://github.com/marshmallow-code/marshmallow/issues/64
     def test_nested_many_with_missing_attribute(self, user):
@@ -2037,7 +2037,7 @@ class TestNestedSchema:
 
     def test_exclude(self, blog):
         serialized = BlogSchemaExclude().dump(blog)
-        assert "uppername" not in serialized["user"].keys()
+        assert "uppername" not in serialized["user"]
 
     def test_list_field(self, blog):
         serialized = BlogSchema().dump(blog)
@@ -2159,7 +2159,7 @@ class TestNestedSchema:
         if unknown is None or unknown == RAISE:
             with pytest.raises(ValidationError) as exc:
                 ParentSchema().load(data)
-                assert exc.messages == {"child": {"extra": ["Unknown field."]}}
+            assert exc.value.messages == {"child": {"extra": ["Unknown field."]}}
         else:
             output = {
                 INCLUDE: {"child": {"num": 1, "extra": 1}},
@@ -2712,11 +2712,11 @@ class TestRequiredFields:
         allow_none_field = fields.Str(allow_none=True)
         allow_none_required_field = fields.Str(required=True, allow_none=True)
 
-    @pytest.fixture()
+    @pytest.fixture
     def string_schema(self):
         return self.StringSchema()
 
-    @pytest.fixture()
+    @pytest.fixture
     def data(self):
         return dict(
             required_field="foo",
@@ -2767,11 +2767,11 @@ class TestDefaults:
         int_with_default = fields.Int(allow_none=True, dump_default=42)
         str_with_default = fields.Str(allow_none=True, dump_default="foo")
 
-    @pytest.fixture()
+    @pytest.fixture
     def schema(self):
         return self.MySchema()
 
-    @pytest.fixture()
+    @pytest.fixture
     def data(self):
         return dict(
             int_no_default=None,
@@ -2795,13 +2795,13 @@ class TestDefaults:
             # the missing key is not in the serialized result
             assert key not in result
             # the rest of the keys are in the result
-            assert all(k in result for k in d.keys())
+            assert all(k in result for k in d)
 
     def test_none_is_serialized_to_none(self, schema, data):
         errors = schema.validate(data)
         assert errors == {}
         result = schema.dump(data)
-        for key in data.keys():
+        for key in data:
             msg = f"result[{key!r}] should be None"
             assert result[key] is None, msg
 
@@ -2814,7 +2814,7 @@ class TestDefaults:
 
     def test_loading_none(self, schema, data):
         result = schema.load(data)
-        for key in data.keys():
+        for key in data:
             assert result[key] is None
 
     def test_missing_inputs_are_excluded_from_load_output(self, schema, data):
@@ -2830,7 +2830,7 @@ class TestDefaults:
             # the missing key is not in the deserialized result
             assert key not in result
             # the rest of the keys are in the result
-            assert all(k in result for k in d.keys())
+            assert all(k in result for k in d)
 
 
 class TestLoadOnly:
@@ -2843,11 +2843,11 @@ class TestLoadOnly:
         str_load_only = fields.String()
         str_regular = fields.String()
 
-    @pytest.fixture()
+    @pytest.fixture
     def schema(self):
         return self.MySchema()
 
-    @pytest.fixture()
+    @pytest.fixture
     def data(self):
         return dict(
             str_dump_only="Dump Only",
@@ -2928,7 +2928,7 @@ def test_unknown_parameter_value_is_validated(usage_location):
     class MySchema(Schema):
         foo = fields.String()
 
-    with pytest.raises(
+    with pytest.raises(  # noqa: PT012
         ValueError,
         match="Object 'badval' is not a valid value for the 'unknown' parameter",
     ):
diff --git a/tests/test_serialization.py b/tests/test_serialization.py
index b4c0f5fd..c3b87341 100644
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -6,7 +6,8 @@ import ipaddress
 import itertools
 import math
 import uuid
-from collections import OrderedDict, namedtuple
+from collections import OrderedDict
+from typing import NamedTuple
 
 import pytest
 
@@ -19,6 +20,11 @@ from marshmallow.warnings import (
 from tests.base import ALL_FIELDS, DateEnum, GenderEnum, HairColorEnum, User, central
 
 
+class Point(NamedTuple):
+    x: int
+    y: int
+
+
 class DateTimeList:
     def __init__(self, dtimes):
         self.dtimes = dtimes
@@ -62,7 +68,7 @@ class TestFieldSerialization:
 
     def test_function_field_passed_func(self, user):
         field = fields.Function(lambda obj: obj.name.upper())
-        assert "FOO" == field.serialize("key", user)
+        assert field.serialize("key", user) == "FOO"
 
     def test_function_field_passed_serialize_only_is_dump_only(self, user):
         field = fields.Function(serialize=lambda obj: obj.name.upper())
@@ -76,12 +82,12 @@ class TestFieldSerialization:
 
     def test_function_field_passed_serialize(self, user):
         field = fields.Function(serialize=lambda obj: obj.name.upper())
-        assert "FOO" == field.serialize("key", user)
+        assert field.serialize("key", user) == "FOO"
 
     # https://github.com/marshmallow-code/marshmallow/issues/395
     def test_function_field_does_not_swallow_attribute_error(self, user):
         def raise_error(obj):
-            raise AttributeError()
+            raise AttributeError
 
         field = fields.Function(serialize=raise_error)
         with pytest.raises(AttributeError):
@@ -119,7 +125,7 @@ class TestFieldSerialization:
         )
         with pytest.warns(RemovedInMarshmallow4Warning):
             field.parent = Parent(context={"key": "BAR"})
-        assert "FOOBAR" == field.serialize("key", user)
+        assert field.serialize("key", user) == "FOOBAR"
 
     def test_function_field_passed_uncallable_object(self):
         with pytest.raises(TypeError):
@@ -362,31 +368,38 @@ class TestFieldSerialization:
 
         m1s = field.serialize("m1", user)
         assert isinstance(m1s, decimal.Decimal)
-        assert m1s.is_qnan() and not m1s.is_signed()
+        assert m1s.is_qnan()
+        assert not m1s.is_signed()
 
         m2s = field.serialize("m2", user)
         assert isinstance(m2s, decimal.Decimal)
-        assert m2s.is_qnan() and not m2s.is_signed()
+        assert m2s.is_qnan()
+        assert not m2s.is_signed()
 
         m3s = field.serialize("m3", user)
         assert isinstance(m3s, decimal.Decimal)
-        assert m3s.is_qnan() and not m3s.is_signed()
+        assert m3s.is_qnan()
+        assert not m3s.is_signed()
 
         m4s = field.serialize("m4", user)
         assert isinstance(m4s, decimal.Decimal)
-        assert m4s.is_qnan() and not m4s.is_signed()
+        assert m4s.is_qnan()
+        assert not m4s.is_signed()
 
         m5s = field.serialize("m5", user)
         assert isinstance(m5s, decimal.Decimal)
-        assert m5s.is_infinite() and m5s.is_signed()
+        assert m5s.is_infinite()
+        assert m5s.is_signed()
 
         m6s = field.serialize("m6", user)
         assert isinstance(m6s, decimal.Decimal)
-        assert m6s.is_infinite() and not m6s.is_signed()
+        assert m6s.is_infinite()
+        assert not m6s.is_signed()
 
         m7s = field.serialize("m7", user)
         assert isinstance(m7s, decimal.Decimal)
-        assert m7s.is_zero() and m7s.is_signed()
+        assert m7s.is_zero()
+        assert m7s.is_signed()
 
         field = fields.Decimal(as_string=True, allow_nan=True)
 
@@ -409,7 +422,8 @@ class TestFieldSerialization:
 
         m7s = field.serialize("m7", user)
         assert isinstance(m7s, decimal.Decimal)
-        assert m7s.is_zero() and m7s.is_signed()
+        assert m7s.is_zero()
+        assert m7s.is_signed()
 
     def test_decimal_field_fixed_point_representation(self, user):
         """
@@ -521,7 +535,7 @@ class TestFieldSerialization:
             mfield = fields.Method("raise_error")
 
             def raise_error(self, obj):
-                raise AttributeError()
+                raise AttributeError
 
         with pytest.raises(AttributeError):
             MySchema().dump({})
@@ -642,9 +656,9 @@ class TestFieldSerialization:
         assert field.serialize("d", {"d": value}) == expected
 
     def test_datetime_field_format(self, user):
-        format = "%Y-%m-%d"
-        field = fields.DateTime(format=format)
-        assert field.serialize("created", user) == user.created.strftime(format)
+        datetimeformat = "%Y-%m-%d"
+        field = fields.DateTime(format=datetimeformat)
+        assert field.serialize("created", user) == user.created.strftime(datetimeformat)
 
     def test_string_field(self):
         field = fields.String()
@@ -863,7 +877,7 @@ class TestFieldSerialization:
         result = field.serialize("dtimes", obj)
         assert len(result) == 2
 
-    def test_list_field_work_with_generators_empty_generator_returns_none_for_every_non_returning_yield_statement(  # noqa: B950
+    def test_list_field_work_with_generators_empty_generator_returns_none_for_every_non_returning_yield_statement(
         self,
     ):
         def custom_generator():
@@ -998,8 +1012,6 @@ class TestSchemaSerialization:
 
 
 def test_serializing_named_tuple():
-    Point = namedtuple("Point", ["x", "y"])
-
     field = fields.Raw()
 
     p = Point(x=4, y=2)
@@ -1008,7 +1020,6 @@ def test_serializing_named_tuple():
 
 
 def test_serializing_named_tuple_with_meta():
-    Point = namedtuple("Point", ["x", "y"])
     p = Point(x=4, y=2)
 
     class PointSerializer(Schema):
@@ -1022,12 +1033,12 @@ def test_serializing_named_tuple_with_meta():
 
 def test_serializing_slice():
     values = [{"value": value} for value in range(5)]
-    slice = itertools.islice(values, None)
+    sliced = itertools.islice(values, None)
 
     class ValueSchema(Schema):
         value = fields.Int()
 
-    serialized = ValueSchema(many=True).dump(slice)
+    serialized = ValueSchema(many=True).dump(sliced)
     assert serialized == values
 
 
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 44baa71f..a1e38522 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -1,7 +1,7 @@
 import datetime as dt
-from collections import namedtuple
 from copy import copy, deepcopy
 from functools import partial
+from typing import NamedTuple
 
 import pytest
 
@@ -14,7 +14,9 @@ def test_missing_singleton_copy():
     assert deepcopy(utils.missing) is utils.missing
 
 
-PointNT = namedtuple("PointNT", ["x", "y"])
+class PointNT(NamedTuple):
+    x: int
+    y: int
 
 
 class PointClass:
@@ -96,8 +98,7 @@ def test_set_value():
 
 
 def test_is_keyed_tuple():
-    Point = namedtuple("Point", ["x", "y"])
-    p = Point(24, 42)
+    p = PointNT(24, 42)
     assert utils.is_keyed_tuple(p) is True
     t = (24, 42)
     assert utils.is_keyed_tuple(t) is False
@@ -249,7 +250,7 @@ def test_from_timestamp_with_negative_value():
 
 def test_from_timestamp_with_overflow_value():
     value = 9223372036854775
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="out of range"):
         utils.from_timestamp(value)
 
 
@@ -271,7 +272,7 @@ def test_get_func_args():
 
 # Regression test for https://github.com/marshmallow-code/marshmallow/issues/540
 def test_function_field_using_type_annotation():
-    def get_split_words(value: str):  # noqa
+    def get_split_words(value: str):
         return value.split(";")
 
     class MySchema(Schema):
diff --git a/tests/test_validate.py b/tests/test_validate.py
index fcda4e81..866276de 100644
--- a/tests/test_validate.py
+++ b/tests/test_validate.py
@@ -95,7 +95,7 @@ def test_url_relative_valid(valid_url):
     assert validator(valid_url) == valid_url
 
 
-@pytest.mark.parametrize(  # noqa: W605
+@pytest.mark.parametrize(
     "invalid_url",
     [
         "http//example.org",
@@ -357,7 +357,7 @@ def test_range_repr():
                 min=None, max=None, error=None, min_inclusive=True, max_inclusive=True
             )
         )
-        == "<Range(min=None, max=None, min_inclusive=True, max_inclusive=True, error=None)>"  # noqa: B950
+        == "<Range(min=None, max=None, min_inclusive=True, max_inclusive=True, error=None)>"
     )
     assert (
         repr(
@@ -365,7 +365,7 @@ def test_range_repr():
                 min=1, max=3, error="foo", min_inclusive=False, max_inclusive=False
             )
         )
-        == "<Range(min=1, max=3, min_inclusive=False, max_inclusive=False, error={!r})>".format(  # noqa: B950
+        == "<Range(min=1, max=3, min_inclusive=False, max_inclusive=False, error={!r})>".format(
             "foo"
         )
     )
@@ -421,12 +421,12 @@ def test_length_equal():
         validate.Length(equal=2)("foo")
     with pytest.raises(ValidationError):
         validate.Length(equal=2)([1, 2, 3])
-
-    with pytest.raises(ValueError):
+    error_message = "The `equal` parameter was provided, maximum or minimum parameter must not be provided"
+    with pytest.raises(ValueError, match=error_message):
         validate.Length(1, None, equal=3)("foo")
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match=error_message):
         validate.Length(None, 5, equal=3)("foo")
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match=error_message):
         validate.Length(1, 5, equal=3)("foo")