pyarmor.cli 9.2.6__4-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/model.py ADDED
@@ -0,0 +1,460 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2024 - Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # Pyarmor #
10
+ # #
11
+ # Version: 9.1.0 - #
12
+ # #
13
+ #############################################################
14
+ #
15
+ #
16
+ # @File: cli/model.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: Sun Dec 8 07:36:16 CST 2024
21
+ #
22
+ from textwrap import dedent
23
+
24
+
25
+ class Item:
26
+
27
+ def __init__(self, name, cls=None, require=False, many=False):
28
+ self.name = name
29
+ self.cls = cls
30
+ self.require = require
31
+ self.many = many
32
+
33
+
34
+ class Option:
35
+
36
+ def __init__(self, name, parent=None, model=None):
37
+ self.name = name
38
+ self.parent = parent
39
+ self.model = model
40
+ self._value = None
41
+
42
+ @property
43
+ def value(self):
44
+ if self._value is None:
45
+ self._value = self.parent.fetch(self.name)
46
+ if self._value is None:
47
+ return self.model.value
48
+ return self._value
49
+
50
+ @value.setter
51
+ def value(self, data: str):
52
+ if data:
53
+ self.check(data)
54
+ self._value = data
55
+ self.parent.store(self.name, data)
56
+ else:
57
+ self.reset()
58
+
59
+ def check(self, data: str):
60
+ pass
61
+
62
+ def hints(self):
63
+ return ''
64
+
65
+ def usage(self):
66
+ fmt = '%-20s: %s'
67
+ hints = [self.hints()]
68
+ hints.extend([(fmt % x) for x in self.model.hints()])
69
+ if getattr(self, 'CHOICES', None):
70
+ values = ', '.join(self.CHOICES)
71
+ hints.append(fmt % ('Available values', values))
72
+ hints.append('')
73
+ return '\n'.join(hints)
74
+
75
+ def push(self, data: str):
76
+ items = self.value.splitlines() if self.value else []
77
+ value = data.strip()
78
+ if value not in items:
79
+ items.append(value)
80
+ self.value = '\n'.join(items)
81
+
82
+ def pop(self, data: str):
83
+ items = self.value.splitlines() if self.value else []
84
+ try:
85
+ items.remove(data.strip())
86
+ except ValueError:
87
+ pass
88
+ else:
89
+ self.value = '\n'.join(items)
90
+
91
+ def reset(self):
92
+ """Clear option settings"""
93
+ self.parent.remove(self.name)
94
+ self._value = None
95
+
96
+
97
+ class OptionModel:
98
+
99
+ def __init__(self, name, cls=None, **kwargs):
100
+ self.name = name
101
+ self.cls = cls
102
+ self.kwargs = kwargs
103
+
104
+ def factory(self, parent):
105
+ Cls = globals().get(self.cls, Option)
106
+ return Cls(self.name, parent=parent, model=self)
107
+
108
+ @property
109
+ def many(self):
110
+ """This option may has many values"""
111
+ return self.kwargs.get('many', False)
112
+
113
+ @property
114
+ def value(self):
115
+ """The default value for this option"""
116
+ return self.kwargs.get('value')
117
+
118
+ def hints(self):
119
+ return [('Type', self.cls), ('Many', self.many)]
120
+
121
+
122
+ class PathOption(Option):
123
+ pass
124
+
125
+
126
+ class PkgPathOption(PathOption):
127
+
128
+ def hints(self):
129
+ return dedent("""\
130
+ It could be relative path or absolute path.
131
+
132
+ If package name is different from last path, use suffix format "path@pkgname"
133
+ """)
134
+
135
+
136
+ class FileOption(PathOption):
137
+ pass
138
+
139
+
140
+ class BoolOption(Option):
141
+
142
+ CHOICES = '0', '1'
143
+
144
+ def check(self, data: str):
145
+ if data not in self.CHOICES:
146
+ raise ValueError('invalid %s' % repr(data))
147
+
148
+
149
+ class EnumOption(Option):
150
+
151
+ def check(self, data: str):
152
+ if data not in self.CHOICES:
153
+ raise ValueError('invalid %s' % repr(data))
154
+
155
+
156
+ class TextOption(Option):
157
+ pass
158
+
159
+
160
+ class NameOption(TextOption):
161
+ pass
162
+
163
+
164
+ class PatternOption(TextOption):
165
+ """re or fnmatch pattern"""
166
+ pass
167
+
168
+
169
+ class ListOption(Option):
170
+ pass
171
+
172
+
173
+ class DictOption(Option):
174
+ pass
175
+
176
+
177
+ class RftAttrEnum(EnumOption):
178
+ """When don't know how to rename attribute
179
+
180
+ - ask: query user interactively
181
+ - log: log it but no rename
182
+ - yes: always rename
183
+ - no: do thing
184
+ - err: raise error quit
185
+ """
186
+
187
+ CHOICES = 'ask', 'log', 'yes', 'no', 'err'
188
+
189
+
190
+ class RftArgEnum(EnumOption):
191
+ """How to rename argument
192
+
193
+ - 0: no rename arguments
194
+ - 1: rename posonly arguments
195
+ - 2: rename kwonly arguments
196
+ - 3: rename all arguments
197
+ """
198
+
199
+ CHOICES = '0', '1', '2', '3'
200
+
201
+
202
+ class RftNamePattern(TextOption):
203
+ """Match ast.Name in ast.Tree
204
+
205
+ Each pattern includes 2 parts:
206
+
207
+ - ModulePattern: fnmatchcase pattern, match module
208
+
209
+ - NamePattern: fnmatchcase pattern, match function/class/name
210
+ """
211
+ pass
212
+
213
+
214
+ class RftAttrFilter(ListOption):
215
+ """Filter ast.Attribute transformed to setattr/getattr
216
+
217
+ Each ruler include 3 patterns
218
+
219
+ - ModulePattern: fnmatchcase pattern, match module
220
+
221
+ - ScopePattern: fnmatchcase pattern, match function/class
222
+
223
+ - AttributePattern: fnmatchcase pattern, match attribute
224
+
225
+ a: match any attribute "a"
226
+ b.a: match only "b.a"
227
+ a*: match any attribute startswith "a"
228
+ b.a*: match any "b" attribute which startswith "a"
229
+ b*.a: match any attribute "a" in parent startswith "b"
230
+ ().a: parent is function call
231
+ [].a: parent is subscript
232
+
233
+ *.a: same as "a"
234
+ """
235
+ pass
236
+
237
+
238
+ SECTIONS = {
239
+ 'pyarmor': [],
240
+ 'finder': [],
241
+ 'builder': [],
242
+ 'pack': [],
243
+
244
+ # Group: filter
245
+ 'assert.import': [],
246
+ 'assert.call': [],
247
+ 'mix.str': [],
248
+
249
+ # Group bcc
250
+ 'bcc': [],
251
+
252
+ # Scope project
253
+ 'project': [
254
+
255
+ OptionModel(
256
+ name='src',
257
+ cls='PathOption',
258
+ examples=[
259
+ '', '.', 'src', '../src',
260
+ '/home/jondy/project/src1',
261
+ 'my projects/project/src4'
262
+ 'C:\\test\\src2', 'C:\\工程\\src3',
263
+ ]
264
+ ),
265
+
266
+ OptionModel(
267
+ name='scripts',
268
+ cls='FileOption',
269
+ many=True,
270
+ restricts=[
271
+ 'ext should be .py, .pyw',
272
+ 'relative to src, or absolute path',
273
+ ],
274
+ examples=['foo.py', 'test/run.py']
275
+ ),
276
+
277
+ OptionModel(
278
+ name='modules',
279
+ cls='FileOption',
280
+ many=True,
281
+ ),
282
+
283
+ OptionModel(
284
+ name='packages',
285
+ cls='PkgPathOption',
286
+ many=True,
287
+ ),
288
+
289
+ # Only match name, no path separator
290
+ OptionModel(
291
+ name='excludes',
292
+ cls='PatternOption',
293
+ many=True,
294
+ examples=['__pycache__', 'test*']
295
+ ),
296
+
297
+ OptionModel(
298
+ name='recursive',
299
+ cls='BoolOption',
300
+ value=0,
301
+ ),
302
+ OptionModel(
303
+ name='pypaths',
304
+ cls='PatternOption',
305
+ many=True,
306
+ ),
307
+ ],
308
+
309
+ 'rft': [
310
+ OptionModel(
311
+ name='remove_assert',
312
+ cls='BoolOption',
313
+ value=0,
314
+ ),
315
+ OptionModel(
316
+ name='remove_docstr',
317
+ cls='BoolOption',
318
+ value=0,
319
+ ),
320
+ OptionModel(
321
+ name='builtin_mode',
322
+ cls='BoolOption',
323
+ value=0,
324
+ ),
325
+ OptionModel(
326
+ name='argument_mode',
327
+ cls='RftArgEnum',
328
+ value='3',
329
+ ),
330
+ OptionModel(
331
+ name='export_mode',
332
+ cls='BoolOption',
333
+ value=0,
334
+ ),
335
+ OptionModel(
336
+ name='extra_builtins',
337
+ cls='NameOption',
338
+ many=True,
339
+ ),
340
+ OptionModel(
341
+ name='exclude_names',
342
+ cls='RftNamePattern',
343
+ many=True,
344
+ ),
345
+ OptionModel(
346
+ name='exclude_funcs',
347
+ cls='RftNamePattern',
348
+ many=True,
349
+ ),
350
+ OptionModel(
351
+ name='external_types',
352
+ cls='RftNamePattern',
353
+ many=True,
354
+ ),
355
+ OptionModel(
356
+ name='external_attrs',
357
+ cls='RftNamePattern',
358
+ many=True,
359
+ ),
360
+ OptionModel(
361
+ name='attr_rules',
362
+ cls='RftAttrFilter',
363
+ ),
364
+ OptionModel(
365
+ name='call_rules',
366
+ cls='RftAttrFilter',
367
+ ),
368
+ OptionModel(
369
+ name='var_types',
370
+ cls='DictOption',
371
+ ),
372
+ ],
373
+ 'rft_filter': [
374
+ OptionModel(
375
+ name='obf_attribute',
376
+ cls='BoolOption',
377
+ ),
378
+ OptionModel(
379
+ name='obf_string',
380
+ cls='BoolOption',
381
+ ),
382
+ OptionModel(
383
+ name='obf_include_strings',
384
+ cls='PatternOption',
385
+ many=True,
386
+ ),
387
+ OptionModel(
388
+ name='obf_attr_filters',
389
+ cls='RftAttrFilter',
390
+ ),
391
+ OptionModel(
392
+ name='rft_import',
393
+ cls='BoolOption',
394
+ ),
395
+ OptionModel(
396
+ name='rft_ximport',
397
+ cls='BoolOption',
398
+ ),
399
+ OptionModel(
400
+ name='wildcard_import_table',
401
+ cls='DictOption',
402
+ ),
403
+ ],
404
+ 'mini': [
405
+ OptionModel(
406
+ name='import_from',
407
+ cls='NameOption',
408
+ value='pyarmor.mini.pyarmor_mini',
409
+ ),
410
+ ],
411
+ 'vmc': [],
412
+ 'ecc': [],
413
+ }
414
+
415
+
416
+ GROUPS = {
417
+ 'filter': ('assert.import', 'assert.call', 'mix.str'),
418
+ 'bcc': ('bcc',
419
+ 'linux.x86_64.bcc', 'linux.x86.bcc',
420
+ 'linux.aarch64.bcc', 'linux.armv7.bcc',
421
+ 'darwin.x86_64.bcc', 'darwin.aarch64.bcc',
422
+ 'windows.x86_64.bcc', 'windows.x86.bcc',
423
+ 'android.x86_64.bcc', 'android.x86.bcc',
424
+ 'android.aarch64.bcc', 'android.armv7.bcc',
425
+ 'alpine.x86_64.bcc', 'alpine.aarch64.bcc',
426
+ 'freebsd.x86_64.bcc'),
427
+ }
428
+
429
+ BUSINESS_MODELS = {
430
+
431
+ 'Project': [
432
+ Item('scripts', cls='Python.Script', many=True),
433
+ Item('modules', cls='Python.Module', many=True),
434
+ Item('packages', cls='Python.Package', many=True),
435
+ ],
436
+
437
+ 'StdScript': [
438
+ Item('script', cls='Python.Script', require=True),
439
+ Item('pyarmor_runtime', cls='Python.Extension', require=True),
440
+ ],
441
+
442
+ 'MiniScript': [
443
+ Item('script', cls='Python.Script', require=True),
444
+ Item('pyarmor_mini', cls='Python.Extension', require=True),
445
+ ],
446
+
447
+ 'EccScript': [
448
+ Item('script', cls='Python.Script', require=True),
449
+ Item('pyarmor_ecc', cls='Python.Extension', require=True),
450
+ ],
451
+
452
+ 'VmcScript': [
453
+ Item('script', cls='Python.Script', require=True),
454
+ Item('pyarmor_ecc', cls='Python.Extension', require=True),
455
+ ],
456
+
457
+ 'RftScript': [
458
+ Item('script', cls='Python.Script', require=True),
459
+ ]
460
+ }