def text_fd_to_metric_families(fd):
name = None<EOL>allowed_names = []<EOL>eof = False<EOL>seen_metrics = set()<EOL>def build_metric(name, documentation, typ, unit, samples):<EOL><INDENT>if name in seen_metrics:<EOL><INDENT>raise ValueError("<STR_LIT>" + name)<EOL><DEDENT>seen_metrics.add(name)<EOL>if typ is None:<EOL><INDENT>typ = '<STR_LIT>'<EOL><DEDENT>if documentation is None:<EOL><INDENT>documentation = '<STR_LIT>'<EOL><DEDENT>if unit is None:<EOL><INDENT>unit = '<STR_LIT>'<EOL><DEDENT>if unit and not name.endswith("<STR_LIT:_>" + unit):<EOL><INDENT>raise ValueError("<STR_LIT>" + name)<EOL><DEDENT>if unit and typ in ['<STR_LIT:info>', '<STR_LIT>']:<EOL><INDENT>raise ValueError("<STR_LIT>" + name)<EOL><DEDENT>if typ in ['<STR_LIT>', '<STR_LIT>']:<EOL><INDENT>_check_histogram(samples, name)<EOL><DEDENT>metric = Metric(name, documentation, typ, unit)<EOL>metric.samples = samples<EOL>return metric<EOL><DEDENT>for line in fd:<EOL><INDENT>if line[-<NUM_LIT:1>] == '<STR_LIT:\n>':<EOL><INDENT>line = line[:-<NUM_LIT:1>]<EOL><DEDENT>if eof:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if line == '<STR_LIT>':<EOL><INDENT>eof = True<EOL><DEDENT>elif line.startswith('<STR_LIT:#>'):<EOL><INDENT>parts = line.split('<STR_LIT:U+0020>', <NUM_LIT:3>)<EOL>if len(parts) < <NUM_LIT:4>:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if parts[<NUM_LIT:2>] == name and samples:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if parts[<NUM_LIT:2>] != name:<EOL><INDENT>if name is not None:<EOL><INDENT>yield build_metric(name, documentation, typ, unit, samples)<EOL><DEDENT>name = parts[<NUM_LIT:2>]<EOL>unit = None<EOL>typ = None<EOL>documentation = None<EOL>group = None<EOL>seen_groups = set()<EOL>group_timestamp = None<EOL>group_timestamp_samples = set()<EOL>samples = []<EOL>allowed_names = [parts[<NUM_LIT:2>]]<EOL><DEDENT>if parts[<NUM_LIT:1>] == '<STR_LIT>':<EOL><INDENT>if documentation is not None:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if len(parts) == <NUM_LIT:4>:<EOL><INDENT>documentation = _unescape_help(parts[<NUM_LIT:3>])<EOL><DEDENT>elif len(parts) == <NUM_LIT:3>:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT><DEDENT>elif parts[<NUM_LIT:1>] == '<STR_LIT>':<EOL><INDENT>if typ is not None:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>typ = parts[<NUM_LIT:3>]<EOL>if typ == '<STR_LIT>':<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>allowed_names = {<EOL>'<STR_LIT>': ['<STR_LIT>', '<STR_LIT>'],<EOL>'<STR_LIT>': ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'],<EOL>'<STR_LIT>': ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'],<EOL>'<STR_LIT>': ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>'],<EOL>'<STR_LIT:info>': ['<STR_LIT>'],<EOL>}.get(typ, ['<STR_LIT>'])<EOL>allowed_names = [name + n for n in allowed_names]<EOL><DEDENT>elif parts[<NUM_LIT:1>] == '<STR_LIT>':<EOL><INDENT>if unit is not None:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>unit = parts[<NUM_LIT:3>]<EOL><DEDENT>else:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>sample = _parse_sample(line)<EOL>if sample.name not in allowed_names:<EOL><INDENT>if name is not None:<EOL><INDENT>yield build_metric(name, documentation, typ, unit, samples)<EOL><DEDENT>name = sample.name<EOL>documentation = None<EOL>unit = None<EOL>typ = '<STR_LIT>'<EOL>samples = []<EOL>group = None<EOL>group_timestamp = None<EOL>group_timestamp_samples = set()<EOL>seen_groups = set()<EOL>allowed_names = [sample.name]<EOL><DEDENT>if typ == '<STR_LIT>' and name not in sample.labels:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if (typ in ['<STR_LIT>', '<STR_LIT>'] and name + '<STR_LIT>' == sample.name<EOL>and (float(sample.labels.get('<STR_LIT>', -<NUM_LIT:1>)) < <NUM_LIT:0><EOL>or sample.labels['<STR_LIT>'] != floatToGoString(sample.labels['<STR_LIT>']))):<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if (typ == '<STR_LIT>' and name == sample.name<EOL>and (not (<NUM_LIT:0> <= float(sample.labels.get('<STR_LIT>', -<NUM_LIT:1>)) <= <NUM_LIT:1>)<EOL>or sample.labels['<STR_LIT>'] != floatToGoString(sample.labels['<STR_LIT>']))):<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>g = tuple(sorted(_group_for_sample(sample, name, typ).items()))<EOL>if group is not None and g != group and g in seen_groups:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if group is not None and g == group:<EOL><INDENT>if (sample.timestamp is None) != (group_timestamp is None):<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if group_timestamp is not None and group_timestamp > sample.timestamp and typ != '<STR_LIT:info>':<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>group_timestamp_samples = set()<EOL><DEDENT>series_id = (sample.name, tuple(sorted(sample.labels.items())))<EOL>if sample.timestamp != group_timestamp or series_id not in group_timestamp_samples:<EOL><INDENT>samples.append(sample)<EOL><DEDENT>group_timestamp_samples.add(series_id)<EOL>group = g<EOL>group_timestamp = sample.timestamp<EOL>seen_groups.add(g)<EOL>if typ == '<STR_LIT>' and sample.value not in [<NUM_LIT:0>, <NUM_LIT:1>]:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if typ == '<STR_LIT:info>' and sample.value != <NUM_LIT:1>:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if typ == '<STR_LIT>' and name == sample.name and sample.value < <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if sample.name[len(name):] in ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'] and math.isnan(<EOL>sample.value):<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if sample.name[len(name):] in ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>',<EOL>'<STR_LIT>'] and sample.value < <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT>if sample.exemplar and not (<EOL>typ in ['<STR_LIT>', '<STR_LIT>']<EOL>and sample.name.endswith('<STR_LIT>')):<EOL><INDENT>raise ValueError("<STR_LIT>" + line)<EOL><DEDENT><DEDENT><DEDENT>if name is not None:<EOL><INDENT>yield build_metric(name, documentation, typ, unit, samples)<EOL><DEDENT>if not eof:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>
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.