<@ULVA73B9P> in Prefect I want to override the loa...
# ask-marvin
r
@Marvin in Prefect I want to override the load method in the Block class to add a try except clause to have the Block loaded from an alternative location if the Prefect database is not available. Do I have to make a new custom sub Class for each Block type I use or can I do it just once, upstream in the Block class?
m
thought for 163.8 seconds
Short answer: don’t override
Block
globally
. In practice, you should either: 1. create a subclass per block type that needs custom fallback behavior, or 2. put the fallback logic in one shared helper/wrapper and call that for any block class.
Block.load(...)
is a classmethod that returns the specific subclass that called it, so while you can technically monkey-patch the upstream
Block
base class, that would affect all blocks globally and is not a supported or safe extension pattern. A better pattern is:
Copy code
python
from prefect.blocks.core import Block

def load_block_with_fallback(block_cls: type[Block], name: str, **kwargs):
    try:
        return block_cls.load(name, **kwargs)
    except Exception:
        # fallback logic here
        return ...
That lets you write the fallback logic once without subclassing every block unless you specifically want a custom API like
MySecretBlock.load(...)
. If you do want it attached to the class API, then yes, you’d generally need a custom subclass for each block type, for example:
Copy code
python
from prefect.blocks.system import Secret

class FallbackSecret(Secret):
    @classmethod
    def load(cls, name: str, validate: bool = True, client=None):
        try:
            return super().load(name, validate=validate, client=client)
        except Exception:
            return cls(value="fallback-secret")
A few important caveats: - Monkey-patching
Block.load
upstream is not recommended
- it changes behavior for every block - it can be hard to debug - you’d also need to think about
aload(...)
for async contexts - Prefect does not provide a built-in alternate-location fallback for blocks if the API/database is unavailable - Often the cleanest fallback source is environment variables or a local config file rather than trying to alter block internals So the practical answer is: - If you want one reusable implementation: use a helper/factory function once - If you want the behavior on the class itself: make a subclass per block type - Do not patch the base
Block
class globally unless you’re willing to own the maintenance risk
Relevant API docs: - Prefect Blocks docs If you want, I can sketch a generic fallback loader that works with
Secret
,
JSON
,
String
, and your own custom blocks without needing one subclass per type.
r
@Marvin how does your proposal for a generic fallback loader for Secret, JSON and String differ from your load_block_with_fallback function above?
m
thought for 11.9 seconds
It’s basically the same pattern. The difference is mostly in ergonomics and how much block-specific fallback behavior you want to encode.
load_block_with_fallback
is the minimal generic idea:
Copy code
python
def load_block_with_fallback(block_cls, name, **kwargs):
    try:
        return block_cls.load(name, **kwargs)
    except Exception:
        return ...
That says: “try Prefect first, otherwise do something else.” But it leaves the fallback undefined. A more complete generic fallback loader would add things like: - different fallback sources per block type - conversion from env/config values into the right block instance - narrower exception handling - optional sync/async support - consistent logging/errors For example:
Copy code
python
import json
import os
from prefect.blocks.system import Secret, JSON, String

def load_block_with_fallback(block_cls, name: str, validate: bool = True, client=None):
    try:
        return block_cls.load(name, validate=validate, client=client)
    except Exception as exc:
        env_name = name.upper().replace("-", "_")

        if block_cls is Secret:
            value = os.getenv(env_name)
            if value is None:
                raise
            return Secret(value=value)

        if block_cls is String:
            value = os.getenv(env_name)
            if value is None:
                raise
            return String(value=value)

        if block_cls is JSON:
            value = os.getenv(env_name)
            if value is None:
                raise
            return JSON(value=json.loads(value))

        raise
So: - the first version is a pattern stub - the generic fallback loader is a real reusable implementation If you want to support many block types without subclassing each one, the second approach is what I meant. One important improvement I’d recommend over my earlier sketch: don’t catch bare
Exception
unless you really mean it
. Prefer catching API/unavailability-related errors only, so you don’t mask validation bugs or coding mistakes. If you want, I can write a production-ready version that: - handles
Secret
,
String
, and
JSON
- uses env vars or local files as fallback - only catches the right Prefect/client errors - includes an async
aload
equivalent too