def traverse(_object: Any, max_length: int=None, max_string: int=None) -> Node:
    'Traverse object and generate a tree.\n\n    Args:\n        _object (Any): Object to be traversed.\n        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.\n            Defaults to None.\n        max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.\n            Defaults to None.\n\n    Returns:\n        Node: The root of a tree structure which can be used to render a pretty repr.\n    '

    def to_repr(obj: Any) -> str:
        'Get repr string for an object, but catch errors.'
        if ((max_string is not None) and isinstance(obj, (bytes, str)) and (len(obj) > max_string)):
            truncated = (len(obj) - max_string)
            obj_repr = f'{obj[:max_string]!r}+{truncated}'
        else:
            try:
                obj_repr = repr(obj)
            except Exception as error:
                obj_repr = f"<repr-error '{error}'>"
        return obj_repr
    visited_ids: Set[int] = set()
    push_visited = visited_ids.add
    pop_visited = visited_ids.remove

    def _traverse(obj: Any, root: bool=False) -> Node:
        'Walk the object depth first.'
        obj_type = type(obj)
        py_version = (sys.version_info.major, sys.version_info.minor)
        children: List[Node]

        def iter_rich_args(rich_args) -> Iterable[Union[(Any, Tuple[(str, Any)])]]:
            for arg in rich_args:
                if isinstance(arg, tuple):
                    if (len(arg) == 3):
                        (key, child, default) = arg
                        if (default == child):
                            continue
                        (yield (key, child))
                    elif (len(arg) == 2):
                        (key, child) = arg
                        (yield (key, child))
                    elif (len(arg) == 1):
                        (yield arg[0])
                else:
                    (yield arg)
        if hasattr(obj, '__rich_repr__'):
            args = list(iter_rich_args(obj.__rich_repr__()))
            if args:
                children = []
                append = children.append
                node = Node(open_brace=f'{obj.__class__.__name__}(', close_brace=')', children=children, last=root)
                for (last, arg) in loop_last(args):
                    if isinstance(arg, tuple):
                        (key, child) = arg
                        child_node = _traverse(child)
                        child_node.last = last
                        child_node.key_repr = key
                        child_node.last = last
                        child_node.key_separator = '='
                        append(child_node)
                    else:
                        child_node = _traverse(arg)
                        child_node.last = last
                        append(child_node)
            else:
                node = Node(value_repr=f'{obj.__class__.__name__}()', children=[], last=root)
        elif (is_dataclass(obj) and (not isinstance(obj, type)) and (('__create_fn__' in obj.__repr__.__qualname__) or (py_version == (3, 6)))):
            obj_id = id(obj)
            if (obj_id in visited_ids):
                return Node(value_repr='...')
            push_visited(obj_id)
            children = []
            append = children.append
            node = Node(open_brace=f'{obj.__class__.__name__}(', close_brace=')', children=children, last=root)
            for (last, field) in loop_last(fields(obj)):
                if field.repr:
                    child_node = _traverse(getattr(obj, field.name))
                    child_node.key_repr = field.name
                    child_node.last = last
                    child_node.key_separator = '='
                    append(child_node)
            pop_visited(obj_id)
        elif (obj_type in _CONTAINERS):
            obj_id = id(obj)
            if (obj_id in visited_ids):
                return Node(value_repr='...')
            push_visited(obj_id)
            (open_brace, close_brace, empty) = _BRACES[obj_type](obj)
            if obj:
                children = []
                node = Node(open_brace=open_brace, close_brace=close_brace, children=children, last=root)
                append = children.append
                num_items = len(obj)
                last_item_index = (num_items - 1)
                if isinstance(obj, _MAPPING_CONTAINERS):
                    iter_items = iter(obj.items())
                    if (max_length is not None):
                        iter_items = islice(iter_items, max_length)
                    for (index, (key, child)) in enumerate(iter_items):
                        child_node = _traverse(child)
                        child_node.key_repr = to_repr(key)
                        child_node.last = (index == last_item_index)
                        append(child_node)
                else:
                    iter_values = iter(obj)
                    if (max_length is not None):
                        iter_values = islice(iter_values, max_length)
                    for (index, child) in enumerate(iter_values):
                        child_node = _traverse(child)
                        child_node.last = (index == last_item_index)
                        append(child_node)
                if ((max_length is not None) and (num_items > max_length)):
                    append(Node(value_repr=f'... +{(num_items - max_length)}', last=True))
            else:
                node = Node(empty=empty, children=[], last=root)
            pop_visited(obj_id)
        else:
            node = Node(value_repr=to_repr(obj), last=root)
        node.is_tuple = isinstance(obj, tuple)
        return node
    node = _traverse(_object, root=True)
    return node
Traverse object and generate a tree.

Args:
    _object (Any): Object to be traversed.
    max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
        Defaults to None.
    max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.
        Defaults to None.

