docs: def from_bubble_data(bbldata:iter, oriented:bool=False,
                           symmetric_edges:bool=True) -> 'BubbleTree':
          """Return a BubbleTree instance.
  
          bbldata -- lines in bubble bbltree
          oriented -- True: returned BubbleTree is oriented
  
          """
          # get structure as two dicts
          edges, inclusions = defaultdict(set), defaultdict(set)
          used_in_edges = set()
          for line in bbldata:
              if not line: continue
              ltype, *payload = line
              if ltype == 'EDGE':
                  source, target = payload
                  edges[source].add(target)
                  used_in_edges.add(source)
                  used_in_edges.add(target)
              elif ltype == 'SET':
                  setname = payload[0]
                  inclusions[setname]  # create it if not already populated
              elif ltype == 'NODE':
                  nodename = payload[0]
                  inclusions[nodename] = ()  # a node can't contain anything
              elif ltype == 'IN':
                  contained, container = payload
                  inclusions[container].add(contained)
              else:  # comment, empty or error
                  if ltype not in {'COMMENT', 'EMPTY', 'ERROR'}:
                      raise ValueError("The following line is not a valid "
                                       "type ({}): '{}'".format(ltype, payload))
                  else:  # it's a comment, an empty line or an error
                      pass
  
  
          # all (power)nodes used in edges should be present in inclusions tree
          for node in used_in_edges:
              if node not in inclusions:  # contains nothing, so its a node
                  inclusions[node] = ()
  
          # all pure nodes needs to be a key in inclusions
          for node in set(it.chain.from_iterable(inclusions.values())):
              # an element that is not in inclusion is either:
              #  - a node not explicitely defined in a NODE line
              #  - a powernode that contains nothing and not explicitely defined in a SET line
              # the second case is meaningless : this is the case for any unused powernode name.
              # Consequently, elements not in inclusions are nodes.
              if node not in inclusions:
                  inclusions[node] = ()
  
          # find the roots
          not_root = set(it.chain.from_iterable(inclusions.values()))
          roots = frozenset(frozenset(inclusions.keys()) - not_root)
  
          # build the (oriented) bubble tree
          symmetric_edges = symmetric_edges and not oriented
          if symmetric_edges:
              edges = utils.completed_graph(edges)
          return BubbleTree(edges=edges, inclusions=dict(inclusions),
                            roots=roots, oriented=oriented, symmetric_edges=symmetric_edges)