podman

Форк
0
/
markdown-preprocess 
184 строки · 7.3 Кб
1
#!/usr/bin/env python3
2
#
3
# markdown-preprocess - filter *.md.in files, convert to .md
4
#
5
"""
6
Simpleminded include mechanism for podman man pages.
7
"""
8

9
import filecmp
10
import glob
11
import os
12
import re
13
import sys
14

15
class Preprocessor():
16
    """
17
    Doesn't really merit a whole OO approach, except we have a lot
18
    of state variables to pass around, and self is a convenient
19
    way to do that. Better than globals, anyway.
20
    """
21
    def __init__(self):
22
        self.infile = ''
23
        self.pod_or_container = ''
24
        self.used_by = {}
25

26
    def process(self, infile:str):
27
        """
28
        Main calling point: preprocesses one file
29
        """
30
        self.infile = infile
31
        # Some options are the same between containers and pods; determine
32
        # which description to use from the name of the source man page.
33
        self.pod_or_container = 'container'
34
        if '-pod-' in infile or '-kube-' in infile:
35
            self.pod_or_container = 'pod'
36

37
        # foo.md.in -> foo.md -- but always write to a tmpfile
38
        outfile = os.path.splitext(infile)[0]
39
        outfile_tmp = outfile + '.tmp.' + str(os.getpid())
40

41
        with open(infile, 'r', encoding='utf-8') as fh_in, open(outfile_tmp, 'w', encoding='utf-8', newline='\n') as fh_out:
42
            for line in fh_in:
43
                # '@@option foo' -> include file options/foo.md
44
                if line.startswith('@@option '):
45
                    _, optionname = line.strip().split(" ")
46
                    optionfile = os.path.join("options", optionname + '.md')
47
                    self.track_optionfile(optionfile)
48
                    self.insert_file(fh_out, optionfile)
49
                # '@@include relative-path/must-exist.md'
50
                elif line.startswith('@@include '):
51
                    _, path = line.strip().split(" ")
52
                    self.insert_file(fh_out, path)
53
                else:
54
                    fh_out.write(line)
55

56
        os.chmod(outfile_tmp, 0o444)
57
        os.rename(outfile_tmp, outfile)
58

59
    def track_optionfile(self, optionfile: str):
60
        """
61
        Keep track of which man pages use which option files
62
        """
63
        if optionfile not in self.used_by:
64
            self.used_by[optionfile] = []
65
        self.used_by[optionfile].append(self.podman_subcommand('full'))
66

67
    def rewrite_optionfiles(self):
68
        """
69
        Rewrite all option files, such that they include header comments
70
        cross-referencing all the man pages in which they're used.
71
        """
72
        for optionfile in self.used_by:
73
            tmpfile = optionfile + '.tmp.' + str(os.getpid())
74
            with open(optionfile, 'r', encoding='utf-8') as fh_in, open(tmpfile, 'w', encoding='utf-8', newline='\n') as fh_out:
75
                fh_out.write("####> This option file is used in:\n")
76
                used_by = ', '.join(x for x in self.used_by[optionfile])
77
                fh_out.write(f"####>   podman {used_by}\n")
78
                fh_out.write("####> If file is edited, make sure the changes\n")
79
                fh_out.write("####> are applicable to all of those.\n")
80
                for line in fh_in:
81
                    if not line.startswith('####>'):
82
                        fh_out.write(line)
83
            # Compare files; only rewrite if the new one differs
84
            if not filecmp.cmp(optionfile, tmpfile):
85
                os.unlink(optionfile)
86
                os.rename(tmpfile, optionfile)
87
            else:
88
                os.unlink(tmpfile)
89

90
    def insert_file(self, fh_out, path: str):
91
        """
92
        Reads one option file, writes it out to the given output filehandle
93
        """
94
        # Comment intended to help someone viewing the .md file.
95
        # Leading newline is important because if two lines are
96
        # consecutive without a break, sphinx (but not go-md2man)
