-
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 AssetChecksDefinitionProps to replace untyped dictionary shenanigans #16747
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
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.
A downside here is that a user who wants to construct an AssetChecksDefinition
needs to go through the extra hoop of constructing one of these props objects.
Might it make sense to make AssetChecksDefinition
itself a NamedTuple
?
I don't think so. You want the option to add variables in |
I agree this is a nuisance, but a fairly minor one IMO since people constructing I can take a stab at some shenanigans, but it will likely involve having an |
fab5c9a
to
55e1992
Compare
@@ -330,12 +330,15 @@ def check_concurrency_claim( | |||
return claim_status.with_sleep_interval(float(self._sleep_interval)) | |||
|
|||
|
|||
def get_all_direct_subclasses_of_marker(marker_interface_cls: Type) -> List[Type]: | |||
import dagster as dagster | |||
def get_all_direct_subclasses_of_marker(marker_interface_cls: Type, module=None) -> List[Type]: |
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.
X
in progress adherence test version that does not require props in __init__ cp cp cp type as ModuleType
6af1750
to
eade43c
Compare
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.
@alangenfeld this is the pattern I want to institute to prevent the description shenanigans you called brutal in #16755 (review)
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 like the conceptual set-up of explicitly capturing the immutable props separate from memoization/utility instance members.
self._props.XXX
is a little bit unfortunate but it does make it clear on every touch whats "fundamental" properties of the definition vs not
hard_coded_list = [ | ||
(AssetChecksDefinition, AssetChecksDefinitionProps), | ||
] | ||
|
||
# keep hard_coded_list up-to-date with all subclasses of IDefinitionPropsClassMatchesInit | ||
assert set([def_type.__name__ for def_type in def_types]) == set( | ||
[entry[0].__name__ for entry in hard_coded_list] | ||
), ( | ||
"You likely added a subclass of IDefinitionPropsClassMatchesInit without adding it to the" | ||
" hard_coded_list" | ||
) |
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.
this is just here because you couldn't figure out how to pluck TPropsType
& TDefType
from the class object?
marker_interface_cls: Type, module: Optional[ModuleType] = None | ||
) -> List[Type]: | ||
"""Get all direct subclasses of a given marker interface class from a given module. If the | ||
module is not specified, it defaults to the dagster module. | ||
""" | ||
if module is None: | ||
import dagster as dagster | ||
|
||
module = dagster |
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.
what motivated this change?
@experimental | ||
class AssetChecksDefinitionInputOutputProps(NamedTuple): | ||
|
||
class IDefinitionPropsClassMatchesInit(Generic[TPropsType, TDefType], ABC): |
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.
nit: something in the direction of IDefinitionWithProps
/ PropsBackedDefinition
, i think it makes more sense to target the fundamentlal pattern then a specific thing we enforce when participating in it
return self.__class__( | ||
**self._props._replace( | ||
resource_defs=merge_resource_defs( | ||
old_resource_defs=self._props.resource_defs, | ||
resource_defs_to_merge_in=resource_defs, | ||
requires_resources=self, | ||
) | ||
)._asdict() |
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.
[1]
@staticmethod | ||
def from_props(props: TPropsType) -> TDefType: ... |
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.
this appears unused - should we instead capture [1] with a method on this class?
Summary & Motivation
Right in now in
AssetChecksDefinition
andAssetsDefinition
we have a bunch of keyword arguments to construct definitions objects, and then for places where we need to make copies keep a parallelget_attributes_dict
method that has to be kept in sync. Instead here we just create a tuple with the "core properties" of the definition and leverage the typing system to keep everything in sync. This mean less code, and the satisfying deletion of the comment "if adding new fields, make sure to handle them in the get_attributes_dict method
" and its associatedget_attributes_dict
method.This also has another key advantage. We can, as a rule, have the props object be exactly what was passed in to the origin construction, rather than anything post-processed. That means that copies will more guaranteed fidelity. We are at risk right now––especially in
AssetsDefinition
––of having the__init__
method process data in subtle ways that might alter behavior when passed through__init__
again when copies are made. There is an underlying assumption in that pattern that__init__
is idempotent, and it is difficult to guarantee that assumption. This pattern avoids that issue entirely.Another nice side benefit if the elimination of
AssetChecksDefinitionInputOutputProps
, which was quite the mouthful. This just hoists its properties into the top-level object.I would like to eventually use this pattern in
AssetsDefinition
as part of cleanup of that class. However it is easy to add earlier rather than later, so adding this toAssetChecksDefinition
now before its complexity inevitably grows.How I Tested These Changes
BK