from typing import Callable
from typing import Optional


def get_namespace(fn: Callable, namespace: Optional[str]) -> str:
    """
    Returns a representation of a function's name (perhaps within a namespace),
    like

    .. code-block:: none

        mymodule:MyClass.myclassfunc  # with no namespace
        mymodule:MyClass.myclassfunc|somenamespace  # with a namespace

    Args:
        fn: a function
        namespace: an optional namespace, which can be of any type but is
            normally a ``str``; if not ``None``, ``str(namespace)`` will be
            added to the result. See
            https://dogpilecache.readthedocs.io/en/latest/api.html#dogpile.cache.region.CacheRegion.cache_on_arguments
    """  # noqa
    # See hidden attributes with dir(fn)
    # noinspection PyUnresolvedReferences
    return "{module}:{name}{extra}".format(
        module=fn.__module__,
        name=fn.__qualname__,  # __qualname__ includes class name, if present
        extra=f"|{namespace}" if namespace is not None else "",
    )