def get_module_members(
    mod: Union[str, ModuleType], aliases_are_defs: bool = False
) -> Optional[Members]:
    """Get all members of a module organized into exports, definitions;
    three types of access: public, friends, privates; and two levels of API:
    high-level and low-level.

    Args:
        mod: The module or its name.

        aliases_are_defs: If True, members that are aliases for definitions in other
            modules are considered definitions here too. This is to catch
            deprecation aliases.

    Returns:
        A namedtuples (modname, exports, definitions, access_publics,
            access_friends, access_privates, api_highs, api_lows) where the all
            but the first are iterables of namedtuples (modname, qualname, val)
            where qualname is the fully qualified name of the member and val is
            the value of the member.
    """

    if isinstance(mod, str):
        try:
            mod = importlib.import_module(mod)
        except Exception as e:
            if mod == "trulens.core.database.migrations.env":
                # This cannot be imported except by alembic. Skip this import
                # error.
                return None

            raise ValueError(f"Could not import module {mod}.") from e

    definitions: List[Member] = []
    exports: List[Member] = []
    publics: List[Member] = []
    friends: List[Member] = []
    privates: List[Member] = []

    highs = []
    lows = []

    export_set: Set[str] = set(inspect.getattr_static(mod, "__all__", []))

    classes = set()

    for name, val in pycompat_utils.getmembers_static(mod):
        qualname = mod.__name__ + "." + name
        member = Member(
            mod, name=name, qualname=qualname, val=val, typ=type(val)
        )

        is_def = False
        is_public = False
        is_export = False
        is_base = False
        is_experimental = False

        if name.lower().startswith("experimental"):
            is_experimental = True

        if aliases_are_defs or _isdefinedin(val, mod):
            is_def = True
            definitions.append(member)

            if inspect.isclass(val):
                classes.add(val)

        if _isdefinedin(val, None):
            # Is a base type that does not define a owner module.
            is_base = True

        if name in export_set:
            is_export = True
            exports.append(member)

        if (
            name.startswith("__")
            and name.endswith("__")
            and not hasattr(builtins, name)
            and name
            not in [
                "__file__",
                "__cached__",
                "__builtins__",
                "__warningregistry__",
                "__path__",
            ]
        ):
            # Checking if name is in builtins filters out standard module
            # attributes like __package__. A few other standard attributes are
            # filtered out too.
            is_public = True
            group = publics

        elif name.startswith("__") and not name.endswith("__"):
            group = privates

        elif name.startswith("_"):
            group = friends

        else:
            if (
                not is_experimental
                and name not in ["TYPE_CHECKING"]  # skip this common value
                and (len(name) > 1 or not isinstance(val, TypeVar))
            ):  # skip generic type vars
                is_public = True
                group = publics
            else:
                is_public = False
                group = privates

        group.append(member)

        if (not is_public) and (name in export_set):
            raise ValueError(
                f"Member {qualname} is both exported and not public."
            )

        if is_public:
            if is_export:
                highs.append(member)
            elif is_def or is_base:
                lows.append(member)

    return Members(
        obj=mod,
        version=None if not hasattr(mod, "__version__") else mod.__version__,
        exports=exports,
        definitions=definitions,
        access_publics=publics,
        access_friends=friends,
        access_privates=privates,
        api_highs=highs,
        api_lows=lows,
    )
Get all members of a module organized into exports, definitions;
    three types of access: public, friends, privates; and two levels of API:
    high-level and low-level.

    Args:
        mod: The module or its name.

        aliases_are_defs: If True, members that are aliases for definitions in other
            modules are considered definitions here too. This is to catch
            deprecation aliases.

    Returns:
        A namedtuples (modname, exports, definitions, access_publics,
            access_friends, access_privates, api_highs, api_lows) where the all
            but the first are iterables of namedtuples (modname, qualname, val)
            where qualname is the fully qualified name of the member and val is
            the value of the member.
    
tests/utils.py