diff --git a/docs/v3/advanced/results.mdx b/docs/v3/advanced/results.mdx index c4ea78e8e2dd..433f65fdcda1 100644 --- a/docs/v3/advanced/results.mdx +++ b/docs/v3/advanced/results.mdx @@ -119,6 +119,35 @@ def my_flow(): other_storage_task() ``` + +**Using result storage with decorators** + +When specifying `result_storage` in `@flow` or `@task` decorators, you have two options: + +- **Block instances**: The block instance must be saved server-side or loaded from a saved block instance before it is provided to the `@task` or `@flow` decorator. +- **String references**: Use the format `"block-type-slug/block-name"` for deferred resolution at runtime + +For testing scenarios, string references are recommended since they don't require server connectivity at import time. + +```python +from prefect import flow +from prefect.filesystems import LocalFileSystem + +# Option 1: Save block first (requires server connection at import time) +storage = LocalFileSystem(basepath="/tmp/results") +storage.save("my-storage", overwrite=True) + +@flow(result_storage=storage) # Works because block is saved +def my_flow(): + return "result" + +# Option 2: Use string reference (recommended for testing) +@flow(result_storage="local-file-system/my-storage") # Resolved at runtime +def my_flow(): + return "result" +``` + + #### Specifying a default filesystem Alternatively, you can specify a different filesystem through the `PREFECT_DEFAULT_RESULT_STORAGE_BLOCK` setting. diff --git a/src/prefect/flows.py b/src/prefect/flows.py index 5f6124f66d08..acbb5f273641 100644 --- a/src/prefect/flows.py +++ b/src/prefect/flows.py @@ -175,9 +175,13 @@ class Flow(Generic[P, R]): that Prefect should choose whether the result should be persisted depending on the features being used. result_storage: An optional block to use to persist the result of this flow. - This value will be used as the default for any tasks in this flow. - If not provided, the local file system will be used unless called as - a subflow, at which point the default will be loaded from the parent flow. + This can be either a saved block instance or a string reference (e.g., + "local-file-system/my-storage"). Block instances must have `.save()` called + first since decorators execute at import time. String references are resolved + at runtime and recommended for testing scenarios. This value will be used as + the default for any tasks in this flow. If not provided, the local file system + will be used unless called as a subflow, at which point the default will be + loaded from the parent flow. result_serializer: An optional serializer to use to serialize the result of this flow for persistence. This value will be used as the default for any tasks in this flow. If not provided, the value of `PREFECT_RESULTS_DEFAULT_SERIALIZER` @@ -376,7 +380,8 @@ def __init__( if getattr(result_storage, "_block_document_id", None) is None: raise TypeError( "Result storage configuration must be persisted server-side." - " Please call `.save()` on your block before passing it in." + " Please call `.save()` on your block before passing it in," + " or use a string reference like 'local-file-system/my-storage' instead." ) self.result_storage = result_storage self.result_serializer = result_serializer @@ -1889,9 +1894,13 @@ def __call__( that Prefect should choose whether the result should be persisted depending on the features being used. result_storage: An optional block to use to persist the result of this flow. - This value will be used as the default for any tasks in this flow. - If not provided, the local file system will be used unless called as - a subflow, at which point the default will be loaded from the parent flow. + This can be either a saved block instance or a string reference (e.g., + "local-file-system/my-storage"). Block instances must have `.save()` called + first since decorators execute at import time. String references are resolved + at runtime and recommended for testing scenarios. This value will be used as + the default for any tasks in this flow. If not provided, the local file system + will be used unless called as a subflow, at which point the default will be + loaded from the parent flow. result_serializer: An optional serializer to use to serialize the result of this flow for persistence. This value will be used as the default for any tasks in this flow. If not provided, the value of `PREFECT_RESULTS_DEFAULT_SERIALIZER` diff --git a/src/prefect/tasks.py b/src/prefect/tasks.py index b72c767a0d93..f8b15550b806 100644 --- a/src/prefect/tasks.py +++ b/src/prefect/tasks.py @@ -343,7 +343,11 @@ class Task(Generic[P, R]): indicates that the global default should be used (which is `True` by default). result_storage: An optional block to use to persist the result of this task. - Defaults to the value set in the flow the task is called in. + This can be either a saved block instance or a string reference (e.g., + "local-file-system/my-storage"). Block instances must have `.save()` called + first since decorators execute at import time. String references are resolved + at runtime and recommended for testing scenarios. Defaults to the value set + in the flow the task is called in. result_storage_key: An optional key to store the result in storage at when persisted. Defaults to a unique identifier. result_serializer: An optional serializer to use to serialize the result of this @@ -576,7 +580,8 @@ def __init__( if getattr(result_storage, "_block_document_id", None) is None: raise TypeError( "Result storage configuration must be persisted server-side." - " Please call `.save()` on your block before passing it in." + " Please call `.save()` on your block before passing it in," + " or use a string reference like 'local-file-system/my-storage' instead." ) self.result_storage = result_storage @@ -1992,7 +1997,11 @@ def task( indicates that the global default should be used (which is `True` by default). result_storage: An optional block to use to persist the result of this task. - Defaults to the value set in the flow the task is called in. + This can be either a saved block instance or a string reference (e.g., + "local-file-system/my-storage"). Block instances must have `.save()` called + first since decorators execute at import time. String references are resolved + at runtime and recommended for testing scenarios. Defaults to the value set + in the flow the task is called in. result_storage_key: An optional key to store the result in storage at when persisted. Defaults to a unique identifier. result_serializer: An optional serializer to use to serialize the result of this