ValueError("Could not hash object of type function")

Hi and thank you in advance.

I am new to Panel and am trying to put together a fairly simple UI + data processing pipeline.

I successfully set up a text input widget that receives text input and submits it with a button and has a loading indicator until the next step is complete, but I am getting the following error:

ValueError("Could not hash object of type function")

Here’s how my code is setup (using Python 3.9)

import panel as pn
import json
from gspread import Client
from authlib.integrations.requests_client import AssertionSession

pn.extension('tabulator')
SCOPES = [
    'https://www.googleapis.com/auth/spreadsheets',
    'https://www.googleapis.com/auth/drive',
]
CONF_FILE = "path_to_file.json"
# Text input component
spreadsheet_name = pn.widgets.TextInput(
name="Spreadsheet Title", placeholder="Enter *title* of your spreadsheet")

# Submit button
submit_button = pn.widgets.Button(name='Get Data', button_type='primary')

# loading indicator
loading_indicator = pn.indicators.LoadingSpinner(
    value=False, width=50, height=50)

@pn.cache
def get_data(spreadsheet_name):
    loading_indicator.value = True
    submit_button.button_type = 'default'
    submit_button.disabled = True

    try:
        session = create_assertion_session(CONF_FILE, SCOPES)
        gc = Client(None, session)
        wks = gc.open(spreadsheet_name).sheet1
        # wks = gc.open("Movavi-Brazil-20221130-20231130").sheet1
        df = pd.DataFrame(wks.get_all_records())
        df.Item.replace('', np.nan, inplace=True)
        df.dropna(subset=['Item'], inplace=True)
        df['Total'] = df['Total'].apply(lambda x: x.strip("$"))
        df['Total'] = df['Total'].astype(float)

        print(df.head().to_markdown(index=False,
              numalign="left", stralign="left"))
        return df

    except Exception as e:
        print(f"Error occured: {e}")
        return pn.pane.Markdown(f"**Error: {e}")
        # return pn.pane.Markdown("**Error: Spreadsheet not found.** Please check the title")
    finally:
        loading_indicator.value = False
        submit_button.button_type = 'primary'
        submit_button.disabled = False

def create_assertion_session(conf_file, scopes, subject=None):
    with open(conf_file, 'r') as f:
        conf = json.load(f)

    token_url = conf['token_uri']
    issuer = conf['client_email']
    key = conf['private_key']
    key_id = conf.get('private_key_id')

    header = {'alg': 'RS256'}
    if key_id:
        header['kid'] = key_id

    # Google puts scope in payload
    claims = {'scope': ' '.join(scopes)}
    return AssertionSession(
        grant_type=AssertionSession.JWT_BEARER_GRANT_TYPE,
        token_endpoint=token_url,
        issuer=issuer,
        audience=token_url,
        claims=claims,
        subject=subject,
        key=key,
        header=header,
    )

# Attach the functions to widgets
spreadsheet_name.param.watch(get_data, 'value')

# create template to organize components
template = pn.template.BootstrapTemplate(title='Channel Spend Analysis')

template.main.append(
    pn.Row(
        pn.Column(
            spreadsheet_name, submit_button, loading_indicator, width=700),
        pn.Column(column_selector_component, model_output_text, width=600)
    )
)

template.servable()

I have been able to execute the text input and reading the spreadsheet using
@pn.cache in the same way in a different function, but there I bound the function and variable text input. Is

spreadsheet_name.param.watch(get_data, 'value') 

not the correct way to invoke get_data when receiving the value of spreadsheet_name?

Thank you!!