File: tag_parser.py

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (174 lines) | stat: -rw-r--r-- 7,385 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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import argparse
import fnmatch
import os
from collections import defaultdict

arguments = argparse.ArgumentParser(
    formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    description="Parses files for content tags and creates and a doxygen suitable listing from the parse results.",
    epilog="%(prog)s Parses FILES in the MATCH directory for TAGs."
    "The comment lines may be in either # or /// form in a file,"
    " but only one form throughout a file.  The results of the parse are"
    ' formatted for a doxygen ".dox" file.',
)
arguments.add_argument("--match", default="./", help="Root directory to search for files to parse.")
arguments.add_argument(
    "-f", "--files", metavar="MASK", nargs="*", default=["*.*"], help="One or more file masks of files to parse."
)
arguments.add_argument(
    "-x",
    "--exclude",
    metavar="MASK",
    nargs="*",
    default=[],
    help="One or more file masks to exclude from results matching source_mask(s).",
)
arguments.add_argument(
    "--tag",
    nargs=2,
    default=["@content_tag{", "}"],
    help="Set of opening and closing" " strings which surround a tag name and precede the tag documentation.",
)
arguments.add_argument("-o", "--output", default="output.txt", help="Full path to an output file.")
arguments.add_argument(
    "--link_source",
    metavar="CONTEXT_DIR BASE_URL LINE_PREFIX]",
    nargs="*",
    default=[],
    help="Display linked source before each tag documentation. Requires arguments in order:"
    " CONTEXT_DIR BASE_URL LINE_PREFIX. File sources will link to"
    " {BASE_URL}{FILE}{LINE_PREFIX}{LINE_NUM}, where FILE is the relative path of CONTEXT_DIR."
    " e.g. Given: FILE=a/b/c/xyz.txt LINE_NUM=4 CONTEXT_DIR=a/b BASE_URL=http://a.it LINE_PREFIX=?"
    " Link result will be: http://a.it/c/xyz.txt?4",
)
arguments.add_argument(
    "--dry_run", action="store_true", help="Does not write to OUTPUT, instead printing info" " to stdout."
)

args = vars(arguments.parse_args())

tag_open = args.get("tag")[0]
tag_close = args.get("tag")[1]


def add_doc_source(_file_name, _line_number, _content, _tags):
    fn = _file_name.lstrip("./")
    # index documentation by tag name storing the source file name, line number, and documentation string
    _tags[_content[0]].add((fn, _line_number, _content[1].strip()))


def parse_file(_parse_file, _tags):  # noqa: C901
    with open(_parse_file, encoding="utf-8") as f:
        match_line = 0
        content = []
        special_comments_this_file = None
        # Documentation may span multiple lines for a content tag.
        # Matched lines are only stored in _tags once an end condition is met.
        # End conditions are: a line not starting a comment, a new content tag, end of file
        for raw_line in enumerate(f):
            if raw_line[1].lstrip().startswith("#") and special_comments_this_file is not True:
                if special_comments_this_file is None:
                    special_comments_this_file = False
                if tag_open in raw_line[1]:
                    if match_line:
                        # in event focs scripts decide to stack one documentation on top of another
                        add_doc_source(_parse_file, match_line, content, _tags)
                    # store content and line for later addition
                    content = "".join(raw_line[1].split(tag_open, 1)[1]).split(tag_close, 1)
                    content[1] = content[1].strip()
                    match_line = raw_line[0] + 1
                elif match_line:
                    # not a new content tag, append to previous line description
                    content[1] += " " + raw_line[1].lstrip("# ")
            elif raw_line[1].lstrip().startswith("/// ") and special_comments_this_file is not False:
                if special_comments_this_file is None:
                    special_comments_this_file = True
                    content = ["# "]
                if tag_open in raw_line[1]:
                    if match_line:
                        # in event focs scripts decide to stack one documentation on top of another
                        add_doc_source(_parse_file, match_line, content, _tags)
                    # store content and line for later addition
                    content = "".join(raw_line[1].split(tag_open, 1)[1]).split(tag_close, 1)
                    content[1] = content[1].strip()
                    match_line = raw_line[0] + 1
                elif match_line:
                    # not a new content tag, append to previous line description
                    content[1] += " " + raw_line[1].lstrip("/// ")
            elif match_line:
                # end of description, add a node for this source
                add_doc_source(_parse_file, match_line, content, _tags)
                match_line = 0
                content = []
        # EOF
        if match_line:
            # in event a matched comment is the last line in a file
            add_doc_source(_parse_file, match_line, content, _tags)


def get_link(_file, _line_number):
    link_args = args.get("link_source")
    if len(link_args) != 3:
        if link_args:
            print("Invalid link_source arguments: ", link_args)
        return ""
    base_path = os.path.relpath(_file, link_args[0].lstrip("./"))
    link = os.path.join(link_args[1], base_path)
    link += str.format("{0}{1}", link_args[2], _line_number)
    return str.format("[{0}]({1})", base_path, link)


all_tags = defaultdict(set)

# parse files for tags
for _file_path, _, _files in os.walk(args.get("match")):
    for file_name in enumerate(_files):
        file_match = False
        # if this file should be checked
        for mask in args.get("files"):
            if fnmatch.fnmatch(file_name[1], mask):
                file_match = True
        if file_match:
            # check for exclusions
            for mask in args.get("exclude"):
                if fnmatch.fnmatch(file_name[1], mask):
                    file_match = False
            if file_match:
                parse_file(os.path.join(_file_path, file_name[1]), all_tags)

output_buff = []

# create tags formatted for doxygen

output_buff.append("/**\n")
output_buff.append("@page content_tag_listing Content Definition Tag Listing\n")
output_buff.append(
    "This page is required to load parsed data into the documentation, the actual listing can be found @ref content_tags\n"
)
for tag in sorted(all_tags):
    output_buff.append("@content_tag{" + tag + "} ")
    for source in all_tags[tag]:
        description = str.format("{0}  ", source[2])
        # prepend source and link if requested
        link_str = get_link(source[0], source[1])
        if link_str:
            link_str += " - "
        output_buff.append(link_str + description)
    output_buff.append("\n")
output_buff.append("*/\n")

if args.get("dry_run"):
    source_count = 0
    for tag in all_tags:
        for value in all_tags[tag]:
            source_count += 1
    print("arguments:")
    for arg in args:
        print("\t", arg, ":", args[arg])
    print(f"\nFound {len(all_tags.keys())} tags with {source_count} total sources")
else:
    abs_dir = os.path.dirname(os.path.abspath(args.get("output")))
    if not os.path.exists(abs_dir):
        os.mkdir(abs_dir)
    with open(args.get("output"), "w") as output_file:
        output_file.writelines(output_buff)