pyarmor.cli 9.2.0__tar.gz → 9.2.2__tar.gz

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.
Files changed (38) hide show
  1. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/PKG-INFO +1 -1
  2. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/__init__.py +1 -1
  3. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/__main__.py +4 -1
  4. pyarmor_cli-9.2.2/pyarmor/cli/batch.py +62 -0
  5. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/command.py +58 -12
  6. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/config.py +3 -3
  7. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/context.py +8 -2
  8. pyarmor_cli-9.2.2/pyarmor/cli/core.data.1 +0 -0
  9. pyarmor_cli-9.2.2/pyarmor/cli/core.data.2 +0 -0
  10. pyarmor_cli-9.2.2/pyarmor/cli/core.data.3 +0 -0
  11. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/default.cfg +5 -5
  12. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/model.py +9 -0
  13. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/plugin.py +5 -5
  14. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/project.py +94 -21
  15. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/register.py +5 -3
  16. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/resource.py +21 -9
  17. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/shell.py +5 -2
  18. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor.cli.egg-info/PKG-INFO +1 -1
  19. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor.cli.egg-info/SOURCES.txt +1 -0
  20. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/setup.py +1 -1
  21. pyarmor_cli-9.2.0/pyarmor/cli/core.data.1 +0 -0
  22. pyarmor_cli-9.2.0/pyarmor/cli/core.data.2 +0 -0
  23. pyarmor_cli-9.2.0/pyarmor/cli/core.data.3 +0 -0
  24. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/README.rst +0 -0
  25. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/bootstrap.py +0 -0
  26. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/bug.py +0 -0
  27. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/docker.py +0 -0
  28. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/generate.py +0 -0
  29. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/hdinfo.py +0 -0
  30. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/merge.py +0 -0
  31. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/mixer.py +0 -0
  32. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/public_capsule.zip +0 -0
  33. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor/cli/repack.py +0 -0
  34. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor.cli.egg-info/dependency_links.txt +0 -0
  35. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor.cli.egg-info/entry_points.txt +0 -0
  36. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor.cli.egg-info/requires.txt +0 -0
  37. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/pyarmor.cli.egg-info/top_level.txt +0 -0
  38. {pyarmor_cli-9.2.0 → pyarmor_cli-9.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyarmor.cli
3
- Version: 9.2.0
3
+ Version: 9.2.2
4
4
  Summary: A comand line tool to obfuscate python scripts
5
5
  Home-page: https://github.com/dashingsoft/pyarmor
6
6
  Author: Jondy Zhao
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- __VERSION__ = '9.2.0'
3
+ __VERSION__ = '9.2.2'
4
4
 
5
5
  logger = logging.getLogger('cli')
6
6
 
@@ -325,7 +325,7 @@ def cmd_reg(ctx, args):
325
325
 
326
326
  else:
327
327
  regsvr = WebRegister(ctx)
328
- regsvr.check_request_interval()
328
+ regsvr.check_request_interval(activation=regfile)
329
329
  info, msg = regsvr.prepare(regfile, args.product, upgrade=upgrade)
330
330
  prompt = 'Are you sure to continue? (yes/no) '
331
331
  if args.confirm:
@@ -767,6 +767,9 @@ def main_entry(argv):
767
767
  if not (x == 3 and y > 6 and y < 16):
768
768
  raise CliError('Python %s.%s is not supported' % (x, y))
769
769
 
770
+ if ctx.py_gil_disabled:
771
+ raise CliError('Pyarmor does not support free-threading Python')
772
+
770
773
  if args.version:
771
774
  print_version(ctx)
772
775
  parser.exit()
@@ -0,0 +1,62 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2025 - Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # Pyarmor #
10
+ # #
11
+ # Version: 9.2.1 - #
12
+ # #
13
+ #############################################################
14
+ #
15
+ #
16
+ # @File: cli/shell.py
17
+ #
18
+ # @Author: Jondy Zhao (pyarmor@163.com)
19
+ #
20
+ # @Create Date: Wed Nov 5 20:22:52 CST 2025
21
+ #
22
+
23
+ """This script could run batch pyarmor commands.
24
+
25
+ For example:
26
+
27
+ python3 -m pyarmor.cli.batch build-script.sh
28
+
29
+ cd abc
30
+ pyarmor
31
+
32
+ The main purpose is to fix pyarmor CI License send too many request issues.
33
+
34
+ """
35
+ import shlex
36
+ import os
37
+
38
+ from pyarmor.cli.__main__ import main_entry as pyarmor_run
39
+
40
+ # Do not run `pyarmor reg pyarmor-ci-xxxx.zip` here, run it before this script
41
+
42
+ build_commands = """
43
+ pyarmor gen main.py
44
+ cd ../package1
45
+ pyarmor gen -R --enable-bcc src/
46
+ cd ../package2
47
+ pyarmor gen -R --enable-jit --private src/
48
+ """
49
+
50
+ for cmd in build_commands.splitlines():
51
+ if cmd.startswith('#'):
52
+ continue
53
+
54
+ if cmd.startswith('cd '):
55
+ path = cmd[3:].strip()
56
+ print('Change current path:', path)
57
+ os.chdir(path)
58
+
59
+ elif cmd.startswith('pyarmor '):
60
+ print('Execute: ', cmd)
61
+ args = shlex.split(cmd[8:].strip())
62
+ pyarmor_run(args)
@@ -135,16 +135,6 @@ class Commander:
135
135
  # action="store_const", const='std',
136
136
  # help='genetate standard obfuscated scripts'
137
137
  # )
138
- # group.add_argument(
139
- # '--ecc', dest='target', default='std',
140
- # action="store_const", const='ecc',
141
- # help='genetate scripts with embedded C code'
142
- # )
143
- # group.add_argument(
144
- # '--vmc', dest='target', default='std',
145
- # action="store_const", const='vmc',
146
- # help='genetate scripts with vm C code'
147
- # )
148
138
  group.add_argument(
149
139
  '--rft', dest='target', default='std',
150
140
  action="store_const", const='rft',
@@ -158,7 +148,38 @@ class Commander:
158
148
  group.add_argument(
159
149
  '--mini-rft', dest='target', default='std',
160
150
  action="store_const", const='mini-rft',
161
- help='genetate high performance refactor scripts'
151
+ help='generate scripts by combining --rft and --mini'
152
+ )
153
+ group.add_argument(
154
+ '--vmc', dest='target', default='std',
155
+ action="store_const", const='vmc',
156
+ help='genetate scripts with vmc code'
157
+ )
158
+ group.add_argument(
159
+ '--vmc-rft', dest='target', default='std',
160
+ action="store_const", const='vmc-rft',
161
+ help='generate scripts by combining --rft and --vmc'
162
+ )
163
+ group.add_argument(
164
+ '--ecc', dest='target', default='std',
165
+ action="store_const", const='ecc',
166
+ help='genetate scripts with embedded C code'
167
+ )
168
+ group.add_argument(
169
+ '--ecc-rft', dest='target', default='std',
170
+ action="store_const", const='ecc-rft',
171
+ help='generate scripts by combining --rft and --ecc'
172
+ )
173
+ group.add_argument(
174
+ '--ecc-nogil', dest='target', default='std',
175
+ action="store_const", const='ecc-nogil',
176
+ help='genetate ECC scripts for free-threading Python'
177
+ )
178
+ group.add_argument(
179
+ '--ecc-rft-nogil', dest='target', default='std',
180
+ action="store_const", const='ecc-rft-nogil',
181
+ help=('generate scripts by combining --rft and --ecc'
182
+ ' for free-threading Python')
162
183
  )
163
184
  group.add_argument(
164
185
  '--list', dest='target', default='std',
@@ -338,7 +359,7 @@ class Commander:
338
359
  logger.info('%-20s: %s', key, value)
339
360
 
340
361
  makedirs(ctx.local_path, exist_ok=True)
341
- with open(ctx.local_config, 'w') as f:
362
+ with open(ctx.local_config, 'w', encoding=ctx.encoding) as f:
342
363
  cfg.write(f)
343
364
  logger.info('project saved')
344
365
 
@@ -366,6 +387,10 @@ class Commander:
366
387
  )
367
388
  cfg.read([ctx.local_config], encoding=ctx.encoding)
368
389
 
390
+ x, y = ctx.python_version
391
+ if not (x == 3 and y > 8 and y < 16):
392
+ raise CliError('`pyarmor build` only works for Python 3.9+')
393
+
369
394
  sectname = 'project'
370
395
  if not cfg.has_section(sectname):
371
396
  raise CliError('no project information')
@@ -412,6 +437,8 @@ class Commander:
412
437
  logger.info('build target %s ...', args.target)
413
438
  output = args.output if args.output else 'dist'
414
439
  self._build(project, args.target, output)
440
+ if args.target.startswith('std'):
441
+ self._cmd_gen(args)
415
442
  logger.info('build target %s end', args.target)
416
443
 
417
444
  def _build(self, project, target, output, value=None):
@@ -420,6 +447,25 @@ class Commander:
420
447
  m = Pytransform3.init(self.ctx)
421
448
  m.pre_build(args)
422
449
 
450
+ def _cmd_gen(self, args):
451
+ """If build target is std, then call `pyarmor gen`
452
+
453
+ It will obfuscate the scripts with `pyarmor_runtime.so`
454
+
455
+ Most of obfuscated options are got from config file, not from
456
+ command line
457
+
458
+ Option `--pack` may be still got from command line
459
+ """
460
+ from .generate import Builder
461
+ from .plugin import Plugin
462
+
463
+ ctx = self.ctx
464
+ builder = Builder(ctx)
465
+ Plugin.install(ctx)
466
+ builder.process(args)
467
+ Plugin.post_build(ctx)
468
+
423
469
  def cmd_env(self, ctx, args):
424
470
  """Check and change pyarmor settings
425
471
 
@@ -43,7 +43,7 @@ class Configer(object):
43
43
 
44
44
  def __init__(self, ctx, encoding=None):
45
45
  self.ctx = ctx
46
- self._encoding = encoding
46
+ self._encoding = encoding or ctx.encoding
47
47
 
48
48
  def _read_config(self, filename):
49
49
  cfg = configparser.ConfigParser(empty_lines_in_values=False)
@@ -166,7 +166,7 @@ class Configer(object):
166
166
  cfg.set(sect, optname, optvalue)
167
167
 
168
168
  os.makedirs(os.path.dirname(filename), exist_ok=True)
169
- with open(filename, 'w') as f:
169
+ with open(filename, 'w', encoding=self._encoding) as f:
170
170
  cfg.write(f)
171
171
 
172
172
  self._list_value(sect, optname, local=local, name=name)
@@ -196,7 +196,7 @@ class Configer(object):
196
196
  logger.info('remove empty section "%s"', section)
197
197
  cfg.remove_section(section)
198
198
 
199
- with open(filename, 'w') as f:
199
+ with open(filename, 'w', encoding=self._encoding) as f:
200
200
  cfg.write(f)
201
201
 
202
202
  def _clear(self, section=None, options=None, local=True, name=None):
@@ -164,9 +164,9 @@ class Context(object):
164
164
  os.path.join(self.home_path, rpath)
165
165
 
166
166
  # self.encoding is just for reading config file
167
- self.encoding = encoding
167
+ self.encoding = encoding if encoding else 'utf-8'
168
168
  cfglist = self.default_config, self.global_config, self.local_config
169
- self.cfg = self._read_config(cfglist, encoding=encoding)
169
+ self.cfg = self._read_config(cfglist, encoding=self.encoding)
170
170
 
171
171
  # self.runtime_package = 'pyarmor_runtime'
172
172
  # self.runtime_suffix = '_000000'
@@ -205,6 +205,7 @@ class Context(object):
205
205
  empty_lines_in_values=False,
206
206
  interpolation=configparser.ExtendedInterpolation(),
207
207
  )
208
+ encoding = encoding or self.encoding
208
209
  cfg.read(filelist, encoding=encoding)
209
210
  return cfg
210
211
 
@@ -330,6 +331,11 @@ class Context(object):
330
331
  def python_version(self):
331
332
  return sys.version_info[:2]
332
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
+
333
339
  @property
334
340
  def default_config(self):
335
341
  return os.path.join(os.path.dirname(__file__), 'default.cfg')
@@ -3,14 +3,14 @@
3
3
  ;; Pyarmor version
4
4
  major = 9
5
5
  minor = 2
6
- patch = 0
6
+ patch = 2
7
7
 
8
- ;; Compatible core version
8
+ ;; Compatible core version for package: pyarmor.cli.core
9
9
  cli.core = 8.1.0
10
- mini.core = 2.0
11
10
 
12
- ;; ecc.core = 1.0
13
- ;; vmc.core = 1.0
11
+ ;; Compatible core version for package: pyarmor.mini
12
+ ;; Which is required to run MINI, VMC, or ECC mode scripts
13
+ mini.core = 3.0
14
14
 
15
15
  ;; Deprecated since Pyarmor 8.2.5
16
16
  ; cli.runtime = 3.2.5
@@ -401,6 +401,15 @@ SECTIONS = {
401
401
  cls='DictOption',
402
402
  ),
403
403
  ],
404
+ 'mini': [
405
+ OptionModel(
406
+ name='import_from',
407
+ cls='NameOption',
408
+ value='pyarmor.mini.pyarmor_mini',
409
+ ),
410
+ ],
411
+ 'vmc': [],
412
+ 'ecc': [],
404
413
  }
405
414
 
406
415
 
@@ -186,7 +186,7 @@ class MultiPythonPlugin:
186
186
  os.makedirs(verpath)
187
187
 
188
188
  pkgscript = os.path.join(pkgpath, '__init__.py')
189
- with open(pkgscript) as f:
189
+ with open(pkgscript, encoding=ctx.encoding) as f:
190
190
  lines = f.readlines()
191
191
  start = 1 if lines[0].startswith('#') else 0
192
192
 
@@ -196,13 +196,13 @@ class MultiPythonPlugin:
196
196
  '{0} = __import__("py%d%d.pyarmor_runtime" % py_version[:2],'
197
197
  ' globals(), locals(), ["{0}"], 1).{0}'.format('__pyarmor__')
198
198
  ])
199
- with open(pkgscript, 'w') as f:
199
+ with open(pkgscript, 'w', encoding=ctx.encoding) as f:
200
200
  f.write(''.join(lines))
201
201
  for x in MultiPythonPlugin.RUNTIME_FILES:
202
202
  move(x, verpath)
203
203
  else:
204
204
  lines[start:start] = 'from sys import version_info as py_version\n'
205
- with open(pkgscript, 'w') as f:
205
+ with open(pkgscript, 'w', encoding=ctx.encoding) as f:
206
206
  f.write(''.join(lines).replace(
207
207
  "join(['_'", "join(['py%d%d' % py_version[:2], '_'"))
208
208
  for x in MultiPythonPlugin.RUNTIME_FILES:
@@ -253,7 +253,7 @@ class DarwinUniversalPlugin:
253
253
  return
254
254
 
255
255
  def rebuild_init(oneplat, init_script):
256
- with open(init_script, 'r') as f:
256
+ with open(init_script, 'r', encoding=ctx.encoding) as f:
257
257
  lines = f.readlines()
258
258
  if oneplat:
259
259
  lines[1:] = ['from .pyarmor_runtime import __pyarmor__']
@@ -262,7 +262,7 @@ class DarwinUniversalPlugin:
262
262
  if lines[i].strip().startswith("# mach = 'universal'"):
263
263
  lines[i] = lines[i].replace('# ', '')
264
264
  break
265
- with open(init_script, 'w') as f:
265
+ with open(init_script, 'w', encoding=ctx.encoding) as f:
266
266
  f.write(''.join(lines))
267
267
 
268
268
  rtpath = find_runtime_package(ctx, outputs[0])
@@ -23,7 +23,7 @@
23
23
  #
24
24
  # - Define project object for Pyarmor 9.
25
25
  # - Define project commands: init, build
26
- # - Define targets: std, fly, vmc, ecc, rft
26
+ # - Define targets: std, mini, vmc, ecc, rft
27
27
 
28
28
  """Manage projects
29
29
 
@@ -151,6 +151,8 @@ class Module:
151
151
  self._tree = None
152
152
  self._type = None
153
153
 
154
+ self._shebang = ''
155
+
154
156
  @property
155
157
  def name(self):
156
158
  return '' if self._name == '__init__' else self._name
@@ -190,14 +192,21 @@ class Module:
190
192
  s = self.qualname + ('' if self.name else '.__init__')
191
193
  return joinpath(*s.split('.')) + splitext(self.path)[-1]
192
194
 
193
- def compile_file(self, force=False):
195
+ @property
196
+ def shebang(self):
197
+ return self._shebang
198
+
199
+ def compile_file(self, force=False, optimize=-1):
194
200
  if self._co is not None and not force:
195
201
  return
196
202
 
197
203
  self.parse_file(force=force)
198
204
 
205
+ options = {
206
+ 'optimize': optimize
207
+ }
199
208
  logger.info('compile %s ...', self.qualname)
200
- self._co = compile(self._tree, self.abspath, 'exec')
209
+ self._co = compile(self._tree, self.abspath, 'exec', **options)
201
210
  logger.info('compile %s end', self.qualname)
202
211
 
203
212
  def parse_file(self, force=False):
@@ -206,7 +215,9 @@ class Module:
206
215
 
207
216
  filename = self.abspath
208
217
  with open(filename, 'rb') as f:
209
- encoding, _ = tokenize.detect_encoding(f.readline)
218
+ encoding, lines = tokenize.detect_encoding(f.readline)
219
+ if lines and lines[0].startswith(b'#!'):
220
+ self._shebang = lines[0].decode(encoding)
210
221
 
211
222
  with open(filename, 'r', encoding=encoding) as f:
212
223
  logger.info('parse %s ...', self.qualname)
@@ -385,6 +396,11 @@ class Project:
385
396
  self._rft_include_attrs = None
386
397
  self._used_external_types = None
387
398
 
399
+ self._std_options = None
400
+ self._mini_options = None
401
+ self._vmc_options = None
402
+ self._ecc_options = None
403
+
388
404
  # Log variable name in chain attributes
389
405
  #
390
406
  # For example, in module "foo.py":
@@ -496,6 +512,20 @@ class Project:
496
512
  for x in self._namespaces:
497
513
  yield x
498
514
 
515
+ @property
516
+ def std_options(self):
517
+ """Options got from ctx.builder, not in project"""
518
+ if self._std_options is None:
519
+ cfg = self.ctx.cfg
520
+ optimize = cfg['builder'].getint('optimize', -1)
521
+ self._std_options = {
522
+ 'optimize': optimize
523
+ }
524
+ return self._std_options
525
+
526
+ def std_opt(self, name):
527
+ return self.std_options.get(name)
528
+
499
529
  @property
500
530
  def rft_options(self):
501
531
  """Refactor options:
@@ -764,6 +794,62 @@ class Project:
764
794
  self._rft_type_rules = vartypes
765
795
  return self._rft_type_rules
766
796
 
797
+ @property
798
+ def mini_options(self):
799
+ """MINI mode options:
800
+
801
+ - import_from: str
802
+
803
+ Default value is "pyarmor.mini.pyarmor_mini", it means from
804
+ package `pyarmor.mini` import extension `pyarmor_mini`
805
+
806
+ Set it to "pyarmor_mini" if it's distributed with obfuscated
807
+ scripts
808
+ """
809
+ if self._mini_options is None:
810
+ cfg = self.ctx.cfg
811
+ sect = 'mini'
812
+ if cfg.has_section(sect):
813
+ self._mini_options = dict(cfg.items(sect))
814
+ else:
815
+ self._mini_options = {}
816
+ self._mini_options.setdefault(
817
+ 'import_from', 'pyarmor.mini.pyarmor_mini')
818
+ return self._mini_options
819
+
820
+ def mini_opt(self, name):
821
+ return self.mini_options.get(name)
822
+
823
+ @property
824
+ def vmc_options(self):
825
+ """VMC mode options"""
826
+ if self._vmc_options is None:
827
+ cfg = self.ctx.cfg
828
+ sect = 'vmc'
829
+ if cfg.has_section(sect):
830
+ self._vmc_options = dict(cfg.items(sect))
831
+ else:
832
+ self._vmc_options = {}
833
+ return self._vmc_options
834
+
835
+ def vmc_opt(self, name):
836
+ return self.vmc_options.get(name)
837
+
838
+ @property
839
+ def ecc_options(self):
840
+ """ECC mode options"""
841
+ if self._ecc_options is None:
842
+ cfg = self.ctx.cfg
843
+ sect = 'ecc'
844
+ if cfg.has_section(sect):
845
+ self._ecc_options = dict(cfg.items(sect))
846
+ else:
847
+ self._ecc_options = {}
848
+ return self._ecc_options
849
+
850
+ def ecc_opt(self, name):
851
+ return self.ecc_options.get(name)
852
+
767
853
  @property
768
854
  def builtins(self):
769
855
  if self._builtins is None:
@@ -780,7 +866,7 @@ class Project:
780
866
  for name in names.split():
781
867
  if name.endswith('.json'):
782
868
  if exists(name):
783
- with open(name) as f:
869
+ with open(name, encoding='utf-8') as f:
784
870
  used_types.update(jsonload(f))
785
871
  else:
786
872
  modname = name.split('::')[0]
@@ -819,19 +905,6 @@ class Project:
819
905
  self._rft_include_attrs.update(value.split())
820
906
  return self._rft_include_attrs
821
907
 
822
- def std_options(self):
823
- """Obfuscation options only for std target
824
-
825
- - std_assert_import
826
- - std_assert_call
827
- - std_restrict_module
828
- - std_expired_date
829
- - std_bind_devices
830
-
831
- Got from command line, not in config file
832
- """
833
- pass
834
-
835
908
  def get_module(self, qualname):
836
909
  """Get module in the project by unique qualname
837
910
  It equals one dict: map_qualname_to_module
@@ -1013,7 +1086,7 @@ class Project:
1013
1086
  if not exists(rulefile):
1014
1087
  logger.info('no found %s', rulefile)
1015
1088
  return
1016
- with open(rulefile) as f:
1089
+ with open(rulefile, encoding='utf-8') as f:
1017
1090
  fixtable = jsonload(f)
1018
1091
 
1019
1092
  if mode == 2:
@@ -1071,7 +1144,7 @@ class Project:
1071
1144
  inattrs = fixtable.get('include_attrs', [])
1072
1145
  rftattrs = header.substitute(attrs='\n'.join(
1073
1146
  [f'[[*{x}][{x}]]' for x in inattrs]))
1074
- with open(output, 'w') as fp:
1147
+ with open(output, 'w', encoding='utf-8') as fp:
1075
1148
  fp.write(rftattrs)
1076
1149
  for attr in inattrs:
1077
1150
  infos = attrinfo.get(attr)
@@ -1164,7 +1237,7 @@ class Project:
1164
1237
  """))
1165
1238
 
1166
1239
  attrs = sorted(rftattrs.keys())
1167
- with open(output, 'w') as fp:
1240
+ with open(output, 'w', encoding='utf-8') as fp:
1168
1241
  fp.write(header.substitute(
1169
1242
  attrs='\n'.join([f'[[*{x}][{x}]]' for x in attrs])
1170
1243
  ))
@@ -498,7 +498,7 @@ class WebRegister(Register):
498
498
  conn.request("GET", url[k:])
499
499
  return conn.getresponse()
500
500
 
501
- def check_request_interval(self, delta=30.0):
501
+ def check_request_interval(self, delta=30.0, activation=False):
502
502
  """Make sure no more than 2 requests in 1 minute"""
503
503
  tspath = os.path.join(self.ctx.reg_path, 'last_register')
504
504
  try:
@@ -509,8 +509,10 @@ class WebRegister(Register):
509
509
  from time import sleep, time
510
510
  d = delta - (time() - st.st_mtime)
511
511
  if d > 0:
512
- logger.warning('caution: this activation file can only '
513
- 'be used no more than 10 times')
512
+ if activation:
513
+ logger.warning(
514
+ 'caution: this activation file "%s" can only '
515
+ 'be used no more than 10 times', activation)
514
516
  logger.info('waiting for %d seconds', d)
515
517
  while time() - st.st_mtime < delta:
516
518
  logger.info('waiting ...')
@@ -109,22 +109,34 @@ class FileResource(Resource):
109
109
  return self.pyext.lower() == '.pyc'
110
110
 
111
111
  def _get_encoding(self, encoding):
112
+ # Maybe: from tokenize import detect_encoding
112
113
  from codecs import BOM_UTF8
113
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
+
114
134
  with open(self.fullpath, 'rb') as f:
115
135
  line = f.read(80)
116
136
  if line and line[:3] == BOM_UTF8:
117
137
  return 'utf-8'
118
138
  if line and line[0] == 35:
119
- n = line.find(b'\n')
120
- m = research(r'coding[=:]\s*([-\w.]+)', line[:n].decode())
121
- if m:
122
- return m.group(1)
123
- if n > -1 and len(line) > (n+1) and line[n+1] == 35:
124
- k = n + 1
125
- n = line.find(b'\n', k)
126
- m = research(r'coding[=:]\s*([-\w.]+)', line[k:n].decode())
127
- return m and m.group(1)
139
+ encoding = find_encoding(line) or encoding
128
140
  return encoding
129
141
 
130
142
  def readlines(self, encoding=None):
@@ -86,7 +86,7 @@ class CfgEntity:
86
86
  self._reader = self._writer = None
87
87
 
88
88
  def save(self):
89
- with open(self.cfgfile, 'w') as f:
89
+ with open(self.cfgfile, 'w', encoding=self._encoding) as f:
90
90
  self.writer.write(f)
91
91
 
92
92
 
@@ -260,7 +260,7 @@ class PyarmorShell(cmd.Cmd):
260
260
  ctx.default_config,
261
261
  ctx.global_config,
262
262
  ctx.local_config
263
- ])
263
+ ], encoding=ctx.encoding)
264
264
  self._domains = self._init_domains()
265
265
  self._domain = self._section = self._domains[domain]
266
266
 
@@ -281,6 +281,9 @@ class PyarmorShell(cmd.Cmd):
281
281
  domain.add_group(name, GROUPS[name])
282
282
 
283
283
  ps.add_section('rft')
284
+ ps.add_section('mini')
285
+ ps.add_section('vmc')
286
+ ps.add_section('ecc')
284
287
 
285
288
  return {'global': gs, 'local': ls, 'project': ps}
286
289
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyarmor.cli
3
- Version: 9.2.0
3
+ Version: 9.2.2
4
4
  Summary: A comand line tool to obfuscate python scripts
5
5
  Home-page: https://github.com/dashingsoft/pyarmor
6
6
  Author: Jondy Zhao
@@ -8,6 +8,7 @@ pyarmor.cli.egg-info/requires.txt
8
8
  pyarmor.cli.egg-info/top_level.txt
9
9
  pyarmor/cli/__init__.py
10
10
  pyarmor/cli/__main__.py
11
+ pyarmor/cli/batch.py
11
12
  pyarmor/cli/bootstrap.py
12
13
  pyarmor/cli/bug.py
13
14
  pyarmor/cli/command.py
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup
2
2
 
3
- __VERSION__ = ' 9.2.0'
3
+ __VERSION__ = ' 9.2.2'
4
4
 
5
5
  with open('README.rst') as f:
6
6
  long_description = f.read()
Binary file
Binary file
Binary file
File without changes
File without changes