-
Notifications
You must be signed in to change notification settings - Fork 5
/
adlib.py
195 lines (155 loc) · 4.62 KB
/
adlib.py
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#
# ADLIB - The Arcane Document Library
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (C) Clear Code, Inc. 2019-2021
#
# HOW TO USE
# ----------
#
# >>> import adlib
# >>> adlib.load('esr60/Install')
#
# OUTPUT DATA SCHEMA
# -----------
#
# {
# 'id': 'Privacy-2',
# 'category': 'Privacy',
# 'index': '2',
# 'title': 'フォームの入力履歴の保存の可否',
# 'options': [
# {'option_id': 'Privacy-2-3',
# 'option_index': '3',
# 'option_title': '保存しない(ポリシーで設定)',
# 'dependency': '何々の場合',
# 'config': '"DisableFormHistory": true'},
# ...
# ]
# }
import re
import textwrap
from collections import OrderedDict
# Our configuration database is just a collection of text files.
# Each file contains a set of configuration items like this:
#
# Install-1: インストーラの表示名
#
# :1: 任意の名前
#
# !define PRODUCT_FULL_NAME "(名前)"
#
# ITEM_MATCHER matches configuration titles.
# OPTION_MATCHER matches option titles.
ITEM_MATCHER = re.compile('^(\S+): (.+)')
OPTION_MATCHER = re.compile('^ +:(\d+): (.*about:[^:]+|[^:]+)(: (.+))?')
def is_comment(line):
return line.startswith("#")
class Loader:
def __init__(self):
self.data = []
self.config = ''
def feed(self, line):
line = line.rstrip()
if is_comment(line):
return
matched = re.match(ITEM_MATCHER, line)
if matched:
return self.new_item(matched.group(1), matched.group(2))
matched = re.match(OPTION_MATCHER, line)
if matched:
return self.new_option(matched.group(1), matched.group(2), matched.group(4))
self.config += line[4:] + '\n'
def new_item(self, id, title):
self.flush()
category, index = id.rsplit('-', 1)
item = {
'id': id,
'category': category,
'index': index,
'title': title,
'options': [],
}
self.data.append(item)
def new_option(self, index, title, dependency):
self.flush()
if not dependency:
dependency = ''
last_item = self.data[-1]
id = '%s-%s' % (last_item['id'], index)
option = {
'option_id': id,
'option_index': index,
'option_title': title,
'dependency': dependency,
'config': '',
}
last_item['options'].append(option)
def flush(self):
if not self.data:
return
last_item = self.data[-1]
if not last_item['options']:
return
last_option = last_item['options'][-1]
last_option['config'] = self.config.strip()
self.config = ''
# This class is for verification manuals. It reads verify.txt
# and returns a simple dictionary.
class VariableLoader:
def __init__(self):
self.data = {}
self.key = None
def feed(self, line):
line = line.rstrip()
if is_comment(line):
return
if line.startswith(" "):
if self.key:
self.data[self.key] += line[4:] + '\n'
return
if ':' in line:
key, value = line.split(':', maxsplit=1)
self.key = key.strip()
self.data[self.key] = value.strip()
if self.key:
self.data[self.key] += '\n'
def flush(self):
for key in self.data:
self.data[key] = self.data[key].strip()
# And finally, we define a set of public functions with easy interfaces.
# For example, if you want to load verify.txt, do this:
#
# import adlib
# vars = adlib.load_variables("verify.txt")
def load(path):
loader = Loader()
with open(path) as fp:
for line in fp:
loader.feed(line)
loader.flush()
return loader.data
def load_as_dict(path):
try:
items = load(path)
except IOError:
return {}
configs = OrderedDict()
for item in items:
for option in item['options']:
configs[option['option_id']] = {
'config': option['config'],
'option_title': option['option_title'],
'dependency': option['dependency'],
}
return configs
def load_variables(path):
loader = VariableLoader()
with open(path) as fp:
for line in fp:
loader.feed(line)
loader.flush()
return loader.data