def walk_orm_tree(obj,
                  debug: bool = False,
                  seen: Set = None,
                  skip_relationships_always: List[str] = None,
                  skip_relationships_by_tablename: Dict[str, List[str]] = None,
                  skip_all_relationships_for_tablenames: List[str] = None,
                  skip_all_objects_for_tablenames: List[str] = None) \
        -> Generator[object, None, None]:
    """
    Starting with a SQLAlchemy ORM object, this function walks a
    relationship tree, yielding each of the objects once.

    To skip attributes by name, put the attribute name(s) in
    ``skip_attrs_always``. To skip by table name, pass
    ``skip_attrs_by_tablename`` as e.g.

    .. code-block:: python

        {'sometable': ['attr1_to_skip', 'attr2_to_skip']}


    Args:
        obj: the SQLAlchemy ORM object to walk

        debug: be verbose

        seen: usually ``None``, but can be a set of objects marked as "already
            seen"; if an object is in this set, it is skipped

        skip_relationships_always: relationships are skipped if the
            relationship has a name in this (optional) list

        skip_relationships_by_tablename: optional dictionary mapping table
            names (keys) to relationship attribute names (values); if the
            "related table"/"relationship attribute" pair are in this
            dictionary, the relationship is skipped

        skip_all_relationships_for_tablenames: relationships are skipped if the
            the related table has a name in this (optional) list

        skip_all_objects_for_tablenames: if the object belongs to a table whose
            name is in this (optional) list, the object is skipped

    Yields:
        SQLAlchemy ORM objects (including the starting object)

    """
    # http://docs.sqlalchemy.org/en/latest/faq/sessions.html#faq-walk-objects
    skip_relationships_always = skip_relationships_always or []  # type: List[str]  # noqa
    skip_relationships_by_tablename = skip_relationships_by_tablename or {}  # type: Dict[str, List[str]]  # noqa
    skip_all_relationships_for_tablenames = skip_all_relationships_for_tablenames or []  # type: List[str]  # noqa
    skip_all_objects_for_tablenames = skip_all_objects_for_tablenames or []  # type: List[str]  # noqa
    stack = [obj]
    if seen is None:
        seen = set()
    while stack:
        obj = stack.pop(0)
        if obj in seen:
            continue
        tablename = obj.__tablename__
        if tablename in skip_all_objects_for_tablenames:
            continue
        seen.add(obj)
        if debug:
            log.debug("walk: yielding {!r}", obj)
        yield obj
        insp = inspect(obj)  # type: InstanceState
        for relationship in insp.mapper.relationships:  # type: RelationshipProperty  # noqa
            attrname = relationship.key
            # Skip?
            if attrname in skip_relationships_always:
                continue
            if tablename in skip_all_relationships_for_tablenames:
                continue
            if (tablename in skip_relationships_by_tablename and
                    attrname in skip_relationships_by_tablename[tablename]):
                continue
            # Process relationship
            if debug:
                log.debug("walk: following relationship {}", relationship)
            related = getattr(obj, attrname)
            if debug and related:
                log.debug("walk: queueing {!r}", related)
            if relationship.uselist:
                stack.extend(related)
            elif related is not None:
                stack.append(related)
Starting with a SQLAlchemy ORM object, this function walks a
    relationship tree, yielding each of the objects once.

    To skip attributes by name, put the attribute name(s) in
    ``skip_attrs_always``. To skip by table name, pass
    ``skip_attrs_by_tablename`` as e.g.

    .. code-block:: python

        {'sometable': ['attr1_to_skip', 'attr2_to_skip']}


    Args:
        obj: the SQLAlchemy ORM object to walk

        debug: be verbose

        seen: usually ``None``, but can be a set of objects marked as "already
            seen"; if an object is in this set, it is skipped

        skip_relationships_always: relationships are skipped if the
            relationship has a name in this (optional) list

        skip_relationships_by_tablename: optional dictionary mapping table
            names (keys) to relationship attribute names (values); if the
            "related table"/"relationship attribute" pair are in this
            dictionary, the relationship is skipped

        skip_all_relationships_for_tablenames: relationships are skipped if the
            the related table has a name in this (optional) list

        skip_all_objects_for_tablenames: if the object belongs to a table whose
            name is in this (optional) list, the object is skipped

    Yields:
        SQLAlchemy ORM objects (including the starting object)