File: formatting.py

package info (click to toggle)
bluebrain-hpc-coding-conventions 1.0.0%2Bgit20221201-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 624 kB
  • sloc: python: 2,021; cpp: 220; makefile: 29
file content (107 lines) | stat: -rwxr-xr-x 3,241 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env python3

from collections import namedtuple
import glob
import os
import os.path as osp

import jinja2
import yaml


CONVENTION_ATTRS = ["title", "description", "clang_format", "snippet"]


class Convention(namedtuple("Convention", CONVENTION_ATTRS)):
    @staticmethod
    def from_file(clang_format, file):
        attrs = dict(description="", snippet="", clang_format={})

        def jinja_expand(content):
            template = jinja2.Template(content)
            return template.render(cf=clang_format)

        def cf_value(key):
            data = clang_format
            for key in key.split("."):
                if key in data:
                    data = data[key]
            return data

        def is_comment(line):
            return line.startswith("//")

        def is_cf_data(line):
            return line.startswith("// clang-format:")

        def get_cf_data(line):
            line = line[16:].lstrip()
            fields = [t.strip() for t in line.split(":", 1)]
            if len(fields) == 1:
                fields = [fields[0], cf_value(fields[0])]
            else:
                fields[1] = yaml.safe_load(fields[1])
            return dict([fields])

        with open(file) as istr:
            content = [line.rstrip() for line in istr.readlines()]
        # retrieve title
        assert is_comment(content[0])
        attrs["title"] = content[0].lstrip("//").lstrip()
        i = 1
        # eat empty lines
        while i < len(content) and not content[i]:
            i += 1
        # retrieve description
        while i < len(content) and is_comment(content[i]):
            if is_cf_data(content[i]):
                attrs["clang_format"].update(get_cf_data(content[i]))
            else:
                attrs["description"] += content[i].lstrip("//").lstrip() + "\n"
            i += 1
        attrs["description"] = jinja_expand(attrs["description"])

        # eat empty lines
        while i < len(content) and not content[i]:
            i += 1
        # retrieve code snippet
        while i < len(content):
            if not content[i].lstrip().startswith("// clang-format"):
                attrs["snippet"] += content[i] + "\n"
            i += 1
        basename = osp.splitext(osp.basename(file))[0]
        data = clang_format
        for key in basename.split("."):
            if key in data:
                data = data[key]
            else:
                break
        else:
            attrs["clang_format"][basename] = data
        return Convention(**attrs)


def load_conventions(path):
    with open("../../.clang-format") as istr:
        clang_format = yaml.safe_load(istr)
    assert osp.isdir(path)
    for file in sorted(glob.glob(path + os.sep + "*.cpp")):
        yield Convention.from_file(clang_format, file)


def build_documentation(template_str, ostr, **kwargs):
    template = jinja2.Template(template_str)
    template.stream(**kwargs).dump(ostr)


def main():
    with open("README.md.jinja") as istr:
        template_str = istr.read()
    with open("README.md", "w") as ostr:
        build_documentation(
            template_str, ostr, conventions=list(load_conventions("snippets"))
        )


if __name__ == "__main__":
    main()