Skip to content

Commit b2b37a5

Browse files
authored
[components] Enforce that all component schemas are of type ResolvableModel (#27677)
## Summary & Motivation Doing this has a few benefits. The first is that we force people to use ResolvableModel as the base class for their component schemas, which means we're able to inject additional stuff into that class in the future. The second is that we can create a default `load()` method, which pretty significantly reduces boilerplate for these component types. ## How I Tested These Changes ## Changelog NOCHANGELOG
1 parent 81fb035 commit b2b37a5

File tree

13 files changed

+21
-92
lines changed

13 files changed

+21
-92
lines changed

python_modules/libraries/dagster-components/dagster_components/core/component.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from dagster._core.definitions.definitions_class import Definitions
1717
from dagster._core.errors import DagsterError
1818
from dagster._utils import snakecase
19-
from pydantic import BaseModel
2019
from typing_extensions import Self
2120

2221
from dagster_components.core.component_key import (
@@ -43,7 +42,7 @@ class Component(ABC):
4342
name: ClassVar[Optional[str]] = None
4443

4544
@classmethod
46-
def get_schema(cls) -> Optional[type[BaseModel]]:
45+
def get_schema(cls) -> Optional[type[ResolvableModel]]:
4746
return None
4847

4948
@classmethod
@@ -65,8 +64,8 @@ def get_additional_scope(cls) -> Mapping[str, Any]:
6564
def build_defs(self, context: "ComponentLoadContext") -> Definitions: ...
6665

6766
@classmethod
68-
@abstractmethod
69-
def load(cls, params: Optional[BaseModel], context: "ComponentLoadContext") -> Self: ...
67+
def load(cls, params: Optional[ResolvableModel], context: "ComponentLoadContext") -> Self:
68+
return cls() if params is None else context.resolve(params, as_type=cls)
7069

7170
@classmethod
7271
def get_metadata(cls) -> "ComponentTypeInternalMetadata":

python_modules/libraries/dagster-components/dagster_components/lib/dbt_project/component.py

-4
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@ def get_scaffolder(cls) -> "DbtProjectComponentScaffolder":
6969
def get_schema(cls) -> type[DbtProjectParams]:
7070
return DbtProjectParams
7171

72-
@classmethod
73-
def load(cls, params: DbtProjectParams, context: ComponentLoadContext) -> "DbtProjectComponent":
74-
return context.resolve(params, as_type=cls)
75-
7672
def get_asset_selection(
7773
self, select: str, exclude: Optional[str] = None
7874
) -> DbtManifestAssetSelection:

python_modules/libraries/dagster-components/dagster_components/lib/definitions_component/component.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
)
88
from dagster._seven import import_uncached_module_from_path
99
from dagster._utils import pushd
10-
from typing_extensions import Self
1110

1211
from dagster_components import (
1312
Component,
@@ -26,8 +25,8 @@ class DefinitionsParamSchema(ResolvableModel):
2625
class DefinitionsComponent(Component):
2726
"""Wraps an arbitrary set of Dagster definitions."""
2827

29-
def __init__(self, definitions_path: Path):
30-
self.definitions_path = definitions_path
28+
def __init__(self, definitions_path: Optional[Path]):
29+
self.definitions_path = definitions_path or Path("definitions.py")
3130

3231
@classmethod
3332
def get_scaffolder(cls) -> DefinitionsComponentScaffolder:
@@ -37,10 +36,6 @@ def get_scaffolder(cls) -> DefinitionsComponentScaffolder:
3736
def get_schema(cls) -> type[DefinitionsParamSchema]:
3837
return DefinitionsParamSchema
3938

40-
@classmethod
41-
def load(cls, params: DefinitionsParamSchema, context: ComponentLoadContext) -> Self:
42-
return cls(definitions_path=Path(params.definitions_path or "definitions.py"))
43-
4439
def build_defs(self, context: ComponentLoadContext) -> Definitions:
4540
with pushd(str(context.path)):
4641
module = import_uncached_module_from_path("definitions", str(self.definitions_path))

python_modules/libraries/dagster-components/dagster_components/lib/pipes_subprocess_script_collection.py

-6
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@ def introspect_from_path(path: Path) -> "PipesSubprocessScriptCollection":
6060
def get_schema(cls) -> type[PipesSubprocessScriptCollectionParams]:
6161
return PipesSubprocessScriptCollectionParams
6262

63-
@classmethod
64-
def load(
65-
cls, params: PipesSubprocessScriptCollectionParams, context: ComponentLoadContext
66-
) -> "PipesSubprocessScriptCollection":
67-
return context.resolve(params, as_type=cls)
68-
6963
def build_defs(self, context: "ComponentLoadContext") -> "Definitions":
7064
from dagster._core.definitions.definitions_class import Definitions
7165

python_modules/libraries/dagster-components/dagster_components/lib/sling_replication_collection/component.py

-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from dagster._record import record
1010
from dagster_sling import DagsterSlingTranslator, SlingResource, sling_assets
1111
from dagster_sling.resources import AssetExecutionContext
12-
from typing_extensions import Self
1312

1413
from dagster_components import Component, ComponentLoadContext
1514
from dagster_components.core.component import registered_component_type
@@ -112,10 +111,6 @@ def get_scaffolder(cls) -> ComponentScaffolder:
112111
def get_schema(cls) -> type[SlingReplicationCollectionParams]:
113112
return SlingReplicationCollectionParams
114113

115-
@classmethod
116-
def load(cls, params: SlingReplicationCollectionParams, context: ComponentLoadContext) -> Self:
117-
return context.resolve(params, as_type=cls)
118-
119114
def build_asset(
120115
self, context: ComponentLoadContext, replication_spec: SlingReplicationSpec
121116
) -> AssetsDefinition:

python_modules/libraries/dagster-components/dagster_components/lib/test/all_metadata_empty_asset.py

-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
from dagster._core.definitions.decorators.asset_decorator import asset
22
from dagster._core.definitions.definitions_class import Definitions
33
from dagster._core.execution.context.asset_execution_context import AssetExecutionContext
4-
from typing_extensions import Self
54

65
from dagster_components import Component, ComponentLoadContext, registered_component_type
76
from dagster_components.core.component_scaffolder import DefaultComponentScaffolder
87

98

109
@registered_component_type(name="all_metadata_empty_asset")
1110
class AllMetadataEmptyAsset(Component):
12-
@classmethod
13-
def load(cls, context: "ComponentLoadContext") -> Self:
14-
return cls()
15-
1611
@classmethod
1712
def get_scaffolder(cls) -> DefaultComponentScaffolder:
1813
return DefaultComponentScaffolder()

python_modules/libraries/dagster-components/dagster_components/lib/test/complex_schema_asset.py

+2-12
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
from dagster._core.definitions.decorators.asset_decorator import asset
55
from dagster._core.definitions.definitions_class import Definitions
66
from dagster._core.execution.context.asset_execution_context import AssetExecutionContext
7-
from pydantic import BaseModel
8-
from typing_extensions import Self
97

108
from dagster_components import Component, ComponentLoadContext, registered_component_type
119
from dagster_components.core.component_scaffolder import DefaultComponentScaffolder
10+
from dagster_components.core.schema.base import ResolvableModel
1211
from dagster_components.core.schema.metadata import ResolvableFieldInfo
1312
from dagster_components.core.schema.objects import (
1413
AssetAttributesModel,
@@ -17,7 +16,7 @@
1716
)
1817

1918

20-
class ComplexAssetParams(BaseModel):
19+
class ComplexAssetParams(ResolvableModel):
2120
value: str
2221
op: Optional[OpSpecModel] = None
2322
asset_attributes: Annotated[
@@ -38,15 +37,6 @@ def get_schema(cls):
3837
def get_scaffolder(cls) -> DefaultComponentScaffolder:
3938
return DefaultComponentScaffolder()
4039

41-
@classmethod
42-
def load(cls, params: ComplexAssetParams, context: "ComponentLoadContext") -> Self:
43-
return cls(
44-
value=params.value,
45-
op_spec=params.op,
46-
asset_attributes=params.asset_attributes,
47-
asset_transforms=params.asset_transforms or [],
48-
)
49-
5040
def __init__(
5141
self,
5242
value: str,

python_modules/libraries/dagster-components/dagster_components/lib/test/simple_asset.py

-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from dagster._core.definitions.definitions_class import Definitions
44
from dagster._core.execution.context.asset_execution_context import AssetExecutionContext
55
from pydantic import BaseModel
6-
from typing_extensions import Self
76

87
from dagster_components import Component, ComponentLoadContext, registered_component_type
98
from dagster_components.core.component_scaffolder import (
@@ -29,13 +28,6 @@ def get_schema(cls):
2928
def get_scaffolder(cls) -> ComponentScaffolder:
3029
return DefaultComponentScaffolder()
3130

32-
@classmethod
33-
def load(cls, params: SimpleAssetParams, context: "ComponentLoadContext") -> Self:
34-
return cls(
35-
asset_key=AssetKey.from_user_string(params.asset_key),
36-
value=params.value,
37-
)
38-
3931
def __init__(self, asset_key: AssetKey, value: str):
4032
self._asset_key = asset_key
4133
self._value = value

python_modules/libraries/dagster-components/dagster_components/lib/test/simple_pipes_script_asset.py

-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from dagster._core.execution.context.asset_execution_context import AssetExecutionContext
88
from dagster._core.pipes.subprocess import PipesSubprocessClient
99
from pydantic import BaseModel
10-
from typing_extensions import Self
1110

1211
from dagster_components import Component, ComponentLoadContext, registered_component_type
1312
from dagster_components.core.component_scaffolder import (
@@ -62,13 +61,6 @@ def get_scaffolder(cls) -> ComponentScaffolder:
6261
def get_schema(cls):
6362
return SimplePipesScriptAssetParams
6463

65-
@classmethod
66-
def load(cls, params: SimplePipesScriptAssetParams, context: "ComponentLoadContext") -> Self:
67-
return cls(
68-
asset_key=AssetKey.from_user_string(params.asset_key),
69-
script_path=context.path / params.filename,
70-
)
71-
7264
def __init__(self, asset_key: AssetKey, script_path: Path):
7365
self._asset_key = asset_key
7466
self._script_path = script_path

python_modules/libraries/dagster-components/dagster_components/test/basic_components.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44

55
from dagster._core.definitions.definitions_class import Definitions
66
from pydantic import BaseModel, ConfigDict
7-
from typing_extensions import Self
87

9-
from dagster_components import Component, registered_component_type
8+
from dagster_components import Component, ResolvableModel, registered_component_type
109
from dagster_components.core.component import ComponentLoadContext
1110

1211

13-
class MyComponentSchema(BaseModel):
12+
class MyComponentSchema(ResolvableModel):
1413
a_string: str
1514
an_int: int
1615

@@ -21,14 +20,14 @@ class MyComponentSchema(BaseModel):
2120
class MyComponent(Component):
2221
name = "my_component"
2322

23+
def __init__(self, a_string: str, an_int: int):
24+
self.a_string = a_string
25+
self.an_int = an_int
26+
2427
@classmethod
2528
def get_schema(cls) -> type[MyComponentSchema]:
2629
return MyComponentSchema
2730

28-
@classmethod
29-
def load(cls, params: MyComponentSchema, context: ComponentLoadContext) -> Self:
30-
return cls()
31-
3231
def build_defs(self, context: ComponentLoadContext) -> Definitions:
3332
return Definitions()
3433

@@ -54,9 +53,5 @@ class MyNestedComponent(Component):
5453
def get_schema(cls) -> type[MyNestedComponentSchema]:
5554
return MyNestedComponentSchema
5655

57-
@classmethod
58-
def load(cls, params: MyComponentSchema, context: ComponentLoadContext) -> Self:
59-
return cls()
60-
6156
def build_defs(self, context: ComponentLoadContext) -> Definitions:
6257
return Definitions()
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from dagster._core.definitions.definitions_class import Definitions
2-
from dagster_components import Component, registered_component_type
2+
from dagster_components import Component, ResolvableModel, registered_component_type
33
from dagster_components.core.component import ComponentLoadContext
4-
from dagster_components.core.schema.base import BaseModel
5-
from typing_extensions import Self
64

75

8-
class MyComponentSchema(BaseModel):
6+
class MyComponentSchema(ResolvableModel):
97
a_string: str
108
an_int: int
119

@@ -14,13 +12,13 @@ class MyComponentSchema(BaseModel):
1412
class MyComponent(Component):
1513
name = "my_component"
1614

15+
def __init__(self, a_string: str, an_int: int):
16+
self.a_string = a_string
17+
self.an_int = an_int
18+
1719
@classmethod
1820
def get_schema(cls):
1921
return MyComponentSchema
2022

21-
@classmethod
22-
def load(cls, params: MyComponentSchema, context: ComponentLoadContext) -> Self:
23-
return cls()
24-
2523
def build_defs(self, context: ComponentLoadContext) -> Definitions:
2624
return Definitions()

python_modules/libraries/dagster-components/dagster_components_tests/integration_tests/components/definitions/other_local_component_sample/__init__.py

-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from dagster_components import Component, registered_component_type
33
from dagster_components.core.component import ComponentLoadContext
44
from pydantic import BaseModel
5-
from typing_extensions import Self
65

76

87
class MyNewComponentSchema(BaseModel):
@@ -18,9 +17,5 @@ class MyNewComponent(Component):
1817
def get_schema(cls):
1918
return MyNewComponentSchema
2019

21-
@classmethod
22-
def load(cls, params: MyNewComponentSchema, context: ComponentLoadContext) -> Self:
23-
return cls()
24-
2520
def build_defs(self, context: ComponentLoadContext) -> Definitions:
2621
return Definitions()

python_modules/libraries/dagster-dg/dagster_dg/templates/COMPONENT_TYPE/COMPONENT_TYPE_NAME_PLACEHOLDER.py.jinja

+2-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ class {{ component_type_class_name }}(Component):
1717
COMPONENT DESCRIPTION HERE.
1818
"""
1919

20+
def __init__(self): ...
21+
2022
@classmethod
2123
def get_schema(cls):
2224
return {{ component_type_class_name }}Params
@@ -25,15 +27,6 @@ class {{ component_type_class_name }}(Component):
2527
def get_scaffolder(cls) -> DefaultComponentScaffolder:
2628
return DefaultComponentScaffolder()
2729

28-
@classmethod
29-
def load(
30-
cls,
31-
params: {{ component_type_class_name }}Params,
32-
context: ComponentLoadContext,
33-
) -> "{{ component_type_class_name }}":
34-
# Add logic for mapping schema parameters to constructor args here.
35-
return cls()
36-
3730
def build_defs(self, load_context: ComponentLoadContext) -> Definitions:
3831
# Add definition construction logic here.
3932
return Definitions()

0 commit comments

Comments
 (0)