-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add indirect execution context access #14954
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
from abc import ABC, ABCMeta, abstractmethod | ||
from contextlib import contextmanager | ||
from contextvars import ContextVar | ||
from inspect import _empty as EmptyAnnotation | ||
from typing import ( | ||
AbstractSet, | ||
|
@@ -48,6 +50,7 @@ | |
from dagster._core.instance import DagsterInstance | ||
from dagster._core.log_manager import DagsterLogManager | ||
from dagster._core.storage.dagster_run import DagsterRun | ||
from dagster._utils.cached_method import cached_method | ||
from dagster._utils.forked_pdb import ForkedPdb | ||
from dagster._utils.warnings import ( | ||
deprecation_warning, | ||
|
@@ -1334,15 +1337,34 @@ def typed_event_stream_error_message(self) -> Optional[str]: | |
def set_requires_typed_event_stream(self, *, error_message: Optional[str] = None) -> None: | ||
self._step_execution_context.set_requires_typed_event_stream(error_message=error_message) | ||
|
||
@staticmethod | ||
def get() -> "OpExecutionContext": | ||
ctx = _current_asset_execution_context.get() | ||
if ctx is None: | ||
raise DagsterInvariantViolationError("No current OpExecutionContext in scope.") | ||
return ctx.get_op_execution_context() | ||
|
||
|
||
class AssetExecutionContext(OpExecutionContext): | ||
def __init__(self, step_execution_context: StepExecutionContext): | ||
super().__init__(step_execution_context=step_execution_context) | ||
|
||
@staticmethod | ||
def get() -> "AssetExecutionContext": | ||
ctx = _current_asset_execution_context.get() | ||
if ctx is None: | ||
raise DagsterInvariantViolationError("No current AssetExecutionContext in scope.") | ||
return ctx | ||
|
||
@cached_method | ||
def get_op_execution_context(self) -> "OpExecutionContext": | ||
return OpExecutionContext(self._step_execution_context) | ||
|
||
def build_execution_context( | ||
|
||
@contextmanager | ||
def enter_execution_context( | ||
step_context: StepExecutionContext, | ||
) -> Union[OpExecutionContext, AssetExecutionContext]: | ||
) -> Iterator[Union[OpExecutionContext, AssetExecutionContext]]: | ||
"""Get the correct context based on the type of step (op or asset) and the user provided context | ||
type annotation. Follows these rules. | ||
|
||
|
@@ -1393,16 +1415,30 @@ def build_execution_context( | |
" OpExecutionContext, or left blank." | ||
) | ||
|
||
if context_annotation is EmptyAnnotation: | ||
# if no type hint has been given, default to: | ||
# * AssetExecutionContext for sda steps not in graph-backed assets, and asset_checks | ||
# * OpExecutionContext for non sda steps | ||
# * OpExecutionContext for ops in graph-backed assets | ||
if is_asset_check: | ||
return AssetExecutionContext(step_context) | ||
if is_op_in_graph_asset or not is_sda_step: | ||
return OpExecutionContext(step_context) | ||
return AssetExecutionContext(step_context) | ||
if context_annotation is AssetExecutionContext: | ||
return AssetExecutionContext(step_context) | ||
return OpExecutionContext(step_context) | ||
# Structured assuming upcoming changes to make AssetExecutionContext contain an OpExecutionContext | ||
asset_ctx = AssetExecutionContext(step_context) | ||
asset_token = _current_asset_execution_context.set(asset_ctx) | ||
|
||
try: | ||
if context_annotation is EmptyAnnotation: | ||
# if no type hint has been given, default to: | ||
# * AssetExecutionContext for sda steps not in graph-backed assets, and asset_checks | ||
# * OpExecutionContext for non sda steps | ||
# * OpExecutionContext for ops in graph-backed assets | ||
if is_asset_check: | ||
yield asset_ctx | ||
elif is_op_in_graph_asset or not is_sda_step: | ||
yield asset_ctx.get_op_execution_context() | ||
else: | ||
yield asset_ctx | ||
elif context_annotation is AssetExecutionContext: | ||
yield asset_ctx | ||
else: | ||
yield asset_ctx.get_op_execution_context() | ||
finally: | ||
_current_asset_execution_context.reset(asset_token) | ||
Comment on lines
+1419
to
+1439
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍🏻 thanks much more comfortable with this |
||
|
||
|
||
_current_asset_execution_context: ContextVar[Optional[AssetExecutionContext]] = ContextVar( | ||
"_current_asset_execution_context", default=None | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,6 +57,7 @@ | |
user_code_error_boundary, | ||
) | ||
from dagster._core.events import DagsterEvent | ||
from dagster._core.execution.context.compute import enter_execution_context | ||
from dagster._core.execution.context.output import OutputContext | ||
from dagster._core.execution.context.system import StepExecutionContext, TypeCheckContext | ||
from dagster._core.execution.plan.compute import execute_core_compute | ||
|
@@ -462,13 +463,14 @@ def core_dagster_event_sequence_for_step( | |
else: | ||
core_gen = step_context.op_def.compute_fn | ||
|
||
with time_execution_scope() as timer_result: | ||
user_event_sequence = check.generator( | ||
execute_core_compute( | ||
step_context, | ||
inputs, | ||
core_gen, | ||
) | ||
with time_execution_scope() as timer_result, enter_execution_context( | ||
step_context | ||
) as compute_context: | ||
Comment on lines
+466
to
+468
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is hoisted up here to the top of the iterator call stack since context managers + active generators get goofy and if you raise an exception based on a yielded value in a frame above where the context manager is opened it does not get closed |
||
user_event_sequence = execute_core_compute( | ||
step_context, | ||
inputs, | ||
core_gen, | ||
compute_context, | ||
) | ||
|
||
# It is important for this loop to be indented within the | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ | |
from dagster._core.definitions.asset_checks import build_asset_with_blocking_check | ||
from dagster._core.definitions.job_definition import JobDefinition | ||
from dagster._core.definitions.op_definition import OpDefinition | ||
from dagster._core.errors import DagsterInvalidDefinitionError | ||
from dagster._core.errors import DagsterInvalidDefinitionError, DagsterInvariantViolationError | ||
from dagster._core.storage.dagster_run import DagsterRun | ||
|
||
|
||
|
@@ -405,3 +405,24 @@ def the_op(context: int): | |
@asset | ||
def the_asset(context: int): | ||
pass | ||
|
||
|
||
def test_get_context(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might be worth testing thread behavior? I guess ContextVar should handle it fine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ya feel pretty good about thread & coroutine concurrency |
||
with pytest.raises(DagsterInvariantViolationError): | ||
OpExecutionContext.get() | ||
|
||
@op | ||
def o(context): | ||
assert context == OpExecutionContext.get() | ||
|
||
@job | ||
def j(): | ||
o() | ||
|
||
assert j.execute_in_process().success | ||
|
||
@asset | ||
def a(context: AssetExecutionContext): | ||
assert context == AssetExecutionContext.get() | ||
|
||
assert materialize([a]).success |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i updated these to just
get
now that they raise + slopps feedback