docs: def product(*generators, repeat=1):
      """Cartesian product akin to :func:`itertools.product`, but accepting
      generator functions
  
      Unlike :func:`itertools.product` this function does not convert the input
      iterables into tuples. Thus, it can handle large or infinite inputs. As a
      drawback, however, it only works with "restartable" iterables (something
      that :func:`iter` can repeatably turn into an iterator, or a generator
      function (but not the generator iterator that is returned by that
      generator function)
  
      Args:
          generators: list of restartable iterators or generator functions
          repeat: number of times `generators` should be repeated
  
      Adapted from https://stackoverflow.com/q/12093364/
      """
      if len(generators) == 0:
          yield ()
      else:
          generators = generators * repeat
          it = generators[0]
          for item in it() if callable(it) else iter(it):
              for items in product(*generators[1:]):
                  yield (item, ) + items