pyarmor.cli 9.2.6__3-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.
@@ -0,0 +1,265 @@
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/resource.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: 2022-12-06
21
+ #
22
+ import ast
23
+ import os
24
+
25
+ from datetime import datetime
26
+ from fnmatch import fnmatch
27
+ from string import Template
28
+
29
+
30
+ class Resource(object):
31
+
32
+ def __init__(self, path, name=None, parent=None):
33
+ self.parent = parent
34
+ self.path = path
35
+ self.name = name if name else self._format_name(path)
36
+
37
+ def __str__(self):
38
+ return self.fullname
39
+
40
+ def _format_name(self, path):
41
+ return os.path.splitext(os.path.basename(path))[0]
42
+
43
+ def is_top(self):
44
+ return self.parent is None
45
+
46
+ def is_script(self):
47
+ return isinstance(self, (FileResource, PycResource))
48
+
49
+ @property
50
+ def fullname(self):
51
+ return self.name if self.is_top() else \
52
+ '.'.join([self.parent.fullname, self.name])
53
+
54
+ @property
55
+ def fullpath(self):
56
+ return self.path if self.is_top() else \
57
+ os.path.join(self.parent.fullpath, self.path)
58
+
59
+ @property
60
+ def pkgname(self):
61
+ # if input path is '.', then pkgname will start with '..'
62
+ suffix = '.__init__'
63
+ if self.fullname.endswith(suffix):
64
+ return self.fullname[:-len(suffix)]
65
+ return self.fullname
66
+
67
+ @property
68
+ def output_path(self):
69
+ return '' if self.is_top() else \
70
+ os.path.join(self.parent.output_path, self.parent.name)
71
+
72
+
73
+ class FileResource(Resource):
74
+
75
+ def __init__(self, path, name=None, parent=None):
76
+ super().__init__(path, name=name, parent=parent)
77
+
78
+ self.mtree = None
79
+ self.mco = None
80
+
81
+ self.shebang = ''
82
+
83
+ # Do not touch these nodes in final protector
84
+ self.exclude_nodes = set()
85
+ # Do not touch these code objects in final patcher
86
+ self.exclude_co_objects = set()
87
+
88
+ def __str__(self):
89
+ return 'file %s%s' % (self.name, self.pyext)
90
+
91
+ def __iter__(self):
92
+ yield self
93
+
94
+ @property
95
+ def pyext(self):
96
+ return os.path.splitext(self.path)[-1]
97
+
98
+ @property
99
+ def output_filename(self):
100
+ return os.path.join(self.output_path, self.name + self.pyext)
101
+
102
+ @property
103
+ def frozenname(self):
104
+ n = self.fullname.find('.__init__')
105
+ return '<frozen %s>' % self.fullname[:None if n == -1 else n]
106
+
107
+ @property
108
+ def is_pyc(self):
109
+ return self.pyext.lower() == '.pyc'
110
+
111
+ def _get_encoding(self, encoding):
112
+ # Maybe: from tokenize import detect_encoding
113
+ from codecs import BOM_UTF8
114
+ from re import search as research
115
+
116
+ def find_encoding(line):
117
+ try:
118
+ pat = r'coding[=:]\s*([-\w.]+)'
119
+ n = line.find(b'\n')
120
+ if n > -1:
121
+ m = research(pat, line[:n].decode())
122
+ if m:
123
+ return m.group(1)
124
+ if line[n+1] == 35:
125
+ k = n + 1
126
+ n = line.find(b'\n', k)
127
+ m = research(pat, line[k:n].decode())
128
+ if m:
129
+ return m.group(1)
130
+ except Exception:
131
+ # Ignore decoding error or index error
132
+ pass
133
+
134
+ with open(self.fullpath, 'rb') as f:
135
+ line = f.read(80)
136
+ if line and line[:3] == BOM_UTF8:
137
+ return 'utf-8'
138
+ if line and line[0] == 35:
139
+ encoding = find_encoding(line) or encoding
140
+ return encoding
141
+
142
+ def readlines(self, encoding=None):
143
+ if not os.path.exists(self.fullpath):
144
+ raise RuntimeError('file "%s" doesn\'t exists' % self.fullpath)
145
+
146
+ with open(self.fullpath, encoding=self._get_encoding(encoding)) as f:
147
+ # file.read() can't read the whole data of big files
148
+ lines = f.readlines()
149
+ if lines and lines[0].startswith('#!'):
150
+ self.shebang = lines[0]
151
+ return lines
152
+
153
+ def reparse(self, lines=None, encoding=None):
154
+ if lines is None:
155
+ lines = self.readlines(encoding=encoding)
156
+ self.mtree = ast.parse(''.join(lines), self.frozenname, 'exec')
157
+
158
+ def _recompile_pyc(self):
159
+ from importlib._bootstrap_external import SourcelessFileLoader
160
+ path, name = self.fullpath, self.pkgname
161
+ self.mco = SourcelessFileLoader(name, path).get_code(name)
162
+
163
+ def recompile(self, mtree=None, optimize=1):
164
+ if self.is_pyc:
165
+ return self._recompile_pyc()
166
+
167
+ if mtree is None:
168
+ mtree = self.mtree
169
+ assert (mtree is not None)
170
+ self.mco = compile(mtree, self.frozenname, 'exec', optimize=optimize)
171
+
172
+ def clean(self):
173
+ self.lines = None
174
+ self.mtree = None
175
+ self.mco = None
176
+ if hasattr(self, 'jit_iv'):
177
+ self.jit_iv = None
178
+ if hasattr(self, 'jit_data'):
179
+ self.jit_data = None
180
+
181
+ def generate_output(self, tpl, code, relative=0, pkgname='pyarmor_runtime',
182
+ bootpath='__file__', rev=''):
183
+ if relative == 0:
184
+ prefix = ''
185
+ elif relative == 1:
186
+ prefix = '.' * self.fullname.count('.')
187
+ else:
188
+ assert (isinstance(relative, str))
189
+ prefix = relative + '.'
190
+ if self.fullname.startswith(prefix):
191
+ prefix = '.' * self.fullname.count('.')
192
+ elif prefix.startswith(self.pkgname + '.'):
193
+ prefix = prefix[len(self.pkgname):]
194
+
195
+ source = Template(tpl).safe_substitute(
196
+ timestamp=datetime.now().isoformat(),
197
+ package=prefix + pkgname,
198
+ path=bootpath,
199
+ code=repr(code),
200
+ rev=rev)
201
+ return (self.shebang + source) if self.shebang else source
202
+
203
+
204
+ class PycResource(FileResource):
205
+
206
+ def recompile(self, mtree=None, optimize=1):
207
+ from importlib._bootstrap_external import SourcelessFileLoader
208
+ path, name = self.fullpath, self.pkgname
209
+ self.mco = SourcelessFileLoader(name, path).get_code(name)
210
+
211
+
212
+ class PathResource(Resource):
213
+
214
+ def __init__(self, path, name=None, parent=None):
215
+ super().__init__(path, name=os.path.basename(path), parent=parent)
216
+ self.respaths = []
217
+ self.resfiles = []
218
+
219
+ def __str__(self):
220
+ return 'path %s' % self.fullname
221
+
222
+ def __iter__(self):
223
+ for res in self.resfiles:
224
+ if res:
225
+ yield res
226
+ for child in self.respaths:
227
+ for res in child:
228
+ yield res
229
+
230
+ def rebuild(self, **options):
231
+ pyexts = options.get('pyexts', ['.py'])
232
+ recursive = options.get('recursive', False)
233
+ includes = options.get('includes', '').split()
234
+ excludes = options.get('excludes', '').split()
235
+ patterns = options.get('data_files', '').split()
236
+
237
+ def in_filter(path, name):
238
+ fullpath = os.path.join(path, name)
239
+ ext = os.path.splitext(name)[1]
240
+ return not ex_filter(path, name) and (
241
+ (ext and ext in pyexts)
242
+ or any([fnmatch(fullpath, x) for x in includes]))
243
+
244
+ def ex_filter(path, name):
245
+ fullpath = os.path.join(path, name)
246
+ return excludes and any([fnmatch(fullpath, x) for x in excludes])
247
+
248
+ def is_res(path, name):
249
+ s = os.path.join(path, name)
250
+ return any([fnmatch(s, x) for x in patterns])
251
+
252
+ for path, dirnames, filenames in os.walk(self.fullpath):
253
+ self.resfiles = [x for x in [
254
+ FileResource(name, parent=self) if in_filter(path, name)
255
+ else Resource(name, parent=self) if is_res(path, name)
256
+ else None for name in filenames
257
+ ] if x]
258
+ self.respaths = [PathResource(name, parent=self)
259
+ for name in dirnames
260
+ if not ex_filter(path, name)]
261
+ break
262
+
263
+ if recursive:
264
+ for res in self.respaths:
265
+ res.rebuild(**options)