1SEP = "/"
2
3
4def _splitnode(nodeid):
5    """Split a nodeid into constituent 'parts'.
6
7    Node IDs are strings, and can be things like:
8        ''
9        'testing/code'
10        'testing/code/test_excinfo.py'
11        'testing/code/test_excinfo.py::TestFormattedExcinfo::()'
12
13    Return values are lists e.g.
14        []
15        ['testing', 'code']
16        ['testing', 'code', 'test_excinfo.py']
17        ['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo', '()']
18    """
19    if nodeid == '':
20        # If there is no root node at all, return an empty list so the caller's logic can remain sane
21        return []
22    parts = nodeid.split(SEP)
23    # Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()'
24    parts[-1:] = parts[-1].split("::")
25    return parts
26
27
28def ischildnode(baseid, nodeid):
29    """Return True if the nodeid is a child node of the baseid.
30
31    E.g. 'foo/bar::Baz::()' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
32    """
33    base_parts = _splitnode(baseid)
34    node_parts = _splitnode(nodeid)
35    if len(node_parts) < len(base_parts):
36        return False
37    return node_parts[:len(base_parts)] == base_parts
38