<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def get_materialized_checkpoints(checkpoints, colnames, lower_dt, odo_kwargs):
 """ Computes a lower bound and a DataFrame checkpoints. Parameters checkpoints : Expr Bound blaze expression for a checkpoints table from which to get a computed lower bound. colnames : iterable of str The names of the columns for which checkpoints should be computed. lower_dt : pd.Timestamp The lower date being queried for that serves as an upper bound for checkpoints. odo_kwargs : dict, optional The extra keyword arguments to pass to ``odo``. """

    if checkpoints is not None:
        ts = checkpoints[TS_FIELD_NAME]
        checkpoints_ts = odo(
            ts[ts < lower_dt].max(),
            pd.Timestamp,
            **odo_kwargs
        )
        if pd.isnull(checkpoints_ts):
            # We don't have a checkpoint for before our start date so just
            # don't constrain the lower date.
            materialized_checkpoints = pd.DataFrame(columns=colnames)
            lower = None
        else:
            materialized_checkpoints = odo(
                checkpoints[ts == checkpoints_ts][colnames],
                pd.DataFrame,
                **odo_kwargs
            )
            lower = checkpoints_ts
    else:
        materialized_checkpoints = pd.DataFrame(columns=colnames)
        lower = None  # we don't have a good lower date constraint
    return lower, materialized_checkpoints