|
20 | 20 | from __future__ import annotations |
21 | 21 |
|
22 | 22 | import contextlib |
23 | | -import copy |
24 | | -import functools |
25 | | -import warnings |
26 | 23 | from collections.abc import ( |
27 | 24 | Container, |
28 | | - ItemsView, |
29 | 25 | Iterator, |
30 | | - KeysView, |
31 | 26 | Mapping, |
32 | | - MutableMapping, |
33 | | - ValuesView, |
34 | 27 | ) |
35 | 28 | from typing import ( |
36 | 29 | TYPE_CHECKING, |
|
40 | 33 | ) |
41 | 34 |
|
42 | 35 | import attrs |
43 | | -import lazy_object_proxy |
44 | 36 | from sqlalchemy import and_, select |
45 | 37 |
|
46 | 38 | from airflow.exceptions import RemovedInAirflow3Warning |
@@ -367,97 +359,12 @@ class AirflowContextDeprecationWarning(RemovedInAirflow3Warning): |
367 | 359 | """Warn for usage of deprecated context variables in a task.""" |
368 | 360 |
|
369 | 361 |
|
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.""" |
400 | 364 |
|
401 | 365 | 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()),) |
461 | 368 |
|
462 | 369 |
|
463 | 370 | 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 |
505 | 412 |
|
506 | 413 | :meta private: |
507 | 414 | """ |
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}) |
510 | 416 | return new |
511 | 417 |
|
512 | 418 |
|
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 | | - |
548 | 419 | def context_get_outlet_events(context: Context) -> OutletEventAccessors: |
549 | 420 | try: |
550 | 421 | return context["outlet_events"] |
|
0 commit comments