Repository: marshmallow-code/marshmallow
Pull request #2731 (2025-01-06 04:56:41)

Deprecate context
to prepare users for the upcoming removal of context in 4.0


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4f60cc21..25b4c4f3 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -13,6 +13,10 @@ Bug fixes:
 
 - Typing: Fix type hint for ``nested`` parameter of `Nested <marshmallow.fields.Nested>`.
 
+Deprecations:
+
+- Deprecate ``context`` parameter of `Schema <marshmallow.schema.Schema>` (:issue:`1826`).
+  Use `contextVars.ContextVar` to pass context data instead.
 
 3.23.3 (2025-01-03)
 *******************
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
index 9a134fb4..72b6a040 100644
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -390,6 +390,13 @@ class Schema(metaclass=SchemaMeta):
             if unknown is None
             else validate_unknown_parameter_value(unknown)
         )
+        if context:
+            warnings.warn(
+                "The `context` parameter is deprecated and will be removed in marshmallow 4.0. "
+                "Use `contextvars.ContextVar` to pass context instead.",
+                RemovedInMarshmallow4Warning,
+                stacklevel=2,
+            )
         self.context = context or {}
         self._normalize_nested_options()
         #: Dictionary mapping field_names -> :class:`Field` objects


Test patch:
diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py
index 80d44272..8816efeb 100644
--- a/tests/test_deserialization.py
+++ b/tests/test_deserialization.py
@@ -10,6 +10,7 @@ import pytest
 from marshmallow import EXCLUDE, INCLUDE, RAISE, Schema, fields, validate
 from marshmallow.exceptions import ValidationError
 from marshmallow.validate import Equal
+from marshmallow.warnings import RemovedInMarshmallow4Warning
 from tests.base import (
     ALL_FIELDS,
     DateEnum,
@@ -992,7 +993,8 @@ class TestFieldDeserialization:
             lambda x: None,
             deserialize=lambda val, context: val.upper() + context["key"],
         )
-        field.parent = Parent(context={"key": "BAR"})
+        with pytest.warns(RemovedInMarshmallow4Warning):
+            field.parent = Parent(context={"key": "BAR"})
         assert field.deserialize("foo") == "FOOBAR"
 
     def test_function_field_passed_deserialize_only_is_load_only(self):
diff --git a/tests/test_schema.py b/tests/test_schema.py
index 443aa300..06edc405 100644
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -23,6 +23,7 @@ from marshmallow.exceptions import (
     StringNotCollectionError,
     ValidationError,
 )
+from marshmallow.warnings import RemovedInMarshmallow4Warning
 from tests.base import (
     Blog,
     BlogOnlySchema,
@@ -364,11 +365,12 @@ def test_nested_on_bind_field_hook():
 
         foo = fields.Nested(NestedSchema)
 
-    schema1 = MySchema(context={"fname": "foobar"})
-    schema2 = MySchema(context={"fname": "quxquux"})
+    with pytest.warns(RemovedInMarshmallow4Warning):
+        schema1 = MySchema(context={"fname": "foobar"})
+        schema2 = MySchema(context={"fname": "quxquux"})
 
-    assert schema1.fields["foo"].schema.fields["bar"].metadata["fname"] == "foobar"
-    assert schema2.fields["foo"].schema.fields["bar"].metadata["fname"] == "quxquux"
+        assert schema1.fields["foo"].schema.fields["bar"].metadata["fname"] == "foobar"
+        assert schema2.fields["foo"].schema.fields["bar"].metadata["fname"] == "quxquux"
 
 
 class TestValidate:
@@ -2518,7 +2520,8 @@ class TestContext:
         ser = CSchema()
         ser.context["info"] = "i like bikes"
         obj = {"inner": {}}
-        result = ser.dump(obj)
+        with pytest.warns(RemovedInMarshmallow4Warning):
+            result = ser.dump(obj)
         assert result["inner"]["likes_bikes"] is True
 
     # Regression test for https://github.com/marshmallow-code/marshmallow/issues/820
@@ -2572,8 +2575,10 @@ class TestContext:
         class InnerSchema(Schema):
             foo = fields.Field()
 
-        class OuterSchema(Schema):
-            inner = fields.Nested(InnerSchema(context={"unp": Unpicklable()}))
+        with pytest.warns(RemovedInMarshmallow4Warning):
+
+            class OuterSchema(Schema):
+                inner = fields.Nested(InnerSchema(context={"unp": Unpicklable()}))
 
         outer = OuterSchema()
         obj = {"inner": {"foo": 42}}
diff --git a/tests/test_serialization.py b/tests/test_serialization.py
index df778ce8..d519c676 100644
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -12,6 +12,7 @@ import pytest
 
 from marshmallow import Schema, fields
 from marshmallow import missing as missing_
+from marshmallow.warnings import RemovedInMarshmallow4Warning
 from tests.base import ALL_FIELDS, DateEnum, GenderEnum, HairColorEnum, User, central
 
 
@@ -110,7 +111,8 @@ class TestFieldSerialization:
         field = fields.Function(
             serialize=lambda obj, context: obj.name.upper() + context["key"]
         )
-        field.parent = Parent(context={"key": "BAR"})
+        with pytest.warns(RemovedInMarshmallow4Warning):
+            field.parent = Parent(context={"key": "BAR"})
         assert "FOOBAR" == field.serialize("key", user)
 
     def test_function_field_passed_uncallable_object(self):