Skip to content

Commit a8cbcd1

Browse files
committed
Remove code for deprecation of Context keys
We removed all the deprecated keys in #43902 so we no longer need this code. In preparation of #45583, I want to simplify this code. We can always revert/re-add this later when we need to deprecate a key.
1 parent 4e66c39 commit a8cbcd1

6 files changed

Lines changed: 19 additions & 150 deletions

File tree

airflow/serialization/serialized_objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -781,7 +781,7 @@ def serialize(
781781
return cls._encode(var.to_json(), type_=DAT.DAG_CALLBACK_REQUEST)
782782
elif var.__class__ == Context:
783783
d = {}
784-
for k, v in var._context.items():
784+
for k, v in var.items():
785785
obj = cls.serialize(v, strict=strict)
786786
d[str(k)] = obj
787787
return cls._encode(d, type_=DAT.TASK_CONTEXT)

airflow/utils/context.py

Lines changed: 5 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,10 @@
2020
from __future__ import annotations
2121

2222
import contextlib
23-
import copy
24-
import functools
25-
import warnings
2623
from collections.abc import (
2724
Container,
28-
ItemsView,
2925
Iterator,
30-
KeysView,
3126
Mapping,
32-
MutableMapping,
33-
ValuesView,
3427
)
3528
from typing import (
3629
TYPE_CHECKING,
@@ -40,7 +33,6 @@
4033
)
4134

4235
import attrs
43-
import lazy_object_proxy
4436
from sqlalchemy import and_, select
4537

4638
from airflow.exceptions import RemovedInAirflow3Warning
@@ -367,97 +359,12 @@ class AirflowContextDeprecationWarning(RemovedInAirflow3Warning):
367359
"""Warn for usage of deprecated context variables in a task."""
368360

369361

370-
def _create_deprecation_warning(key: str, replacements: list[str]) -> RemovedInAirflow3Warning:
371-
message = f"Accessing {key!r} from the template is deprecated and will be removed in a future version."
372-
if not replacements:
373-
return AirflowContextDeprecationWarning(message)
374-
display_except_last = ", ".join(repr(r) for r in replacements[:-1])
375-
if display_except_last:
376-
message += f" Please use {display_except_last} or {replacements[-1]!r} instead."
377-
else:
378-
message += f" Please use {replacements[-1]!r} instead."
379-
return AirflowContextDeprecationWarning(message)
380-
381-
382-
class Context(MutableMapping[str, Any]):
383-
"""
384-
Jinja2 template context for task rendering.
385-
386-
This is a mapping (dict-like) class that can lazily emit warnings when
387-
(and only when) deprecated context keys are accessed.
388-
"""
389-
390-
_DEPRECATION_REPLACEMENTS: dict[str, list[str]] = {}
391-
392-
def __init__(self, context: MutableMapping[str, Any] | None = None, **kwargs: Any) -> None:
393-
self._context: MutableMapping[str, Any] = context or {}
394-
if kwargs:
395-
self._context.update(kwargs)
396-
self._deprecation_replacements = self._DEPRECATION_REPLACEMENTS.copy()
397-
398-
def __repr__(self) -> str:
399-
return repr(self._context)
362+
class Context(dict[str, Any]):
363+
"""Jinja2 template context for task rendering."""
400364

401365
def __reduce_ex__(self, protocol: SupportsIndex) -> tuple[Any, ...]:
402-
"""
403-
Pickle the context as a dict.
404-
405-
We are intentionally going through ``__getitem__`` in this function,
406-
instead of using ``items()``, to trigger deprecation warnings.
407-
"""
408-
items = [(key, self[key]) for key in self._context]
409-
return dict, (items,)
410-
411-
def __copy__(self) -> Context:
412-
new = type(self)(copy.copy(self._context))
413-
new._deprecation_replacements = self._deprecation_replacements.copy()
414-
return new
415-
416-
def __getitem__(self, key: str) -> Any:
417-
with contextlib.suppress(KeyError):
418-
warnings.warn(
419-
_create_deprecation_warning(key, self._deprecation_replacements[key]),
420-
stacklevel=2,
421-
)
422-
with contextlib.suppress(KeyError):
423-
return self._context[key]
424-
raise KeyError(key)
425-
426-
def __setitem__(self, key: str, value: Any) -> None:
427-
self._deprecation_replacements.pop(key, None)
428-
self._context[key] = value
429-
430-
def __delitem__(self, key: str) -> None:
431-
self._deprecation_replacements.pop(key, None)
432-
del self._context[key]
433-
434-
def __contains__(self, key: object) -> bool:
435-
return key in self._context
436-
437-
def __iter__(self) -> Iterator[str]:
438-
return iter(self._context)
439-
440-
def __len__(self) -> int:
441-
return len(self._context)
442-
443-
def __eq__(self, other: Any) -> bool:
444-
if not isinstance(other, Context):
445-
return NotImplemented
446-
return self._context == other._context
447-
448-
def __ne__(self, other: Any) -> bool:
449-
if not isinstance(other, Context):
450-
return NotImplemented
451-
return self._context != other._context
452-
453-
def keys(self) -> KeysView[str]:
454-
return self._context.keys()
455-
456-
def items(self):
457-
return ItemsView(self._context)
458-
459-
def values(self):
460-
return ValuesView(self._context)
366+
"""Pickle the context as a dict."""
367+
return dict, (list(self.items()),)
461368

462369

463370
def context_merge(context: Mapping[str, Any], *args: Any, **kwargs: Any) -> None:
@@ -505,46 +412,10 @@ def context_copy_partial(source: Mapping[str, Any], keys: Container[str]) -> Con
505412
506413
:meta private:
507414
"""
508-
new = Context({k: v for k, v in source._context.items() if k in keys})
509-
new._deprecation_replacements = source._deprecation_replacements.copy()
415+
new = Context({k: v for k, v in source.items() if k in keys})
510416
return new
511417

512418

513-
def lazy_mapping_from_context(source: Context) -> Mapping[str, Any]:
514-
"""
515-
Create a mapping that wraps deprecated entries in a lazy object proxy.
516-
517-
This further delays deprecation warning to until when the entry is actually
518-
used, instead of when it's accessed in the context. The result is useful for
519-
passing into a callable with ``**kwargs``, which would unpack the mapping
520-
too eagerly otherwise.
521-
522-
This is implemented as a free function because the ``Context`` type is
523-
"faked" as a ``TypedDict`` in ``context.pyi``, which cannot have custom
524-
functions.
525-
526-
:meta private:
527-
"""
528-
if not isinstance(source, Context):
529-
# Sometimes we are passed a plain dict (usually in tests, or in User's
530-
# custom operators) -- be lenient about what we accept so we don't
531-
# break anything for users.
532-
return source
533-
534-
def _deprecated_proxy_factory(k: str, v: Any) -> Any:
535-
replacements = source._deprecation_replacements[k]
536-
warnings.warn(_create_deprecation_warning(k, replacements), stacklevel=2)
537-
return v
538-
539-
def _create_value(k: str, v: Any) -> Any:
540-
if k not in source._deprecation_replacements:
541-
return v
542-
factory = functools.partial(_deprecated_proxy_factory, k, v)
543-
return lazy_object_proxy.Proxy(factory)
544-
545-
return {k: _create_value(k, v) for k, v in source._context.items()}
546-
547-
548419
def context_get_outlet_events(context: Context) -> OutletEventAccessors:
549420
try:
550421
return context["outlet_events"]

airflow/utils/context.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,5 +143,4 @@ def context_merge(context: Context, additions: Iterable[tuple[str, Any]], **kwar
143143
def context_merge(context: Context, **kwargs: Any) -> None: ...
144144
def context_update_for_unmapped(context: Mapping[str, Any], task: BaseOperator) -> None: ...
145145
def context_copy_partial(source: Context, keys: Container[str]) -> Context: ...
146-
def lazy_mapping_from_context(source: Context) -> Mapping[str, Any]: ...
147146
def context_get_outlet_events(context: Context) -> OutletEventAccessors: ...

airflow/utils/operator_helpers.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
from airflow import settings
2727
from airflow.sdk.definitions.asset.metadata import Metadata
2828
from airflow.typing_compat import ParamSpec
29-
from airflow.utils.context import Context, lazy_mapping_from_context
3029
from airflow.utils.types import NOTSET
3130

3231
if TYPE_CHECKING:
@@ -151,9 +150,8 @@ class KeywordParameters:
151150
content and use it somewhere else without needing ``lazy-object-proxy``.
152151
"""
153152

154-
def __init__(self, kwargs: Mapping[str, Any], *, wildcard: bool) -> None:
153+
def __init__(self, kwargs: Mapping[str, Any]) -> None:
155154
self._kwargs = kwargs
156-
self._wildcard = wildcard
157155

158156
@classmethod
159157
def determine(
@@ -181,20 +179,14 @@ def determine(
181179

182180
if has_wildcard_kwargs:
183181
# If the callable has a **kwargs argument, it's ready to accept all the kwargs.
184-
return cls(kwargs, wildcard=True)
182+
return cls(kwargs)
185183

186184
# If the callable has no **kwargs argument, it only wants the arguments it requested.
187-
kwargs = {key: kwargs[key] for key in signature.parameters if key in kwargs}
188-
return cls(kwargs, wildcard=False)
185+
filtered_kwargs = {key: kwargs[key] for key in signature.parameters if key in kwargs}
186+
return cls(filtered_kwargs)
189187

190188
def unpacking(self) -> Mapping[str, Any]:
191189
"""Dump the kwargs mapping to unpack with ``**`` in a function call."""
192-
if self._wildcard and isinstance(self._kwargs, Context): # type: ignore[misc]
193-
return lazy_mapping_from_context(self._kwargs)
194-
return self._kwargs
195-
196-
def serializing(self) -> Mapping[str, Any]:
197-
"""Dump the kwargs mapping for serialization purposes."""
198190
return self._kwargs
199191

200192

providers/src/airflow/providers/standard/operators/python.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,11 @@ def _execute_python_callable_in_subprocess(self, python_path: Path):
581581
return self._read_result(output_path)
582582

583583
def determine_kwargs(self, context: Mapping[str, Any]) -> Mapping[str, Any]:
584-
return KeywordParameters.determine(self.python_callable, self.op_args, context).serializing()
584+
keyword_params = KeywordParameters.determine(self.python_callable, self.op_args, context)
585+
if AIRFLOW_V_3_0_PLUS:
586+
return keyword_params.unpacking()
587+
else:
588+
return keyword_params.serializing() # type: ignore[attr-defined]
585589

586590

587591
class PythonVirtualenvOperator(_BasePythonVirtualenvOperator):

providers/tests/standard/operators/test_python.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1939,7 +1939,10 @@ def get_all_the_context(**context):
19391939
current_context = get_current_context()
19401940
with warnings.catch_warnings():
19411941
warnings.simplefilter("ignore", AirflowContextDeprecationWarning)
1942-
assert context == current_context._context
1942+
if AIRFLOW_V_3_0_PLUS:
1943+
assert context == current_context
1944+
else:
1945+
assert current_context._context
19431946

19441947

19451948
@pytest.fixture

0 commit comments

Comments
 (0)