pyarmor.cli 9.2.5__2-py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyarmor/cli/config.py ADDED
@@ -0,0 +1,306 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2023 - Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # Pyarmor #
10
+ # #
11
+ # Version: 8.0.1 - #
12
+ # #
13
+ #############################################################
14
+ #
15
+ #
16
+ # @File: cli/config.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: Thu Jan 12 10:27:05 CST 2023
21
+ #
22
+ import configparser
23
+ import fnmatch
24
+ import os
25
+
26
+ from . import logger, CliError
27
+
28
+
29
+ def indent(lines, n=2):
30
+ fmt = ' ' * 2 + '%s'
31
+ return [fmt % x for x in lines]
32
+
33
+
34
+ def str_opt(k, v, n=30):
35
+ v = '\n\t'.join(v.splitlines())
36
+ return ' %s = %s%s' % (k, v[:n], '...' if len(v) > n else '')
37
+
38
+
39
+ class Configer(object):
40
+
41
+ SECTIONS = 'pyarmor', 'logging', 'finder', 'builder', 'runtime', \
42
+ 'pack', 'bcc', 'mix.str', 'assert.call', 'assert.import'
43
+
44
+ def __init__(self, ctx, encoding=None):
45
+ self.ctx = ctx
46
+ self._encoding = encoding or ctx.encoding
47
+
48
+ def _read_config(self, filename):
49
+ cfg = configparser.ConfigParser(empty_lines_in_values=False)
50
+ cfg.read(filename, encoding=self._encoding)
51
+ return cfg
52
+
53
+ def list_sections(self, local=True, name=None):
54
+ lines = ['All available sections:']
55
+ cfg = self.ctx.cfg
56
+ lines.extend(indent(self.SECTIONS))
57
+
58
+ lines.extend(['', 'Global sections'])
59
+ cfg = self._read_config(self.ctx.global_config)
60
+ lines.extend(indent(cfg.sections()))
61
+
62
+ if local:
63
+ lines.extend(['', 'Local sections'])
64
+ cfg = self._read_config(self.ctx.local_config)
65
+ lines.extend(indent(cfg.sections()))
66
+
67
+ if name:
68
+ lines.extend(['', 'Private "%s" sections' % name])
69
+ cfg = self._read_config(self.ctx.get_filename(local, name))
70
+ lines.extend(indent(cfg.sections()))
71
+
72
+ return lines
73
+
74
+ def list_options(self, sect, local=True, name=None):
75
+ lines = ['Current options']
76
+
77
+ cfg = self.ctx.cfg
78
+ if cfg.has_section(sect):
79
+ lines.extend([str_opt(*x) for x in cfg.items(sect)])
80
+
81
+ lines.extend(['', 'Global options'])
82
+ cfg = self._read_config(self.ctx.global_config)
83
+ if cfg.has_section(sect):
84
+ lines.extend([str_opt(*x) for x in cfg.items(sect)])
85
+
86
+ if local:
87
+ lines.extend(['', 'Local options'])
88
+ cfg = self._read_config(self.ctx.local_config)
89
+ if cfg.has_section(sect):
90
+ lines.extend([str_opt(*x) for x in cfg.items(sect)])
91
+
92
+ if name:
93
+ lines.extend(['', 'Private "%s" options' % name])
94
+
95
+ cfg = self._read_config(self.ctx.get_filename(local, name))
96
+ if cfg.has_section(sect):
97
+ lines.extend([str_opt(*x) for x in cfg.items(sect)])
98
+
99
+ return lines
100
+
101
+ def _list_value(self, sect, opt, local=True, name=None):
102
+ clines, glines, lines, plines = self.infos
103
+
104
+ def format_value(opt):
105
+ v = cfg[sect].get(opt)
106
+ n = 1 << 30
107
+ return 'no option "%s"' % opt if v is None else str_opt(opt, v, n)
108
+
109
+ cfg = self.ctx.cfg
110
+ if cfg.has_section(sect):
111
+ clines.append(format_value(opt))
112
+
113
+ cfg = self._read_config(self.ctx.global_config)
114
+ if cfg.has_section(sect) and cfg.has_option(sect, opt):
115
+ glines.append(format_value(opt))
116
+
117
+ if local:
118
+ cfg = self._read_config(self.ctx.local_config)
119
+ if cfg.has_section(sect) and cfg.has_option(sect, opt):
120
+ lines.append(format_value(opt))
121
+
122
+ if name:
123
+ cfg = self._read_config(self.ctx.get_filename(local, name))
124
+ if cfg.has_section(sect) and cfg.has_option(sect, opt):
125
+ plines.append(format_value(opt))
126
+
127
+ return clines, glines, lines, plines
128
+
129
+ def _set_option(self, sect, opt, value, local=True, name=None):
130
+ ctx = self.ctx
131
+ filename = ctx.get_filename(local=local, name=name)
132
+
133
+ cfg = self._read_config(filename)
134
+ if not cfg.has_section(sect):
135
+ cfg.add_section(sect)
136
+
137
+ # TBD: input multiple lines
138
+ optname, optvalue = opt, value
139
+ if optvalue and optvalue[:1] in ('+', '-', '=', '^'):
140
+ op = optvalue[:1]
141
+ optvalue = optvalue.strip(op)
142
+ if op == '=':
143
+ op = None
144
+ else:
145
+ op = None
146
+ if optvalue and optvalue[:1] in ("'", '"'):
147
+ optvalue = optvalue.strip(optvalue[0])
148
+
149
+ if not optvalue:
150
+ if op is None:
151
+ self._remove(sect, [optname], local, name)
152
+ return
153
+
154
+ ctxcfg = ctx.cfg
155
+ old = ctxcfg[sect].get(optname, '') if ctxcfg.has_section(sect) else ''
156
+
157
+ if op == '+':
158
+ if (optvalue + ' ').find(old + ' ') == -1:
159
+ optvalue = ('%s %s' % (old, optvalue)).strip()
160
+ elif op == '-':
161
+ optvalue = (old + ' ').replace(optvalue + ' ', '').strip()
162
+ elif op == '^':
163
+ optvalue = '\n'.join((old.splitlines() + [optvalue]))
164
+
165
+ logger.info('change option "%s" to new value "%s"', optname, optvalue)
166
+ cfg.set(sect, optname, optvalue)
167
+
168
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
169
+ with open(filename, 'w', encoding=self._encoding) as f:
170
+ cfg.write(f)
171
+
172
+ self._list_value(sect, optname, local=local, name=name)
173
+
174
+ def _remove(self, section=None, options=None, local=True, name=None):
175
+ ctx = self.ctx
176
+ filename = ctx.get_filename(local=local, name=name)
177
+
178
+ if section is None:
179
+ logger.info('remove config file "%s"', filename)
180
+ if os.path.exists(filename):
181
+ os.remove(filename)
182
+ return
183
+
184
+ cfg = self._read_config(filename)
185
+
186
+ if cfg.has_section(section):
187
+ if not options:
188
+ logger.info('remove section "%s"', section)
189
+ cfg.remove_section(section)
190
+ else:
191
+ for opt in [x for x in options if cfg.has_option(section, x)]:
192
+ logger.info('remove option "%s:%s"', section, opt)
193
+ cfg.remove_option(section, opt)
194
+
195
+ if not cfg.options(section):
196
+ logger.info('remove empty section "%s"', section)
197
+ cfg.remove_section(section)
198
+
199
+ with open(filename, 'w', encoding=self._encoding) as f:
200
+ cfg.write(f)
201
+
202
+ def _clear(self, section=None, options=None, local=True, name=None):
203
+ ctx = self.ctx
204
+ scope = '%s%s config file' % (
205
+ 'local' if local else 'global',
206
+ ' "%s"' % name if name else ''
207
+ )
208
+ filename = ctx.get_filename(local=local, name=name)
209
+
210
+ if not filename:
211
+ logger.info('no %s', scope)
212
+ return
213
+
214
+ logger.info('remove %s "%s"', scope, filename)
215
+ if os.path.exists(filename):
216
+ os.remove(filename)
217
+
218
+ def _parse_opt(self, opt):
219
+ i = opt.find(':')
220
+ if i == -1:
221
+ j = opt.find('=')
222
+ pat, v = (opt, '') if j == -1 else (opt[:j], opt[j:])
223
+ cfg = self.ctx.cfg
224
+ rlist = []
225
+ for sect in self.SECTIONS:
226
+ if cfg.has_section(sect):
227
+ rlist.append((sect, [x+v for x in cfg.options(sect)
228
+ if fnmatch.fnmatch(x, pat)]))
229
+ return [x for x in rlist if x[1]]
230
+ else:
231
+ return [(opt[:i], [opt[i+1:]])]
232
+
233
+ def reset(self, options=None, local=True, name=None):
234
+ for pat in options:
235
+ for sect, opts in self._parse_opt(pat):
236
+ self._remove(sect, opts, local, name)
237
+
238
+ def run(self, options=None, local=True, name=None):
239
+ lines = []
240
+
241
+ if options and len(options) == 1 and options[0].find('=') == -1:
242
+ for sect, opts in self._parse_opt(options[0]):
243
+ title = 'Section: %s' % sect
244
+ lines.extend(['', '-' * 60, title])
245
+ self.infos = [], [], [], []
246
+
247
+ for opt in opts:
248
+ self._list_value(sect, opt, local, name)
249
+
250
+ lines.extend(['', 'Current settings'])
251
+ lines.extend(self.infos[0])
252
+ lines.extend(['', 'Global settings'])
253
+ lines.extend(self.infos[1])
254
+ lines.extend(['', 'Local settings'])
255
+ lines.extend(self.infos[2])
256
+ if name:
257
+ lines.extend(['', 'Private "%s" settings' % name])
258
+ lines.extend(self.infos[3])
259
+
260
+ elif options:
261
+ pairs = []
262
+ prev, op = None, ''
263
+ for opt in options:
264
+ i = opt.find('=')
265
+ if i > 0:
266
+ pairs.append([opt[:i], opt[i+1:]])
267
+ elif opt in ('+', '=', '-', '^'):
268
+ op = opt
269
+ elif prev:
270
+ pairs.append((prev, op + opt))
271
+ prev, op = None, ''
272
+ else:
273
+ prev = opt
274
+ if prev:
275
+ raise CliError('no value for option "%s"' % prev)
276
+
277
+ for pat, value in pairs:
278
+ sect_opts = self._parse_opt(pat)
279
+ if not sect_opts:
280
+ logger.debug('new builder option "%s"', pat)
281
+ sect_opts = [('builder', [pat])]
282
+ for sect, opts in sect_opts:
283
+ title = 'Section: %s' % sect
284
+ lines.extend(['', '-' * 60, title])
285
+ self.infos = [], [], [], []
286
+
287
+ for opt in opts:
288
+ self._set_option(sect, opt, value, local, name)
289
+
290
+ lines.extend(['', 'Current settings'])
291
+ lines.extend(self.infos[0])
292
+ lines.extend(['', 'Global settings'])
293
+ lines.extend(self.infos[1])
294
+ lines.extend(['', 'Local settings'])
295
+ lines.extend(self.infos[2])
296
+ if name:
297
+ lines.extend(['', 'Private "%s" settings' % name])
298
+ lines.extend(self.infos[3])
299
+
300
+ else:
301
+ for sect in self.SECTIONS:
302
+ title = 'Section: %s' % sect
303
+ lines.extend(['', '-' * 60, title, ''])
304
+ lines.extend(self.list_options(sect, local, name))
305
+
306
+ print('\n'.join(lines))