def METHOD_NAME(self, key, default=None):
    """Remove the given key, or subkey, from the cache
    Args:
        key: key or subkey to remove.
        default: value to return if key is not found
    Returns:
        If the key is not found, 'default'. If the key is complete, the removed
        value. If the key is partial, the TreeCacheNode corresponding to the part
        of the tree that was removed.
    """
    if not isinstance(key, tuple):
        raise TypeError("The cache key must be a tuple not %r" % (type(key),))
    # a list of the nodes we have touched on the way down the tree
    nodes = []
    node = self.root
    for k in key[:-1]:
        node = node.get(k, None)
        if node is None:
            return default
        if not isinstance(node, TreeCacheNode):
            # we've gone off the end of the tree
            raise ValueError("pop() key too long")
        nodes.append(node)  # don't add the root node
    popped = node.METHOD_NAME(key[-1], SENTINEL)
    if popped is SENTINEL:
        return default
    # working back up the tree, clear out any nodes that are now empty
    node_and_keys = list(zip(nodes, key))
    node_and_keys.reverse()
    node_and_keys.append((self.root, None))
    for i in range(len(node_and_keys) - 1):
        n, k = node_and_keys[i]
        if n:
            break
        # found an empty node: remove it from its parent, and loop.
        node_and_keys[i + 1][0].METHOD_NAME(k)
    cnt = sum(1 for _ in iterate_tree_cache_entry(popped))
    self.size -= cnt
    return popped