docs: def text_fd_to_metric_families(fd):
      """Parse Prometheus text format from a file descriptor.
  
      This is a laxer parser than the main Go parser,
      so successful parsing does not imply that the parsed
      text meets the specification.
  
      Yields Metric's.
      """
      name = None
      allowed_names = []
      eof = False
  
      seen_metrics = set()
  
      def build_metric(name, documentation, typ, unit, samples):
          if name in seen_metrics:
              raise ValueError("Duplicate metric: " + name)
          seen_metrics.add(name)
          if typ is None:
              typ = 'unknown'
          if documentation is None:
              documentation = ''
          if unit is None:
              unit = ''
          if unit and not name.endswith("_" + unit):
              raise ValueError("Unit does not match metric name: " + name)
          if unit and typ in ['info', 'stateset']:
              raise ValueError("Units not allowed for this metric type: " + name)
          if typ in ['histogram', 'gaugehistogram']:
              _check_histogram(samples, name)
          metric = Metric(name, documentation, typ, unit)
          # TODO: check labelvalues are valid utf8
          metric.samples = samples
          return metric
  
      for line in fd:
          if line[-1] == '\n':
              line = line[:-1]
  
          if eof:
              raise ValueError("Received line after # EOF: " + line)
  
          if line == '# EOF':
              eof = True
          elif line.startswith('#'):
              parts = line.split(' ', 3)
              if len(parts) < 4:
                  raise ValueError("Invalid line: " + line)
              if parts[2] == name and samples:
                  raise ValueError("Received metadata after samples: " + line)
              if parts[2] != name:
                  if name is not None:
                      yield build_metric(name, documentation, typ, unit, samples)
                  # New metric
                  name = parts[2]
                  unit = None
                  typ = None
                  documentation = None
                  group = None
                  seen_groups = set()
                  group_timestamp = None
                  group_timestamp_samples = set()
                  samples = []
                  allowed_names = [parts[2]]
  
              if parts[1] == 'HELP':
                  if documentation is not None:
                      raise ValueError("More than one HELP for metric: " + line)
                  if len(parts) == 4:
                      documentation = _unescape_help(parts[3])
                  elif len(parts) == 3:
                      raise ValueError("Invalid line: " + line)
              elif parts[1] == 'TYPE':
                  if typ is not None:
                      raise ValueError("More than one TYPE for metric: " + line)
                  typ = parts[3]
                  if typ == 'untyped':
                      raise ValueError("Invalid TYPE for metric: " + line)
                  allowed_names = {
                      'counter': ['_total', '_created'],
                      'summary': ['_count', '_sum', '', '_created'],
                      'histogram': ['_count', '_sum', '_bucket', '_created'],
                      'gaugehistogram': ['_gcount', '_gsum', '_bucket'],
                      'info': ['_info'],
                  }.get(typ, [''])
                  allowed_names = [name + n for n in allowed_names]
              elif parts[1] == 'UNIT':
                  if unit is not None:
                      raise ValueError("More than one UNIT for metric: " + line)
                  unit = parts[3]
              else:
                  raise ValueError("Invalid line: " + line)
          else:
              sample = _parse_sample(line)
              if sample.name not in allowed_names:
                  if name is not None:
                      yield build_metric(name, documentation, typ, unit, samples)
                  # Start an unknown metric.
                  name = sample.name
                  documentation = None
                  unit = None
                  typ = 'unknown'
                  samples = []
                  group = None
                  group_timestamp = None
                  group_timestamp_samples = set()
                  seen_groups = set()
                  allowed_names = [sample.name]
  
              if typ == 'stateset' and name not in sample.labels:
                  raise ValueError("Stateset missing label: " + line)
              if (typ in ['histogram', 'gaugehistogram'] and name + '_bucket' == sample.name
                      and (float(sample.labels.get('le', -1)) < 0
                           or sample.labels['le'] != floatToGoString(sample.labels['le']))):
                  raise ValueError("Invalid le label: " + line)
              if (typ == 'summary' and name == sample.name
                      and (not (0 <= float(sample.labels.get('quantile', -1)) <= 1)
                           or sample.labels['quantile'] != floatToGoString(sample.labels['quantile']))):
                  raise ValueError("Invalid quantile label: " + line)
  
              g = tuple(sorted(_group_for_sample(sample, name, typ).items()))
              if group is not None and g != group and g in seen_groups:
                  raise ValueError("Invalid metric grouping: " + line)
              if group is not None and g == group:
                  if (sample.timestamp is None) != (group_timestamp is None):
                      raise ValueError("Mix of timestamp presence within a group: " + line)
                  if group_timestamp is not None and group_timestamp > sample.timestamp and typ != 'info':
                      raise ValueError("Timestamps went backwards within a group: " + line)
              else:
                  group_timestamp_samples = set()
  
              series_id = (sample.name, tuple(sorted(sample.labels.items())))
              if sample.timestamp != group_timestamp or series_id not in group_timestamp_samples:
                  # Not a duplicate due to timestamp truncation.
                  samples.append(sample)
              group_timestamp_samples.add(series_id)
  
              group = g
              group_timestamp = sample.timestamp
              seen_groups.add(g)
  
              if typ == 'stateset' and sample.value not in [0, 1]:
                  raise ValueError("Stateset samples can only have values zero and one: " + line)
              if typ == 'info' and sample.value != 1:
                  raise ValueError("Info samples can only have value one: " + line)
              if typ == 'summary' and name == sample.name and sample.value < 0:
                  raise ValueError("Quantile values cannot be negative: " + line)
              if sample.name[len(name):] in ['_total', '_sum', '_count', '_bucket', '_gcount', '_gsum'] and math.isnan(
                      sample.value):
                  raise ValueError("Counter-like samples cannot be NaN: " + line)
              if sample.name[len(name):] in ['_total', '_sum', '_count', '_bucket', '_gcount',
                                             '_gsum'] and sample.value < 0:
                  raise ValueError("Counter-like samples cannot be negative: " + line)
              if sample.exemplar and not (
                      typ in ['histogram', 'gaugehistogram']
                      and sample.name.endswith('_bucket')):
                  raise ValueError("Invalid line only histogram/gaugehistogram buckets can have exemplars: " + line)
  
      if name is not None:
          yield build_metric(name, documentation, typ, unit, samples)
  
      if not eof:
          raise ValueError("Missing # EOF at end")