<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 async def serialize_property(value: 'Input[Any]', deps: List['Resource'], input_transformer: Optional[Callable[[str], str]] = None) -> Any: """ Serializes a single Input into a form suitable for remoting to the engine, awaiting any futures required to do so. """

    if isinstance(value, list):
        props = []
        for elem in value:
            props.append(await serialize_property(elem, deps, input_transformer))

        return props

    if known_types.is_custom_resource(value):
        deps.append(value)
        return await serialize_property(value.id, deps, input_transformer)

    if known_types.is_asset(value):
        # Serializing an asset requires the use of a magical signature key, since otherwise it would look
        # like any old weakly typed object/map when received by the other side of the RPC boundary.
        obj = {
            _special_sig_key: _special_asset_sig
        }

        if hasattr(value, "path"):
            obj["path"] = await serialize_property(value.path, deps, input_transformer)
        elif hasattr(value, "text"):
            obj["text"] = await serialize_property(value.text, deps, input_transformer)
        elif hasattr(value, "uri"):
            obj["uri"] = await serialize_property(value.uri, deps, input_transformer)
        else:
            raise AssertionError(f"unknown asset type: {value}")

        return obj

    if known_types.is_archive(value):
        # Serializing an archive requires the use of a magical signature key, since otherwise it would look
        # like any old weakly typed object/map when received by the other side of the RPC boundary.
        obj = {
            _special_sig_key: _special_archive_sig
        }

        if hasattr(value, "assets"):
            obj["assets"] = await serialize_property(value.assets, deps, input_transformer)
        elif hasattr(value, "path"):
            obj["path"] = await serialize_property(value.path, deps, input_transformer)
        elif hasattr(value, "uri"):
            obj["uri"] = await serialize_property(value.uri, deps, input_transformer)
        else:
            raise AssertionError(f"unknown archive type: {value}")

        return obj

    if inspect.isawaitable(value):
        # Coroutines and Futures are both awaitable. Coroutines need to be scheduled.
        # asyncio.ensure_future returns futures verbatim while converting coroutines into
        # futures by arranging for the execution on the event loop.
        #
        # The returned future can then be awaited to yield a value, which we'll continue
        # serializing.
        future_return = await asyncio.ensure_future(value)
        return await serialize_property(future_return, deps, input_transformer)

    if known_types.is_output(value):
        deps.extend(value.resources())

        # When serializing an Output, we will either serialize it as its resolved value or the "unknown value"
        # sentinel. We will do the former for all outputs created directly by user code (such outputs always
        # resolve isKnown to true) and for any resource outputs that were resolved with known values.
        is_known = await value._is_known
        value = await serialize_property(value.future(), deps, input_transformer)
        return value if is_known else UNKNOWN

    if isinstance(value, dict):
        obj = {}
        for k, v in value.items():
            transformed_key = k
            if input_transformer is not None:
                transformed_key = input_transformer(k)
                log.debug(f"transforming input property: {k} -> {transformed_key}")
            obj[transformed_key] = await serialize_property(v, deps, input_transformer)

        return obj

    return value