Returns:
    Node: The root of a tree structure which can be used to render a pretty repr.
def traverse(_object: Any, max_length: int=None, max_string: int=None) -> Node:
    'Traverse object and generate a tree.\n\n    Args:\n        _object (Any): Object to be traversed.\n        max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.\n            Defaults to None.\n        max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.\n            Defaults to None.\n\n    Returns:\n        Node: The root of a tree structure which can be used to render a pretty repr.\n    '

    def to_repr(obj: Any) -> str:
        'Get repr string for an object, but catch errors.'
        if ((max_string is not None) and isinstance(obj, (bytes, str)) and (len(obj) > max_string)):
            truncated = (len(obj) - max_string)
            obj_repr = f'{obj[:max_string]!r}+{truncated}'
        else:
            try:
                obj_repr = repr(obj)
            except Exception as error:
                obj_repr = f"<repr-error '{error}'>"
        return obj_repr
    visited_ids: Set[int] = set()
    push_visited = visited_ids.add
    pop_visited = visited_ids.remove

    def _traverse(obj: Any, root: bool=False) -> Node:
        'Walk the object depth first.'
        obj_type = type(obj)
        py_version = (sys.version_info.major, sys.version_info.minor)
        children: List[Node]

        def iter_rich_args(rich_args) -> Iterable[Union[(Any, Tuple[(str, Any)])]]:
            for arg in rich_args:
                if isinstance(arg, tuple):
                    if (len(arg) == 3):
                        (key, child, default) = arg
                        if (default == child):
                            continue
                        (yield (key, child))
                    elif (len(arg) == 2):
                        (key, child) = arg
                        (yield (key, child))
                    elif (len(arg) == 1):
                        (yield arg[0])
                else:
                    (yield arg)
        if hasattr(obj, '__rich_repr__'):
            args = list(iter_rich_args(obj.__rich_repr__()))
            if args:
                children = []
                append = children.append
                node = Node(open_brace=f'{obj.__class__.__name__}(', close_brace=')', children=children, last=root)
                for (last, arg) in loop_last(args):
                    if isinstance(arg, tuple):
                        (key, child) = arg
                        child_node = _traverse(child)
                        child_node.last = last
                        child_node.key_repr = key
                        child_node.last = last
                        child_node.key_separator = '='
                        append(child_node)
                    else:
                        child_node = _traverse(arg)
                        child_node.last = last
                        append(child_node)
            else:
                node = Node(value_repr=f'{obj.__class__.__name__}()', children=[], last=root)
        elif (is_dataclass(obj) and (not isinstance(obj, type)) and (('__create_fn__' in obj.__repr__.__qualname__) or (py_version == (3, 6)))):
            obj_id = id(obj)
            if (obj_id in visited_ids):
                return Node(value_repr='...')
            push_visited(obj_id)
            children = []
            append = children.append
            node = Node(open_brace=f'{obj.__class__.__name__}(', close_brace=')', children=children, last=root)
            for (last, field) in loop_last(fields(obj)):
                if field.repr:
                    child_node = _traverse(getattr(obj, field.name))
                    child_node.key_repr = field.name
                    child_node.last = last
                    child_node.key_separator = '='
                    append(child_node)
            pop_visited(obj_id)
        elif (obj_type in _CONTAINERS):
            obj_id = id(obj)
            if (obj_id in visited_ids):
                return Node(value_repr='...')
            push_visited(obj_id)
            (open_brace, close_brace, empty) = _BRACES[obj_type](obj)
            if obj:
                children = []
                node = Node(open_brace=open_brace, close_brace=close_brace, children=children, last=root)
                append = children.append
                num_items = len(obj)
                last_item_index = (num_items - 1)
                if isinstance(obj, _MAPPING_CONTAINERS):
                    iter_items = iter(obj.items())
                    if (max_length is not None):
                        iter_items = islice(iter_items, max_length)
                    for (index, (key, child)) in enumerate(iter_items):
                        child_node = _traverse(child)
                        child_node.key_repr = to_repr(key)
                        child_node.last = (index == last_item_index)
                        append(child_node)
                else:
                    iter_values = iter(obj)
                    if (max_length is not None):
                        iter_values = islice(iter_values, max_length)
                    for (index, child) in enumerate(iter_values):
                        child_node = _traverse(child)
                        child_node.last = (index == last_item_index)
                        append(child_node)
                if ((max_length is not None) and (num_items > max_length)):
                    append(Node(value_repr=f'... +{(num_items - max_length)}', last=True))
            else:
                node = Node(empty=empty, children=[], last=root)
            pop_visited(obj_id)
        else:
            node = Node(value_repr=to_repr(obj), last=root)
        node.is_tuple = isinstance(obj, tuple)
        return node
    node = _traverse(_object, root=True)
    return node