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/context.py ADDED
@@ -0,0 +1,790 @@
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/context.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: 2022-12-06
21
+ #
22
+ import configparser
23
+ import os
24
+ import sys
25
+
26
+ bootstrap_template = '''# Pyarmor $rev, $timestamp
27
+ from $package import __pyarmor__
28
+ __pyarmor__(__name__, $path, $code)
29
+ '''
30
+
31
+ runtime_package_template = '''# Pyarmor $rev, $timestamp
32
+ from .pyarmor_runtime import __pyarmor__
33
+ '''
34
+
35
+ multi_runtime_package_template = '''# Pyarmor $rev, $timestamp
36
+ def __pyarmor__():
37
+ import platform
38
+ import sys
39
+ from struct import calcsize
40
+
41
+ def format_system():
42
+ plat = platform.system().lower()
43
+ plat = ('cygwin' if plat.startswith('cygwin') else
44
+ 'linux' if plat.startswith('linux') else
45
+ 'freebsd' if plat.startswith(
46
+ ('freebsd', 'openbsd', 'isilon onefs')) else plat)
47
+ if plat == 'linux':
48
+ if hasattr(sys, 'getandroidapilevel'):
49
+ plat = 'android'
50
+ else:
51
+ cname, cver = platform.libc_ver()
52
+ if cname == 'musl':
53
+ plat = 'alpine'
54
+ elif cname == 'libc':
55
+ plat = 'android'
56
+ return plat
57
+
58
+ def format_machine():
59
+ mach = platform.machine().lower()
60
+ arch_table = (
61
+ ('x86', ('i386', 'i486', 'i586', 'i686', 'x86')),
62
+ ('x86_64', ('x64', 'x86_64', 'amd64', 'intel')),
63
+ ('arm', ('armv5',)),
64
+ ('armv6', ('armv6l',)),
65
+ ('armv7', ('armv7l',)),
66
+ ('aarch32', ('aarch32',)),
67
+ ('aarch64', ('aarch64', 'arm64')),
68
+ ('ppc64le', ('ppc64le',)),
69
+ ('mips32el', ('mipsel', 'mips32el')),
70
+ ('mips64el', ('mips64el',)),
71
+ ('riscv64', ('riscv64',)),
72
+ )
73
+ for alias, archlist in arch_table:
74
+ if mach in archlist:
75
+ mach = alias
76
+ break
77
+ return mach
78
+
79
+ plat, mach = format_system(), format_machine()
80
+ if plat == 'windows' and mach == 'x86_64':
81
+ bitness = calcsize('P'.encode()) * 8
82
+ if bitness == 32:
83
+ mach = 'x86'
84
+ # mach = 'universal' if plat == 'darwin' else mach
85
+ name = '.'.join(['_'.join([plat, mach]), 'pyarmor_runtime'])
86
+ return __import__(name, globals(), locals(), ['__pyarmor__'], level=1)
87
+ __pyarmor__ = __pyarmor__().__pyarmor__
88
+ '''
89
+
90
+ runtime_package_template3 = '''# Pyarmor $rev, $timestamp
91
+ from importlib.machinery import ExtensionFileLoader
92
+ from sysconfig import get_platform
93
+ __pyarmor__ = ExtensionFileLoader(
94
+ '.pyarmor_runtime', __file__.replace('__init__.py', 'pyarmor_runtime.so')
95
+ ).load_module().__pyarmor__
96
+ '''
97
+
98
+
99
+ def format_platform(plat=None, arch=None):
100
+ from struct import calcsize
101
+ from fnmatch import fnmatchcase
102
+
103
+ # Fix pyarmor.cli.core 6.5.2 issue, may be removed in 6.5.3
104
+ if plat is None and arch is None:
105
+ if os.getenv('PYARMOR_PLATFORM'):
106
+ plat, arch = os.getenv('PYARMOR_PLATFORM').split('.')
107
+ else:
108
+ from platform import system, machine
109
+ plat, arch = system().lower(), machine().lower()
110
+
111
+ plat_table = (
112
+ ('windows', ('windows',)),
113
+ ('cygwin', ('cygwin*',)),
114
+ ('darwin', ('darwin',)),
115
+ ('linux', ('linux*',)),
116
+ ('freebsd', ('freebsd*', 'openbsd*', 'isilon onefs')),
117
+ )
118
+
119
+ arch_table = (
120
+ ('x86', ('i?86', 'x86')),
121
+ ('x86_64', ('x64', 'x86_64', 'amd64', 'intel')),
122
+ ('arm', ('armv5',)),
123
+ ('armv6', ('armv6l',)),
124
+ ('armv7', ('armv7l',)),
125
+ ('aarch32', ('aarch32',)),
126
+ ('aarch64', ('aarch64', 'arm64')),
127
+ ('ppc64le', ('ppc64le',)),
128
+ ('mips32el', ('mipsel', 'mips32el')),
129
+ ('mips64el', ('mips64el',)),
130
+ ('riscv64', ('riscv64',)),
131
+ )
132
+
133
+ for alias, platlist in plat_table:
134
+ if any([fnmatchcase(plat, x) for x in platlist]):
135
+ plat = alias
136
+ break
137
+
138
+ for alias, archlist in arch_table:
139
+ if any([fnmatchcase(arch, x) for x in archlist]):
140
+ mach = alias
141
+ break
142
+ else:
143
+ raise RuntimeError('unsupported arch "%s"' % arch)
144
+
145
+ if plat == 'linux' and hasattr(sys, 'getandroidapilevel'):
146
+ plat = 'android'
147
+
148
+ if plat == 'windows' and mach == 'x86_64':
149
+ bitness = calcsize('P'.encode()) * 8
150
+ if bitness == 32:
151
+ mach = 'x86'
152
+
153
+ return '.'.join([plat, mach])
154
+
155
+
156
+ class Context(object):
157
+
158
+ def __init__(self, home, gpath='', lpath='', rpath='', encoding=None):
159
+ self.home_path = os.path.normpath(home)
160
+ self.global_path = os.path.join(home, gpath if gpath else 'config')
161
+ self.local_path = lpath if lpath else '.pyarmor'
162
+ self.reg_path = self.home_path if not rpath else \
163
+ rpath if os.path.isabs(rpath) else \
164
+ os.path.join(self.home_path, rpath)
165
+
166
+ # self.encoding is just for reading config file
167
+ self.encoding = encoding if encoding else 'utf-8'
168
+ cfglist = self.default_config, self.global_config, self.local_config
169
+ self.cfg = self._read_config(cfglist, encoding=self.encoding)
170
+
171
+ # self.runtime_package = 'pyarmor_runtime'
172
+ # self.runtime_suffix = '_000000'
173
+ # default inner key filename within runtime package
174
+ self.runtime_keyfile = '.pyarmor.ikey'
175
+
176
+ self.bootstrap_template = bootstrap_template
177
+
178
+ # Alias format for duplicated input names
179
+ self.alias_suffix = '{0}-{1}'
180
+
181
+ self.input_paths = []
182
+ self.outputs = []
183
+ self.resources = []
184
+ self.extra_resources = []
185
+
186
+ self.module_relations = {}
187
+ self.module_types = {}
188
+ self.variable_types = {}
189
+ self.module_builtins = set()
190
+
191
+ self.obfuscated_modules = set()
192
+ self.extra_libs = {}
193
+
194
+ self.rft_auto_excludes = set(['super'])
195
+ self.rft_export_names = set()
196
+ self.rft_transform_op = '?'
197
+
198
+ self.runtime_key = None
199
+
200
+ self.cmd_options = {}
201
+ self.plugins = []
202
+
203
+ def _read_config(self, filelist, encoding=None):
204
+ cfg = configparser.ConfigParser(
205
+ empty_lines_in_values=False,
206
+ interpolation=configparser.ExtendedInterpolation(),
207
+ )
208
+ encoding = encoding or self.encoding
209
+ cfg.read(filelist, encoding=encoding)
210
+ return cfg
211
+
212
+ def _named_config(self, name, encoding=None):
213
+ nlist = []
214
+ if self.cfg.getboolean('builder', 'propagate_package_options'):
215
+ i = name.find('.')
216
+ while i > -1:
217
+ nlist.append(name[:i])
218
+ i = name.find('.', i+1)
219
+ nlist.append(name)
220
+ flist = [os.path.join(b, a)
221
+ for a in nlist
222
+ for b in (self.global_path, self.local_path)]
223
+ return self._read_config(flist, encoding=encoding)
224
+
225
+ def read_token(self):
226
+ for filename in (self.license_group_token, self.license_token):
227
+ if os.path.exists(filename):
228
+ with open(filename, 'rb') as f:
229
+ return f.read()
230
+
231
+ def save_token(self, data):
232
+ with open(self.license_token, 'wb') as f:
233
+ if not data.endswith(b'=='):
234
+ raise RuntimeError('got invalid token %r' % data)
235
+ f.write(data)
236
+
237
+ def save_group_token(self, data):
238
+ with open(self.license_group_token, 'wb') as f:
239
+ if not data.endswith(b'=='):
240
+ raise RuntimeError('got invalid token %r' % data)
241
+ f.write(data)
242
+
243
+ def clear_token(self):
244
+ if os.path.exists(self.license_token):
245
+ with open(self.license_token, 'wb') as f:
246
+ f.close()
247
+
248
+ def group_device_file(self, devid):
249
+ filename = 'pyarmor-group-device.%s' % devid
250
+ return os.path.join(self.local_path, 'group', filename)
251
+
252
+ def read_license(self):
253
+ if os.path.exists(self.license_file):
254
+ with open(self.license_file, 'rb') as f:
255
+ return f.read()
256
+
257
+ def push(self, options):
258
+ finder = {}
259
+ for opt in ('recursive', 'findall', 'includes', 'excludes'):
260
+ if opt in options:
261
+ finder[opt] = options[opt]
262
+ if finder:
263
+ self.cmd_options['finder'] = finder
264
+ self.cmd_options.update(options)
265
+
266
+ def pop(self):
267
+ return self.cmd_options.clear()
268
+
269
+ def get_res_options(self, name, sect='finder'):
270
+ options = {}
271
+ if self.cfg.has_section(sect):
272
+ options.update(self.cfg.items(sect))
273
+ if sect == 'finder':
274
+ options.update(self.cmd_options.get('finder', {}))
275
+ elif sect == 'builder':
276
+ options.update(self.cmd_options)
277
+ extra_sect = ':'.join([name, sect])
278
+ if self.cfg.has_section(extra_sect):
279
+ options.update(self.cfg.items(extra_sect))
280
+ if name:
281
+ # If input path is '.', package name will start with '..'
282
+ cfg = self._named_config(name.strip('.') + '.ruler')
283
+ if cfg.has_section(sect):
284
+ options.update(cfg.items(sect))
285
+ return options
286
+
287
+ def get_path(self, local=True):
288
+ return self.local_path if local else self.global_path
289
+
290
+ def get_filename(self, local=True, name=None):
291
+ return os.path.join(self.get_path(local), name + '.ruler') if name \
292
+ else self.local_config if local else self.global_config
293
+
294
+ def version_info(self, verbose=3):
295
+ # 8.0.1
296
+ # 8.0.1 (trial)
297
+ # 8.0.1 (basic), 002000
298
+ # 8.0.1 (group), 002002, Product
299
+ # 8.0.1 (group), 002002, Product, Company
300
+ rev = '.'.join(self.version)
301
+ if not verbose:
302
+ return rev
303
+
304
+ licinfo = self.license_info
305
+ lictype = 'basic' if licinfo['features'] in (1, 17) else \
306
+ 'pro' if licinfo['features'] == 7 else \
307
+ 'group' if licinfo['features'] == 15 else \
308
+ 'ci' if licinfo['features'] == 23 else \
309
+ 'trial' if licinfo['token'] == 0 else 'unknown'
310
+ verinfo = ['%s (%s)' % (rev, lictype)]
311
+
312
+ if verbose > 1:
313
+ verinfo.append(licinfo['licno'][-6:])
314
+
315
+ if verbose > 2:
316
+ pname = licinfo['product']
317
+ verinfo.append(pname)
318
+
319
+ if verbose > 3:
320
+ regname = licinfo['regname']
321
+ if regname:
322
+ verinfo.append(regname)
323
+
324
+ return ', '.join(verinfo)
325
+
326
+ @property
327
+ def version(self):
328
+ return [self.cfg.get('pyarmor', x) for x in ('major', 'minor', 'patch')]
329
+
330
+ @property
331
+ def python_version(self):
332
+ return sys.version_info[:2]
333
+
334
+ @property
335
+ def py_gil_disabled(self):
336
+ from sysconfig import get_config_var
337
+ return 1 if get_config_var("Py_GIL_DISABLED") else 0
338
+
339
+ @property
340
+ def default_config(self):
341
+ return os.path.join(os.path.dirname(__file__), 'default.cfg')
342
+
343
+ @property
344
+ def global_config(self):
345
+ return os.path.join(self.global_path, 'global')
346
+
347
+ @property
348
+ def local_config(self):
349
+ return os.path.join(self.local_path, 'config')
350
+
351
+ def _make_public_capsule(self, filename):
352
+ from shutil import copy
353
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
354
+ path = os.path.dirname(__file__)
355
+ public_capsule = os.path.join(path, 'public_capsule.zip')
356
+ copy(public_capsule, filename)
357
+
358
+ @property
359
+ def private_capsule(self):
360
+ filename = os.path.join(self.reg_path, '.pyarmor_capsule.zip')
361
+ if not os.path.exists(filename):
362
+ self._make_public_capsule(filename)
363
+ return filename
364
+
365
+ @property
366
+ def license_file(self):
367
+ return os.path.join(self.reg_path, 'license.lic')
368
+
369
+ @property
370
+ def license_token(self):
371
+ return os.path.join(self.reg_path, '.license.token')
372
+
373
+ @property
374
+ def license_group_token(self):
375
+ return os.path.join(self.reg_path, '.license.group.token')
376
+
377
+ @property
378
+ def license_info(self):
379
+ from .register import parse_token
380
+ return parse_token(self.read_token())
381
+
382
+ @property
383
+ def native_platform(self):
384
+ from platform import system, machine
385
+ return '.'.join([system().lower(), machine().lower()])
386
+
387
+ @property
388
+ def pyarmor_platform(self):
389
+ platname = os.getenv('PYARMOR_PLATFORM', self.native_platform)
390
+ return format_platform(*platname.split('.'))
391
+
392
+ @property
393
+ def target_platforms(self):
394
+ platforms = self.cmd_options.get('platforms')
395
+ return platforms if platforms else [self.native_platform]
396
+
397
+ def _check_logpath(self, logfile):
398
+ path = os.path.dirname(logfile)
399
+ if path not in ('', '.') and not os.path.exists(path):
400
+ os.makedirs(path)
401
+ return logfile
402
+
403
+ @property
404
+ def debug_logfile(self):
405
+ return self._check_logpath(
406
+ self.cfg['logging'].get('debug_logfile', 'pyarmor.debug.log'))
407
+
408
+ @property
409
+ def trace_logfile(self):
410
+ return self._check_logpath(
411
+ self.cfg['logging'].get('trace_logfile', 'pyarmor.trace.log'))
412
+
413
+ def _optb(self, section, name):
414
+ return self.cfg.getboolean(section, name, vars=self.cmd_options)
415
+
416
+ def _opts(self, section, name):
417
+ return self.cfg.get(section, name, vars=self.cmd_options)
418
+
419
+ def _opti(self, section, name):
420
+ return self.cfg.getint(section, name, vars=self.cmd_options)
421
+
422
+ @property
423
+ def recursive(self):
424
+ return self._optb('finder', 'recursive')
425
+
426
+ @property
427
+ def findall(self):
428
+ return self._optb('finder', 'findall')
429
+
430
+ @property
431
+ def pyexts(self):
432
+ return self._opts('finder', 'pyexts').split()
433
+
434
+ @property
435
+ def enable_themida(self):
436
+ return self._optb('builder', 'enable_themida')
437
+
438
+ @property
439
+ def enable_jit(self):
440
+ return self._optb('builder', 'enable_jit')
441
+
442
+ @property
443
+ def enable_bcc(self):
444
+ return self._optb('builder', 'enable_bcc')
445
+
446
+ @property
447
+ def enable_rft(self):
448
+ return self._optb('builder', 'enable_rft')
449
+
450
+ @property
451
+ def assert_call(self):
452
+ return self._optb('builder', 'assert_call')
453
+
454
+ @property
455
+ def assert_import(self):
456
+ return self._optb('builder', 'assert_import')
457
+
458
+ @property
459
+ def mix_coname(self):
460
+ return self._optb('builder', 'mix_coname')
461
+
462
+ @property
463
+ def mix_localnames(self):
464
+ return self._optb('builder', 'mix_localnames')
465
+
466
+ @property
467
+ def mix_argnames(self):
468
+ return self._optb('builder', 'mix_argnames')
469
+
470
+ @property
471
+ def mix_str(self):
472
+ return self._optb('builder', 'mix_str')
473
+
474
+ @property
475
+ def obf_module(self):
476
+ return self._opti('builder', 'obf_module')
477
+
478
+ @property
479
+ def obf_code(self):
480
+ return self._opti('builder', 'obf_code')
481
+
482
+ @property
483
+ def wrap_mode(self):
484
+ return self._optb('builder', 'wrap_mode')
485
+
486
+ @property
487
+ def restrict_module(self):
488
+ return self._opti('builder', 'restrict_module')
489
+
490
+ @property
491
+ def import_check_license(self):
492
+ return self._optb('builder', 'import_check_license')
493
+
494
+ @property
495
+ def clear_module_co(self):
496
+ return self._optb('builder', 'clear_module_co')
497
+
498
+ @property
499
+ def clear_frame_locals(self):
500
+ return self._optb('builder', 'clear_frame_locals')
501
+
502
+ @property
503
+ def import_prefix(self):
504
+ v = self._opts('builder', 'import_prefix')
505
+ return int(v) if v.isdecimal() else v
506
+
507
+ @property
508
+ def exclude_restrict_modules(self):
509
+ return self._opts('builder', 'exclude_restrict_modules').split()
510
+
511
+ @property
512
+ def co_threshold(self):
513
+ return self._opti('builder', 'co_threshold')
514
+
515
+ @property
516
+ def jit_iv_threshold(self):
517
+ return self._opti('builder', 'jit_iv_threshold')
518
+
519
+ @property
520
+ def exclude_co_names(self):
521
+ return self.cfg['builder'].get('exclude_co_names', '').split()
522
+
523
+ @property
524
+ def outer_keyname(self):
525
+ return self.cfg['builder'].get('outer_keyname', 'pyarmor.rkey')
526
+
527
+ @property
528
+ def use_runtime(self):
529
+ return self.cmd_options.get('use_runtime',
530
+ self.cfg['builder'].get('use_runtime'))
531
+
532
+ @property
533
+ def inline_plugin_marker(self):
534
+ marker = self.cfg['builder'].get('inline_plugin_marker', 'false')
535
+ if marker.lower() not in ('', 'false', '0'):
536
+ return '# %s: ' % marker
537
+
538
+ #
539
+ # runtime configuration
540
+ #
541
+ def _rt_opt(self, opt):
542
+ return self.cmd_options.get(opt, self.cfg['runtime'].get(opt))
543
+
544
+ @property
545
+ def runtime_suffix(self):
546
+ return self.license_info['licno'][-6:]
547
+
548
+ @property
549
+ def runtime_package_name(self):
550
+ fmt = self.cfg.get('runtime', 'package_name_format')
551
+ return fmt.format(suffix=self.runtime_suffix)
552
+
553
+ @property
554
+ def runtime_platforms(self):
555
+ return self._rt_opt('platforms')
556
+
557
+ @property
558
+ def runtime_on_error(self):
559
+ return self._opti('runtime', 'on_error')
560
+
561
+ @property
562
+ def runtime_outer(self):
563
+ return self._optb('runtime', 'outer')
564
+
565
+ @property
566
+ def runtime_period(self):
567
+ period = self._rt_opt('period')
568
+ if period:
569
+ c = period[-1].lower()
570
+ if c.isdecimal():
571
+ return int(period) * 3600
572
+
573
+ if c in ('m', 'h', 's'):
574
+ unit = {
575
+ 's': 1,
576
+ 'm': 60,
577
+ 'h': 3600,
578
+ }
579
+ return int(period[:-1]) * unit[c]
580
+
581
+ return -1
582
+
583
+ @property
584
+ def runtime_expired(self):
585
+ return self._rt_opt('expired')
586
+
587
+ @property
588
+ def runtime_nts(self):
589
+ return self._opts('runtime', 'nts')
590
+
591
+ @property
592
+ def runtime_nts_timeout(self):
593
+ return self._opti('runtime', 'nts_timeout')
594
+
595
+ @property
596
+ def runtime_devices(self):
597
+ value = self._rt_opt('devices')
598
+ return value.splitlines() if isinstance(value, str) else value
599
+
600
+ @property
601
+ def runtime_interps(self):
602
+ interps = self._rt_opt('interps')
603
+ rules = interps.splitlines() if interps else []
604
+ cfg = self.cfg['builder']
605
+ if cfg.getboolean('check_debugger', False):
606
+ rules.append('check-debugger')
607
+ if cfg.getboolean('check_interp', False):
608
+ rules.append('check-interp')
609
+ if self.runtime_hook('pyarmor_runtime'):
610
+ rules.append('py:bootstrap')
611
+ return '\n'.join(rules)
612
+
613
+ @property
614
+ def runtime_timer(self):
615
+ return self._opti('runtime', 'timer')
616
+
617
+ @property
618
+ def runtime_simple_extension_name(self):
619
+ return self._optb('runtime', 'simple_extension_name')
620
+
621
+ @property
622
+ def runtime_user_data(self):
623
+ data = b''
624
+ filename = self.cmd_options.get('user_data')
625
+ if filename:
626
+ if filename[0] == '@':
627
+ with open(filename[1:], 'rb') as f:
628
+ data = f.read()
629
+ else:
630
+ data = filename.encode()
631
+
632
+ return data
633
+
634
+ @property
635
+ def runtime_messages(self):
636
+ value = self.cfg['runtime'].get('messages', '')
637
+ if value:
638
+ name, encoding = (value + ':utf-8').split(':')[:2]
639
+ cfg = self._named_config(name, encoding=encoding)
640
+ if cfg.has_section('runtime.message'):
641
+ return cfg
642
+
643
+ def runtime_package_template(self, platforms):
644
+ return runtime_package_template if len(platforms) < 2 else \
645
+ multi_runtime_package_template
646
+
647
+ @property
648
+ def runtime_obf_key_mode(self):
649
+ return self._opti('runtime', 'obf_key_mode')
650
+
651
+ @property
652
+ def runtime_patch_extension(self):
653
+ return self._opti('runtime', 'patch_extension')
654
+
655
+ #
656
+ # RFT settings
657
+ #
658
+
659
+ def rft_output_script(self, name):
660
+ return self._check_logpath(os.path.join(self.local_path, 'rft', name))
661
+
662
+ def rft_set_exclude_table(self, encoding=None):
663
+ filename = os.path.join(self.local_path, 'rft_exclude_table')
664
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
665
+ with open(filename, 'w', encoding=encoding) as f:
666
+ f.write(' '.join(self.rft_auto_excludes))
667
+
668
+ def rft_get_exclude_table(self, encoding=None):
669
+ filename = os.path.join(self.local_path, 'rft_exclude_table')
670
+ if os.path.exists(filename):
671
+ with open(filename, encoding=encoding) as f:
672
+ return f.read().split()
673
+ return []
674
+
675
+ #
676
+ # BCC settings
677
+ #
678
+
679
+ @property
680
+ def bcc_build_path(self):
681
+ path = os.path.join(self.local_path, 'bcc')
682
+ os.makedirs(path, exist_ok=True)
683
+ return path
684
+
685
+ @property
686
+ def bcc_encoding(self):
687
+ return self.cfg['builder'].get('encoding', 'utf-8')
688
+
689
+ #
690
+ # Plugin and hook
691
+ #
692
+ def runtime_hook(self, modname):
693
+ for path in self.local_path, self.global_path:
694
+ filename = os.path.join(path, 'hooks', modname + '.py')
695
+ if os.path.exists(filename):
696
+ encoding = self.cfg['builder'].get('encoding', 'utf-8')
697
+ with open(filename, encoding=encoding) as f:
698
+ return f.read()
699
+
700
+ def runtime_plugin(self, source, target, platforms):
701
+ from .plugin import Plugin
702
+ Plugin.post_runtime(self, source, target, platforms)
703
+
704
+ #
705
+ # Core data, new in 8.3
706
+ #
707
+ def _core_data(self, name):
708
+ n = __file__.find('context.py')
709
+ with open(__file__[:n] + name, 'rb') as f:
710
+ return f.read()
711
+
712
+ @property
713
+ def core_data_1(self):
714
+ return self._core_data('core.data.1')
715
+
716
+ @property
717
+ def core_data_2(self):
718
+ return self._core_data('core.data.2')
719
+
720
+ @property
721
+ def core_data_3(self):
722
+ return self._core_data('core.data.3')
723
+
724
+ #
725
+ # Get http proxy of token server
726
+ #
727
+ @property
728
+ def token_http_proxy(self):
729
+ http_proxy = os.environb.get(b'http_proxy', b'')
730
+ if not http_proxy:
731
+ return b''
732
+ i = http_proxy.find(b'@')
733
+ if i > 0:
734
+ from base64 import b64encode
735
+ header = b'Authorization: Basic %s\r\n' % b64encode(http_proxy[:i])
736
+ else:
737
+ header = b''
738
+ i += 1
739
+ j = http_proxy.find(b':', i)
740
+ if j == -1:
741
+ host = http_proxy[i:]
742
+ port = b'80'
743
+ else:
744
+ host = http_proxy[i:j]
745
+ port = http_proxy[j+1:]
746
+ url = b'http://pyarmor.dashingsoft.com'
747
+ return b'\x00'.join([host, port, url, header, b'\x00'])
748
+
749
+ def request_token(self, url, timeout=6.0):
750
+ from urllib.request import urlopen
751
+
752
+ def get_response(host):
753
+ try:
754
+ from ssl import _create_unverified_context
755
+ context = _create_unverified_context()
756
+ req = 'https://%s%s' % (host, url)
757
+ except Exception:
758
+ context = None
759
+ req = 'http://%s%s' % (host, url)
760
+ return urlopen(req, None, timeout, context=context)
761
+
762
+ with get_response('pyarmor.dashingsoft.com') as res:
763
+ return res.read()
764
+
765
+ #
766
+ # Pack options for auto/onefile/onefolder mode
767
+ #
768
+ @property
769
+ def pyi_options(self):
770
+ from json import loads as json_loads
771
+ from shlex import split as split_opt
772
+ pyi_opts = []
773
+ jsmarker = 'json::'
774
+ for line in self.cfg['pack'].get('pyi_options', '').splitlines():
775
+ line = line.strip()
776
+ if line.startswith(jsmarker):
777
+ pyi_opts.extend(json_loads(line[len(jsmarker):]))
778
+ elif line.startswith('-'):
779
+ pyi_opts.extend(split_opt(line))
780
+ elif line:
781
+ pyi_opts.append(line)
782
+ return pyi_opts
783
+
784
+ @property
785
+ def pack_basepath(self):
786
+ return os.path.join(self.local_path, 'pack')
787
+
788
+ @property
789
+ def pack_obfpath(self):
790
+ return os.path.join(self.pack_basepath, 'dist')