97
        # treats them as one line and will unwantedly render the
98
        # comment in its output.
99
        fh_out.write("\n[//]: # (BEGIN included file " + path + ")\n")
100
        with open(path, 'r', encoding='utf-8') as fh_included:
101
            for opt_line in fh_included:
102
                if opt_line.startswith('####>'):
103
                    continue
104
                opt_line = self.replace_type(opt_line)
105
                opt_line = opt_line.replace('<<subcommand>>', self.podman_subcommand())
106
                opt_line = opt_line.replace('<<fullsubcommand>>', self.podman_subcommand('full'))
107
                fh_out.write(opt_line)
108
            fh_out.write("\n[//]: # (END   included file " + path + ")\n")
109

110
    def podman_subcommand(self, full=None) -> str:
111
        """
112
        Returns the string form of the podman command, based on man page name;
113
        e.g., 'foo bar' for podman-foo-bar.1.md.in
114
        """
115
        subcommand = self.infile
116
        # Special case: 'podman-pod-start' becomes just 'start'
117
        if not full:
118
            if subcommand.startswith("podman-pod-"):
119
                subcommand = subcommand[len("podman-pod-"):]
120
        if subcommand.startswith("podman-"):
121
            subcommand = subcommand[len("podman-"):]
122
        if subcommand.endswith(".1.md.in"):
123
            subcommand = subcommand[:-len(".1.md.in")]
124
        return subcommand.replace("-", " ")
125

126
    def replace_type(self, line: str) -> str:
127
        """
128
        Replace instances of '<<pod string|container string>>' with the
129
        appropriate one based on whether this is a pod-related man page
130
        or not.
131
        """
132
        # Internal helper function: determines the desired half of the <a|b> string
133
        def replwith(matchobj):
134
            lhs, rhs = matchobj[0].split('|')
135
            # Strip off '<<' and '>>'
136
            lhs = lhs[2:]
137
            rhs = rhs[:len(rhs)-2]
138

139
            # Check both sides for 'pod' followed by (non-"m" or end-of-string).
140
            # The non-m prevents us from triggering on 'podman', which could
141
            # conceivably be present in both sides. And we check for 'pod',
142
            # not 'container', because it's possible to have something like
143
            # <<container in pod|container>>.
144
            if re.match('.*pod([^m]|$)', lhs, re.IGNORECASE):
145
                if re.match('.*pod([^m]|$)', rhs, re.IGNORECASE):
146
                    raise Exception(f"'{matchobj[0]}' matches 'pod' in both left and right sides")
147
                # Only left-hand side has "pod"
148
                if self.pod_or_container == 'pod':
149
                    return lhs
150
                return rhs
151

152
            # 'pod' not in lhs, must be in rhs
153
            if not re.match('.*pod([^m]|$)', rhs, re.IGNORECASE):
154
                raise Exception(f"'{matchobj[0]}' does not match 'pod' in either side")
155
            if self.pod_or_container == 'pod':
156
                return rhs
157
            return lhs
158

159
        return re.sub(r'<<[^\|>]*\|[^\|>]*>>', replwith, line)
160

161

162
def main():
163
    "script entry point"
164
    script_dir = os.path.abspath(os.path.dirname(__file__))
165
    man_dir = os.path.join(script_dir,"../docs/source/markdown")
166

167
    try:
168
        os.chdir(man_dir)
169
    except FileNotFoundError as ex:
170
        raise Exception("Please invoke me from the base repo dir") from ex
171

172
    # No longer possible to invoke us with args: reject any such invocation
173
    if len(sys.argv) > 1:
174
        raise Exception("This script accepts no arguments")
175

176
    preprocessor = Preprocessor()
177
    for infile in sorted(glob.glob('*.md.in')):
178
        preprocessor.process(infile)
179

180
    # Now rewrite all option files
181
    preprocessor.rewrite_optionfiles()
182

183
if __name__ == "__main__":
184
    main()
185

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.