pyarmor.cli 9.1.2__tar.gz → 9.2.0__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 (40) hide show
  1. pyarmor_cli-9.2.0/PKG-INFO +74 -0
  2. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/__init__.py +1 -1
  3. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/__main__.py +2 -2
  4. pyarmor_cli-9.2.0/pyarmor/cli/core.data.1 +0 -0
  5. pyarmor_cli-9.2.0/pyarmor/cli/core.data.2 +0 -0
  6. pyarmor_cli-9.2.0/pyarmor/cli/core.data.3 +0 -0
  7. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/default.cfg +33 -6
  8. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/project.py +41 -12
  9. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/register.py +271 -39
  10. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/repack.py +1 -1
  11. pyarmor_cli-9.2.0/pyarmor.cli.egg-info/PKG-INFO +74 -0
  12. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor.cli.egg-info/entry_points.txt +0 -1
  13. pyarmor_cli-9.2.0/pyarmor.cli.egg-info/requires.txt +1 -0
  14. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/setup.py +2 -2
  15. pyarmor.cli-9.1.2/PKG-INFO +0 -74
  16. pyarmor.cli-9.1.2/pyarmor/cli/core.data.1 +0 -0
  17. pyarmor.cli-9.1.2/pyarmor/cli/core.data.2 +0 -0
  18. pyarmor.cli-9.1.2/pyarmor/cli/core.data.3 +0 -0
  19. pyarmor.cli-9.1.2/pyarmor.cli.egg-info/PKG-INFO +0 -74
  20. pyarmor.cli-9.1.2/pyarmor.cli.egg-info/requires.txt +0 -1
  21. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/README.rst +0 -0
  22. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/bootstrap.py +0 -0
  23. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/bug.py +0 -0
  24. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/command.py +0 -0
  25. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/config.py +0 -0
  26. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/context.py +0 -0
  27. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/docker.py +0 -0
  28. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/generate.py +0 -0
  29. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/hdinfo.py +0 -0
  30. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/merge.py +0 -0
  31. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/mixer.py +0 -0
  32. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/model.py +0 -0
  33. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/plugin.py +0 -0
  34. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/public_capsule.zip +0 -0
  35. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/resource.py +0 -0
  36. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor/cli/shell.py +0 -0
  37. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor.cli.egg-info/SOURCES.txt +0 -0
  38. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor.cli.egg-info/dependency_links.txt +0 -0
  39. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/pyarmor.cli.egg-info/top_level.txt +0 -0
  40. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.0}/setup.cfg +0 -0
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyarmor.cli
3
+ Version: 9.2.0
4
+ Summary: A comand line tool to obfuscate python scripts
5
+ Home-page: https://github.com/dashingsoft/pyarmor
6
+ Author: Jondy Zhao
7
+ Author-email: pyarmor@163.com
8
+ License: Free To Use But Restricted
9
+ Keywords: protect obfuscate encrypt obfuscation distribute
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: Free To Use But Restricted
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Build Tools
17
+ Classifier: Topic :: Utilities
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: System :: Software Distribution
20
+ Requires-Dist: pyarmor.cli.core~=8.1.0
21
+
22
+ Protect Python Scripts By Pyarmor
23
+ =================================
24
+
25
+ Pyarmor is a command line tool used to obfuscate python scripts, bind obfuscated scripts to fixed machine or expire obfuscated scripts.
26
+
27
+ Key Features
28
+ ------------
29
+
30
+ * The obfuscated scritpt is still a normal `.py` script, in most of cases the original python scripts can be replaced with obfuscated scripts seamlessly.
31
+ * Provide many ways to obfuscate the scripts to balance security and performance
32
+ * Rename functions/methods/classes/variables/arguments, irreversible obfuscation
33
+ * Convert part of Python functions to C function, compile to binary by high optimize option, irreversible obfuscation
34
+ * Bind obfuscated scripts to fixed machine or expire obfuscted scripts
35
+ * Protect obfuscated scripts by Themida (Only for Windows)
36
+
37
+ Support Platforms
38
+ -----------------
39
+
40
+ * Python 3.7~3.13
41
+ * Windows
42
+ * Many linuxs, include embedded systems
43
+ * Apple Intel and Apple Silicon
44
+
45
+ Quick Start
46
+ -----------
47
+
48
+ Install::
49
+
50
+ pip install pyarmor
51
+
52
+ Obfuscate the script `foo.py`::
53
+
54
+ pyarmor gen foo.py
55
+
56
+ This command generates an obfuscated script `dist/foo.py` like this:
57
+
58
+ .. code:: python
59
+
60
+ from pyarmor_runtime import __pyarmor__
61
+ __pyarmor__(__name__, __file__, b'\x28\x83\x20\x58....')
62
+
63
+ Run it::
64
+
65
+ python dist/foo.py
66
+
67
+ More Resources
68
+ --------------
69
+
70
+ - `Home <https://github.com/dashingsoft/pyarmor>`_
71
+ - `Website <http://pyarmor.dashingsoft.com>`_
72
+ - `中文网站 <http://pyarmor.dashingsoft.com/index-zh.html>`_
73
+ - `Issues <https://github.com/dashingsoft/pyarmor/issues>`_
74
+ - `Documentation <https://pyarmor.readthedocs.io/>`_
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- __VERSION__ = '9.1.2'
3
+ __VERSION__ = '9.2.0'
4
4
 
5
5
  logger = logging.getLogger('cli')
6
6
 
@@ -309,7 +309,7 @@ def cmd_reg(ctx, args):
309
309
  raise CliError('please use ".zip" file to register group device')
310
310
  regsvr = WebRegister(ctx)
311
311
  regsvr.check_request_interval()
312
- regsvr.register_group_device(regfile, args.device)
312
+ regsvr.request_device_regfile(regfile, args.device)
313
313
  logger.info('The device regfile has been generated successfully')
314
314
 
315
315
  elif args.ci:
@@ -764,7 +764,7 @@ def main_entry(argv):
764
764
  log_settings(ctx, args)
765
765
 
766
766
  x, y = sys.version_info[:2]
767
- if not (x == 3 and y > 6 and y < 14):
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
770
  if args.version:
@@ -2,14 +2,15 @@
2
2
 
3
3
  ;; Pyarmor version
4
4
  major = 9
5
- minor = 1
6
- patch = 2
5
+ minor = 2
6
+ patch = 0
7
7
 
8
8
  ;; Compatible core version
9
- cli.core = 7.6.4
10
- mini.core = 1.0
11
- ecc.core = 1.0
12
- vmc.core = 1.0
9
+ cli.core = 8.1.0
10
+ mini.core = 2.0
11
+
12
+ ;; ecc.core = 1.0
13
+ ;; vmc.core = 1.0
13
14
 
14
15
  ;; Deprecated since Pyarmor 8.2.5
15
16
  ; cli.runtime = 3.2.5
@@ -147,6 +148,16 @@ enable_jit = 0
147
148
  enable_bcc = 0
148
149
  enable_rft = 0
149
150
 
151
+ ;; Not implemented
152
+ ;; Enable opcode virtual protection
153
+ ;; This feature is meaningless in BCC mode
154
+ enable_vmc = 0
155
+
156
+ ;; New in v9.2.0
157
+ ;; Enable to obfuscate attribute name
158
+ ;; When obf_code == 2, it's enabled implicitly
159
+ mix_attr = 0
160
+
150
161
  ;; assert: call import
151
162
  assert_call = 0
152
163
  assert_import = 0
@@ -165,7 +176,16 @@ mix_localnames = 1
165
176
  ;; mix argument names, it also clears annotations
166
177
  mix_argnames = 0
167
178
 
179
+ ;; Obfuscate the whole module
168
180
  obf_module = 1
181
+
182
+ ;; Obfuscate each code object
183
+ ;;
184
+ ;; 0: no obfuscation for each co
185
+ ;; 1: obfuscate each co
186
+ ;; 2: obfuscate each co plus mix attribute name
187
+ ;; this is deprecated since 9.2.0, use
188
+ ;; obf_code == 1 and mix_attr = 1
169
189
  obf_code = 1
170
190
 
171
191
  ;; 0: no wrap mode
@@ -174,8 +194,15 @@ obf_code = 1
174
194
  ;; Since Python 3.12, "2" is same as "1", both of them use full wrap mode
175
195
  wrap_mode = 1
176
196
 
197
+ ;; 1: the obfuscated scripts can't be modified
198
+ ;; 2: private module, can't be imported by plain scripts
199
+ ;; 3: restrict module, module attribute is hidden for outer scripts
200
+ ;;
177
201
  restrict_module = 1
178
202
 
203
+ ;; if set to 1, module attribute can't be written by plain scripts
204
+ readonly_module = 0
205
+
179
206
  ;;
180
207
  ;; Advanced features
181
208
  ;;
@@ -187,7 +187,7 @@ class Module:
187
187
 
188
188
  @property
189
189
  def destpath(self):
190
- s = self.qualname + ('' if self._name else '.__init__')
190
+ s = self.qualname + ('' if self.name else '.__init__')
191
191
  return joinpath(*s.split('.')) + splitext(self.path)[-1]
192
192
 
193
193
  def compile_file(self, force=False):
@@ -241,17 +241,34 @@ class Script(Module):
241
241
  class Package(Module):
242
242
  """Package concept"""
243
243
 
244
- def __init__(self, path, name=None, parent=None):
244
+ def __init__(self, path, name=None, parent=None, excludes=[]):
245
245
  super().__init__(path, name=name, parent=parent)
246
246
 
247
247
  self._modules = None
248
248
  self._packages = None
249
+ self._excludes = excludes if excludes else GLOBAL_EXCLS
250
+ self._filters = None
251
+
252
+ @property
253
+ def filters(self):
254
+ if self._filters is None:
255
+ self._filters = []
256
+ for x in self._excludes:
257
+ i = x.find(':')
258
+ if i == -1:
259
+ self._filters.append(x)
260
+ elif fnmatch(self.qualname, x[:i]):
261
+ self._filters.append(x[i+1:])
262
+ return self._filters
249
263
 
250
264
  def load(self):
251
- excludes = GLOBAL_EXCLS
252
- files, dirs = scan_path(self.abspath, excludes=excludes)
265
+ excls = self.filters
266
+ files, dirs = scan_path(self.abspath, excludes=excls)
253
267
  self._modules = [Module(x, parent=self) for x in files]
254
- self._packages = [Package(x, parent=self) for x in dirs]
268
+ self._packages = [
269
+ Package(x, parent=self, excludes=self._excludes)
270
+ for x in dirs
271
+ ]
255
272
 
256
273
  @property
257
274
  def modules(self):
@@ -858,17 +875,19 @@ class Project:
858
875
  src = self.src = data['src']
859
876
  name = data.get('name')
860
877
  excludes = vlist('excludes') + list(GLOBAL_EXCLS)
878
+ proexcls = [x.strip(':') for x in excludes
879
+ if x.find(':') < 1]
861
880
 
862
881
  scripts = []
863
882
  for pat in vlist('scripts'):
864
- scripts.extend(search_item(src, pat, excludes))
883
+ scripts.extend(search_item(src, pat, proexcls))
865
884
  self._scripts.extend([
866
885
  Script(self.relsrc(x), parent=self) for x in scripts
867
886
  ])
868
887
 
869
888
  modules = []
870
889
  for pat in vlist('modules'):
871
- modules.extend(search_item(src, pat, excludes))
890
+ modules.extend(search_item(src, pat, proexcls))
872
891
 
873
892
  packages = vlist('packages')
874
893
  if packages:
@@ -883,7 +902,10 @@ class Project:
883
902
  path, pkgname = item.split('@')
884
903
  if not isabs(path):
885
904
  path = joinpath(src, path)
886
- obj = Package(path, name=pkgname, parent=self)
905
+ obj = Package(path,
906
+ name=pkgname,
907
+ parent=self,
908
+ excludes=excludes)
887
909
  self._packages.append(obj)
888
910
 
889
911
  recursive = data.get('recursive', '0')
@@ -891,10 +913,13 @@ class Project:
891
913
  pkginit = joinpath(src, '__init__.py')
892
914
  if exists(pkginit):
893
915
  pkgname = name if name else basename(src)
894
- obj = Package(src, name=pkgname, parent=self)
916
+ obj = Package(src,
917
+ name=pkgname,
918
+ parent=self,
919
+ excludes=excludes)
895
920
  self._packages.append(obj)
896
921
  else:
897
- files, dirs = scan_path(src, excludes=excludes)
922
+ files, dirs = scan_path(src, excludes=proexcls)
898
923
  modules.extend([joinpath(src, x) for x in files])
899
924
  self._packages.extend([
900
925
  Package(x, parent=self) for x in dirs
@@ -939,7 +964,7 @@ class Project:
939
964
 
940
965
  def _get_external_types(self, modname, pypaths=None):
941
966
  from sys import executable
942
- from subprocess import check_output, CalledProcessError
967
+ from subprocess import check_output, CalledProcessError, DEVNULL
943
968
  source = Template(dedent("""\
944
969
  import json
945
970
  import sys
@@ -960,12 +985,16 @@ class Project:
960
985
  """)).substitute(name=modname, pypaths=repr(pypaths))
961
986
 
962
987
  try:
963
- output = check_output([executable, '-c', source])
988
+ output = check_output([executable, '-c', source], stderr=DEVNULL)
964
989
  return jsonloads(output)
965
990
  except CalledProcessError:
966
991
  pass
967
992
 
968
993
  def get_external_type(self, modname, clsname=None):
994
+ if not all([x.isidentifier() for x in modname.split('.')]):
995
+ logger.debug('invalid external module: %s', modname)
996
+ return []
997
+
969
998
  extypes = self.used_external_types
970
999
  if modname not in extypes:
971
1000
  result = self._get_external_types(modname)
@@ -89,7 +89,7 @@ def parse_token(data):
89
89
 
90
90
  def show_help_page(prompt, url):
91
91
  choice = input('\n'.join(prompt)).lower()[:1]
92
- if choice == 'h':
92
+ if choice in ('h', 'y'):
93
93
  import webbrowser
94
94
  webbrowser.open(url)
95
95
  return choice
@@ -208,7 +208,7 @@ class Register(object):
208
208
  if len(line) == 192 and line.find(' ') == -1:
209
209
  return regname, line
210
210
 
211
- raise CliError('no registration code found in %s' % filename)
211
+ raise CliError('invalid activation file "%s"' % filename)
212
212
 
213
213
  def _register_offline_license(self, fzip, namelist):
214
214
  logger.info('machine id in group license: %s', ', '.join(
@@ -279,13 +279,15 @@ class Register(object):
279
279
  regname = regname.encode('utf-8')
280
280
  product = product.encode('utf-8')
281
281
  notes = notes.encode('utf-8')
282
- data = pack('<II8x20s28xBB2sB2sB2s', token,
282
+ sizes = len(regname), len(product), len(notes)
283
+ data = pack('<II8x20s28xBB{0}sB{1}sB{2}s'.format(*sizes),
284
+ token,
283
285
  rev | features << 8,
284
286
  licno.encode('utf-8'),
285
287
  0,
286
- len(regname), b'%s',
287
- len(product), b'%s',
288
- len(notes), b'%s') % (regname, product, notes)
288
+ sizes[0], regname,
289
+ sizes[1], product,
290
+ sizes[2], notes)
289
291
  return b64encode(data) + b' *=='
290
292
 
291
293
  def register_regfile(self, regfile, clean=True):
@@ -443,7 +445,7 @@ class Register(object):
443
445
  ]
444
446
  if lictype == 'trial':
445
447
  self.notes.append('* Can\'t obfuscate big script and mix str')
446
- elif lictype in ('bacic', 'pro'):
448
+ elif lictype in ('basic', 'pro'):
447
449
  self.notes.append('* Need verify license online '
448
450
  'when obfuscating scripts')
449
451
  elif lictype == 'group':
@@ -478,6 +480,16 @@ The upgraded license information will be''')
478
480
 
479
481
  class WebRegister(Register):
480
482
 
483
+ # Before v9.0: 1
484
+ # Pyarmor v9.0: 2
485
+ # Pyarmor v9.2: 3
486
+ LICENSE_REVSION = 3
487
+
488
+ # Before v9.0: no CI License
489
+ # Pyarmor v9.0: 1
490
+ # Pyarmor v9.2: 2
491
+ CI_LICENSE_REVSION = 2
492
+
481
493
  def _request(self, url):
482
494
  from http.client import HTTPSConnection
483
495
  n = len('https://')
@@ -497,8 +509,9 @@ class WebRegister(Register):
497
509
  from time import sleep, time
498
510
  d = delta - (time() - st.st_mtime)
499
511
  if d > 0:
500
- logger.info('last register was within %d seconds, '
501
- 'waiting for %d seconds', delta, d)
512
+ logger.warning('caution: this activation file can only '
513
+ 'be used no more than 10 times')
514
+ logger.info('waiting for %d seconds', d)
502
515
  while time() - st.st_mtime < delta:
503
516
  logger.info('waiting ...')
504
517
  sleep(3.0)
@@ -566,6 +579,8 @@ class WebRegister(Register):
566
579
  else:
567
580
  lines.append(upgrade_to_basic_info.substitute())
568
581
  else:
582
+ if info['lictype'] == 'OLD':
583
+ raise CliError('old license only works for Pyarmor <= 7.7.4')
569
584
  if info['lictype'] not in ('BASIC', 'PRO', 'GROUP', 'CI'):
570
585
  raise CliError('unknown license type %s' % info['lictype'])
571
586
  lines.append('This license registration information will be')
@@ -611,7 +626,7 @@ class WebRegister(Register):
611
626
 
612
627
  logger.info('send upgrade request to server')
613
628
  res = self._send_request(url)
614
- regfile = self._handle_response(res)
629
+ regfile, lictype = self._handle_response(res)
615
630
 
616
631
  logger.info('update license token')
617
632
  self.update_token()
@@ -631,15 +646,14 @@ class WebRegister(Register):
631
646
  reginfo = self.parse_keyfile(keyfile)
632
647
 
633
648
  url = self.regurl(reginfo[1], product=product)
634
- # Request license file with extra info by rev 2
635
- url += '&rev=2'
649
+ url += '&rev=' + str(self.LICENSE_REVSION)
636
650
  if upgrade:
637
651
  url += '&upgrade_to_basic=1'
638
652
  logger.debug('url: %s', url)
639
653
 
640
654
  logger.info('send request to server')
641
655
  res = self._send_request(url)
642
- regfile = self._handle_response(res)
656
+ regfile, lictype = self._handle_response(res)
643
657
 
644
658
  logger.info('')
645
659
  logger.info('the registration file "%s" has been generated', regfile)
@@ -651,35 +665,22 @@ class WebRegister(Register):
651
665
  '* Please backup regfile "%s", and '
652
666
  'use this file for next any registration' % regfile,
653
667
  '* Do not use this file in docker and CI/CD pipeline',
668
+ '',
654
669
  ]
655
-
656
- if group:
657
- notes.append('* Please check `pyarmor reg` in Man page for '
658
- 'how to register Pyarmor on offline device')
659
- logger.info('\n\nImport Notes:\n%s\n', '\n'.join(notes))
660
- return
661
-
662
- notes.append('')
663
- notes.append('Next register Pyarmor in build device by this command:')
664
-
665
- notes.append('\tpyarmor reg %s' % regfile)
666
- notes.append('')
667
670
  logger.info('\n\nImport Notes:\n%s\n', '\n'.join(notes))
668
671
 
669
- prompt = 'Do you want register Pyarmor in this machine? (Y/n) '
670
- choice = input(prompt).lower()[:1]
671
- if choice == 'y':
672
- logger.info('register "%s"', regfile)
673
- self.register_regfile(regfile)
674
- logger.info('This license registration information:\n\n%s', self)
672
+ input('Type Enter to continue ...')
673
+ LicenseHelper(self).run(lictype, regfile)
675
674
 
676
675
  def _handle_response(self, res):
677
676
  if res and res.code == 200:
677
+ lictype = None
678
678
  dis = res.headers.get('Content-Disposition')
679
679
  filename = dis.split('"')[1] if dis else 'pyarmor-regfile.zip'
680
680
  logger.info('write registration file "%s"', filename)
681
681
  data = res.read()
682
682
  if data.startswith(b'{"group":'):
683
+ lictype = 'G'
683
684
  n = data.find(b'}') + 1
684
685
  with open(filename, 'wb') as f:
685
686
  f.write(data[n:])
@@ -691,13 +692,15 @@ class WebRegister(Register):
691
692
  n += i
692
693
  with open(filename, 'wb') as f:
693
694
  f.write(data[n:])
694
- self._write_reg_info(filename, data[i:n])
695
+ reginfo = data[i:n]
696
+ lictype = json_loads(reginfo).get('type', None)
697
+ self._write_reg_info(filename, reginfo)
695
698
  else:
696
699
  # Only for request group token
697
700
  logger.debug('no REGINFO found')
698
701
  with open(filename, 'wb') as f:
699
702
  f.write(data)
700
- return filename
703
+ return filename, lictype
701
704
 
702
705
  elif res:
703
706
  data = res.read()
@@ -723,10 +726,11 @@ class WebRegister(Register):
723
726
  with ZipFile(filename, 'a') as f:
724
727
  f.writestr('group.info', data)
725
728
 
726
- def register_group_device(self, regfile, devid, rev=2):
729
+ def request_device_regfile(self, regfile, devid):
727
730
  from zipfile import ZipFile
728
731
  devfile = self.ctx.group_device_file(devid)
729
- logger.info('register device file "%s"', devfile)
732
+ rev = self.LICENSE_REVSION
733
+ logger.info('request device regfile "%s" (v%d)', devfile, rev)
730
734
  logger.info('use group license "%s"', regfile)
731
735
  if not os.path.exists(devfile):
732
736
  logger.error('please generate device file in offline device by')
@@ -770,7 +774,7 @@ class WebRegister(Register):
770
774
  logger.debug('url: %s', url)
771
775
 
772
776
  res = self._send_request(url)
773
- filename = self._handle_response(res)
777
+ filename, lictype = self._handle_response(res)
774
778
  with open(filename, 'rb') as f:
775
779
  data = f.read()
776
780
  os.makedirs(os.path.dirname(tokencache), exist_ok=True)
@@ -786,14 +790,18 @@ class WebRegister(Register):
786
790
  logger.info('please copy deivce regfile to offline device and run')
787
791
  logger.info(' pyarmor reg %s', filename)
788
792
 
793
+ register_group_device = request_device_regfile
794
+
789
795
  def _write_ci_info(self, filename, data):
790
796
  from zipfile import ZipFile
791
797
  logger.info('write ci information')
792
798
  with ZipFile(filename, 'a') as f:
793
799
  f.writestr('ci.token', data)
794
800
 
795
- def request_ci_regfile(self, regfile, rev=2):
796
- logger.info('request ci regfile by "%s"', regfile)
801
+ def request_ci_regfile(self, regfile):
802
+ rev = self.LICENSE_REVSION
803
+ cirev = self.CI_LICENSE_REVSION
804
+ logger.info('request ci regfile (v%d) by "%s"', cirev, regfile)
797
805
  from zipfile import ZipFile
798
806
 
799
807
  with ZipFile(regfile, 'r') as f:
@@ -810,7 +818,7 @@ class WebRegister(Register):
810
818
  raise CliError('invalid registration file "%s"', regfile)
811
819
 
812
820
  url = self.regurl('ci/%s' % ucode)
813
- paras = (('rev', str(rev)),)
821
+ paras = ('rev', str(rev)), ('cirev', str(cirev))
814
822
  url += '&'.join(['='.join(x) for x in paras])
815
823
  logger.debug('url: %s', url)
816
824
 
@@ -850,3 +858,227 @@ class WebRegister(Register):
850
858
  '\n\tpip install pyarmor==%s\n'
851
859
  '\tpyarmor reg %s\n',
852
860
  cifile, ver, cifile)
861
+
862
+
863
+ BASIC_LICENSE_HELP_INFO = Template('''
864
+ Using Basic license in CI/CD pipeline or docker container need extra steps, please check this page
865
+
866
+ $docurl/how-to/ci.html
867
+
868
+ More usage about Basic License, check this page
869
+
870
+ $docurl/how-to/register.html
871
+
872
+ If need register Pyarmor in build device, run this command:
873
+
874
+ pyarmor reg $regfile
875
+
876
+ ''')
877
+
878
+ PRO_LICENSE_HELP_INFO = Template('''
879
+ Pro license can't be used in CI/CD pipeline or docker container direclty.
880
+
881
+ A few times for debug purpose, about 60 runs per month, may work.
882
+
883
+ But there is one workaroud for Pro licnese in CI/CD pipeline, please check this page
884
+
885
+ $docurl/how-to/ci.html
886
+
887
+ More usage about Pro License, check this page
888
+
889
+ $docurl/how-to/register.html
890
+
891
+ If need register Pyarmor in build device, run this command:
892
+
893
+ pyarmor reg $regfile
894
+
895
+ ''')
896
+
897
+ GROUP_LICENSE_HELP_INFO = Template('''
898
+ Group License file `$regfile` is only used to request device regfile
899
+
900
+ In order to register Pyarmor in offline device:
901
+
902
+ 1. In the build device (may be offline), generate device info by this command
903
+
904
+ pyarmor reg -g 1
905
+
906
+ 2. In any online device, copy device file generated by first step, then request device regfile for this device
907
+
908
+ cp pyarmor-group-device.1 .pyarmor/group/
909
+ pyarmor reg -g 1 $regfile
910
+
911
+ 3. In the build device, register Pyarmor by device regfile
912
+
913
+ pyarmor reg pyarmor-device-regfile-xxxx.1.zip
914
+
915
+ More usage about CI License, check section `Using group licnese`
916
+
917
+ $docurl/how-to/register.html
918
+ ''')
919
+
920
+ CI_LICENSE_HELP_INFO = Template('''
921
+ CI license file `$regfile` is only used to request CI regfile
922
+
923
+ CI regfile is used to register Pyarmor in the CI/CD pipeline
924
+
925
+ If need request CI regfile, run this command:
926
+
927
+ pyarmor reg -C $regfile
928
+
929
+ Note that CI regfile can NOT be used in physical machine, and there is rate limits to register CI regfile in CI/CD pipeline and docker container.
930
+
931
+ It may need request new CI regfile after Pyarmor is upgraded, please check section `When need to request new CI regfile` in this page
932
+
933
+ $docurl/how-to/ci.html
934
+
935
+ More usage about CI License, check these pages
936
+
937
+ $docurl/how-to/register.html
938
+ $docurl/how-to/ci.html
939
+ ''')
940
+
941
+
942
+ class LicenseHelper(object):
943
+ """Only used for first activate license.
944
+
945
+ Help the beginner to understand how to use different licenses.
946
+
947
+ """
948
+
949
+ def __init__(self, parent):
950
+ self.parent = parent
951
+ self.docurl = parent.ctx.cfg.get(
952
+ 'pyarmor', 'docurl').rstrip('/').replace('{lang}', 'en')
953
+ self.print = print
954
+
955
+ def run(self, lictype, regfile):
956
+ if lictype == 'G':
957
+ self._group_license_helper(regfile)
958
+
959
+ elif lictype == 'C':
960
+ self._ci_license_helper(regfile)
961
+
962
+ elif lictype == 'Z':
963
+ self._pro_license_helper(regfile)
964
+
965
+ elif lictype in ('J', 'B', 'P'):
966
+ self._basic_license_helper(regfile)
967
+
968
+ else:
969
+ raise CliError('unknown license type "%s"' % lictype)
970
+
971
+ def _basic_license_helper(self, regfile):
972
+ self.print(BASIC_LICENSE_HELP_INFO.substitute(
973
+ docurl=self.docurl, regfile=regfile))
974
+
975
+ prompt = 'Yes (y), No (n), Quit (q): '
976
+ self.print('Show basic license usage in webbrowser? (n)')
977
+ choice = show_help_page([prompt], self.docurl + '/how-to/register.html')
978
+ if choice == 'q':
979
+ return
980
+
981
+ self.print('Show basic license for CI/CD pipeline or docker? (n)')
982
+ choice = show_help_page([prompt], self.docurl + '/how-to/ci.html')
983
+ if choice == 'q':
984
+ return
985
+
986
+ self.print('Do you want register Pyarmor in this machine? (y)')
987
+ choice = input(prompt).lower()[:1]
988
+ if choice == 'q':
989
+ return
990
+
991
+ if choice in ('y', ''):
992
+ self.print('register "%s"' % regfile)
993
+ self.parent.register_regfile(regfile)
994
+ self.print('This license registration information:\n\n'
995
+ '%s' % self.parent)
996
+
997
+ def _pro_license_helper(self, regfile):
998
+ self.print(PRO_LICENSE_HELP_INFO.substitute(
999
+ docurl=self.docurl, regfile=regfile))
1000
+
1001
+ prompt = 'Yes (y), No (n), Quit (q): '
1002
+ self.print('Show pro license usage in webbrowser? (n)')
1003
+ choice = show_help_page([prompt], self.docurl + '/how-to/register.html')
1004
+ if choice == 'q':
1005
+ return
1006
+
1007
+ self.print('Show pro license for CI/CD pipeline or docker? (n)')
1008
+ choice = show_help_page([prompt], self.docurl + '/how-to/ci.html')
1009
+ if choice == 'q':
1010
+ return
1011
+
1012
+ self.print('Do you want register Pyarmor in this machine? (y)')
1013
+ choice = input(prompt).lower()[:1]
1014
+ if choice == 'q':
1015
+ return
1016
+
1017
+ if choice in ('y', ''):
1018
+ self.print('register "%s"' % regfile)
1019
+ self.parent.register_regfile(regfile)
1020
+ self.print('This license registration information:\n\n'
1021
+ '%s' % self.parent)
1022
+
1023
+ def _group_license_helper(self, regfile):
1024
+ self.print(GROUP_LICENSE_HELP_INFO.substitute(
1025
+ docurl=self.docurl, regfile=regfile))
1026
+
1027
+ prompt = 'Yes (y), No (n), Quit (q): '
1028
+ self.print('Show group license usage in webbrowser? (n)')
1029
+ choice = show_help_page([prompt], self.docurl + '/how-to/register.html')
1030
+ if choice == 'q':
1031
+ return
1032
+
1033
+ self.print('Do you want register Pyarmor in this machine? (n)')
1034
+ choice = input(prompt).lower()[:1]
1035
+ if choice == 'q':
1036
+ return
1037
+
1038
+ if choice != 'y':
1039
+ return
1040
+
1041
+ self.print('Please assign one unused device no. to this device, '
1042
+ 'starts from 1')
1043
+ devid = None
1044
+ while devid is None:
1045
+ a = input('Type device no. (default is 1): ')
1046
+ if a == '':
1047
+ devid = 1
1048
+ elif (not a.isdigit()) or int(a) < 1 or int(a) > 100:
1049
+ self.print('invalid input')
1050
+ else:
1051
+ devid = int(a)
1052
+
1053
+ if devid:
1054
+ sep = '-' * 16
1055
+ devinfo = self.parent.ctx.group_device_file(devid)
1056
+ self.print('%s 1. generate device info' % sep)
1057
+ if os.path.exists(devinfo):
1058
+ logger.warning('old device file has been exists')
1059
+ else:
1060
+ self.parent.generate_group_device(devid)
1061
+ self.print('%s 2. request device regfile' % sep)
1062
+ self.parent.request_device_regfile(regfile, devid)
1063
+ self.print('%s 3. register Pyarmor with device regfile' % sep)
1064
+ self.parent.register(regfile.
1065
+ replace('.zip', '.%d.zip' % devid).
1066
+ replace('-regfile', '-device-regfile'))
1067
+
1068
+ def _ci_license_helper(self, regfile):
1069
+ self.print(CI_LICENSE_HELP_INFO.substitute(
1070
+ docurl=self.docurl, regfile=regfile))
1071
+
1072
+ prompt = 'Yes (y), No (n), Quit (q): '
1073
+ self.print('Show ci license usage in webbrowser? (n)')
1074
+ choice = show_help_page([prompt], self.docurl + '/how-to/ci.html')
1075
+ if choice == 'q':
1076
+ return
1077
+
1078
+ self.print('Do you want request one CI Regfile now? (y)')
1079
+ choice = input(prompt).lower()[:1]
1080
+ if choice == 'q':
1081
+ return
1082
+
1083
+ if choice in ('y', ''):
1084
+ self.parent.request_ci_regfile(regfile)
@@ -96,7 +96,7 @@ for name, path, kind in a.binaries:
96
96
 
97
97
  with open(resfile, 'wb') as f:
98
98
  marshal.dump(mlist + list(plist), f)
99
- with open(hookscript, 'w') as f:
99
+ with open(hookscript, 'w', encoding='utf-8') as f:
100
100
  f.write("hiddenimports=[%s]" % ", ".join([repr(x) for x in hiddenimports]))
101
101
  '''
102
102
 
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyarmor.cli
3
+ Version: 9.2.0
4
+ Summary: A comand line tool to obfuscate python scripts
5
+ Home-page: https://github.com/dashingsoft/pyarmor
6
+ Author: Jondy Zhao
7
+ Author-email: pyarmor@163.com
8
+ License: Free To Use But Restricted
9
+ Keywords: protect obfuscate encrypt obfuscation distribute
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: Free To Use But Restricted
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Build Tools
17
+ Classifier: Topic :: Utilities
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: System :: Software Distribution
20
+ Requires-Dist: pyarmor.cli.core~=8.1.0
21
+
22
+ Protect Python Scripts By Pyarmor
23
+ =================================
24
+
25
+ Pyarmor is a command line tool used to obfuscate python scripts, bind obfuscated scripts to fixed machine or expire obfuscated scripts.
26
+
27
+ Key Features
28
+ ------------
29
+
30
+ * The obfuscated scritpt is still a normal `.py` script, in most of cases the original python scripts can be replaced with obfuscated scripts seamlessly.
31
+ * Provide many ways to obfuscate the scripts to balance security and performance
32
+ * Rename functions/methods/classes/variables/arguments, irreversible obfuscation
33
+ * Convert part of Python functions to C function, compile to binary by high optimize option, irreversible obfuscation
34
+ * Bind obfuscated scripts to fixed machine or expire obfuscted scripts
35
+ * Protect obfuscated scripts by Themida (Only for Windows)
36
+
37
+ Support Platforms
38
+ -----------------
39
+
40
+ * Python 3.7~3.13
41
+ * Windows
42
+ * Many linuxs, include embedded systems
43
+ * Apple Intel and Apple Silicon
44
+
45
+ Quick Start
46
+ -----------
47
+
48
+ Install::
49
+
50
+ pip install pyarmor
51
+
52
+ Obfuscate the script `foo.py`::
53
+
54
+ pyarmor gen foo.py
55
+
56
+ This command generates an obfuscated script `dist/foo.py` like this:
57
+
58
+ .. code:: python
59
+
60
+ from pyarmor_runtime import __pyarmor__
61
+ __pyarmor__(__name__, __file__, b'\x28\x83\x20\x58....')
62
+
63
+ Run it::
64
+
65
+ python dist/foo.py
66
+
67
+ More Resources
68
+ --------------
69
+
70
+ - `Home <https://github.com/dashingsoft/pyarmor>`_
71
+ - `Website <http://pyarmor.dashingsoft.com>`_
72
+ - `中文网站 <http://pyarmor.dashingsoft.com/index-zh.html>`_
73
+ - `Issues <https://github.com/dashingsoft/pyarmor/issues>`_
74
+ - `Documentation <https://pyarmor.readthedocs.io/>`_
@@ -1,4 +1,3 @@
1
1
  [console_scripts]
2
2
  pyarmor = pyarmor.cli.__main__:main
3
3
  pyarmor-auth = pyarmor.cli.docker:main
4
-
@@ -0,0 +1 @@
1
+ pyarmor.cli.core~=8.1.0
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup
2
2
 
3
- __VERSION__ = '9.1.2'
3
+ __VERSION__ = ' 9.2.0'
4
4
 
5
5
  with open('README.rst') as f:
6
6
  long_description = f.read()
@@ -51,7 +51,7 @@ setup(
51
51
  package_data={"pyarmor.cli": ["default.cfg", "public_capsule.zip", "core.data.*"]},
52
52
 
53
53
  install_requires=[
54
- 'pyarmor.cli.core~=7.6.4'
54
+ 'pyarmor.cli.core~=8.1.0'
55
55
  ],
56
56
 
57
57
  entry_points={
@@ -1,74 +0,0 @@
1
- Metadata-Version: 1.1
2
- Name: pyarmor.cli
3
- Version: 9.1.2
4
- Summary: A comand line tool to obfuscate python scripts
5
- Home-page: https://github.com/dashingsoft/pyarmor
6
- Author: Jondy Zhao
7
- Author-email: pyarmor@163.com
8
- License: Free To Use But Restricted
9
- Description: Protect Python Scripts By Pyarmor
10
- =================================
11
-
12
- Pyarmor is a command line tool used to obfuscate python scripts, bind obfuscated scripts to fixed machine or expire obfuscated scripts.
13
-
14
- Key Features
15
- ------------
16
-
17
- * The obfuscated scritpt is still a normal `.py` script, in most of cases the original python scripts can be replaced with obfuscated scripts seamlessly.
18
- * Provide many ways to obfuscate the scripts to balance security and performance
19
- * Rename functions/methods/classes/variables/arguments, irreversible obfuscation
20
- * Convert part of Python functions to C function, compile to binary by high optimize option, irreversible obfuscation
21
- * Bind obfuscated scripts to fixed machine or expire obfuscted scripts
22
- * Protect obfuscated scripts by Themida (Only for Windows)
23
-
24
- Support Platforms
25
- -----------------
26
-
27
- * Python 3.7~3.13
28
- * Windows
29
- * Many linuxs, include embedded systems
30
- * Apple Intel and Apple Silicon
31
-
32
- Quick Start
33
- -----------
34
-
35
- Install::
36
-
37
- pip install pyarmor
38
-
39
- Obfuscate the script `foo.py`::
40
-
41
- pyarmor gen foo.py
42
-
43
- This command generates an obfuscated script `dist/foo.py` like this:
44
-
45
- .. code:: python
46
-
47
- from pyarmor_runtime import __pyarmor__
48
- __pyarmor__(__name__, __file__, b'\x28\x83\x20\x58....')
49
-
50
- Run it::
51
-
52
- python dist/foo.py
53
-
54
- More Resources
55
- --------------
56
-
57
- - `Home <https://github.com/dashingsoft/pyarmor>`_
58
- - `Website <http://pyarmor.dashingsoft.com>`_
59
- - `中文网站 <http://pyarmor.dashingsoft.com/index-zh.html>`_
60
- - `Issues <https://github.com/dashingsoft/pyarmor/issues>`_
61
- - `Documentation <https://pyarmor.readthedocs.io/>`_
62
-
63
- Keywords: protect obfuscate encrypt obfuscation distribute
64
- Platform: UNKNOWN
65
- Classifier: Programming Language :: Python :: 3
66
- Classifier: License :: Free To Use But Restricted
67
- Classifier: Operating System :: MacOS
68
- Classifier: Operating System :: Microsoft :: Windows
69
- Classifier: Operating System :: POSIX
70
- Classifier: Intended Audience :: Developers
71
- Classifier: Topic :: Software Development :: Build Tools
72
- Classifier: Topic :: Utilities
73
- Classifier: Topic :: Security
74
- Classifier: Topic :: System :: Software Distribution
Binary file
Binary file
Binary file
@@ -1,74 +0,0 @@
1
- Metadata-Version: 1.1
2
- Name: pyarmor.cli
3
- Version: 9.1.2
4
- Summary: A comand line tool to obfuscate python scripts
5
- Home-page: https://github.com/dashingsoft/pyarmor
6
- Author: Jondy Zhao
7
- Author-email: pyarmor@163.com
8
- License: Free To Use But Restricted
9
- Description: Protect Python Scripts By Pyarmor
10
- =================================
11
-
12
- Pyarmor is a command line tool used to obfuscate python scripts, bind obfuscated scripts to fixed machine or expire obfuscated scripts.
13
-
14
- Key Features
15
- ------------
16
-
17
- * The obfuscated scritpt is still a normal `.py` script, in most of cases the original python scripts can be replaced with obfuscated scripts seamlessly.
18
- * Provide many ways to obfuscate the scripts to balance security and performance
19
- * Rename functions/methods/classes/variables/arguments, irreversible obfuscation
20
- * Convert part of Python functions to C function, compile to binary by high optimize option, irreversible obfuscation
21
- * Bind obfuscated scripts to fixed machine or expire obfuscted scripts
22
- * Protect obfuscated scripts by Themida (Only for Windows)
23
-
24
- Support Platforms
25
- -----------------
26
-
27
- * Python 3.7~3.13
28
- * Windows
29
- * Many linuxs, include embedded systems
30
- * Apple Intel and Apple Silicon
31
-
32
- Quick Start
33
- -----------
34
-
35
- Install::
36
-
37
- pip install pyarmor
38
-
39
- Obfuscate the script `foo.py`::
40
-
41
- pyarmor gen foo.py
42
-
43
- This command generates an obfuscated script `dist/foo.py` like this:
44
-
45
- .. code:: python
46
-
47
- from pyarmor_runtime import __pyarmor__
48
- __pyarmor__(__name__, __file__, b'\x28\x83\x20\x58....')
49
-
50
- Run it::
51
-
52
- python dist/foo.py
53
-
54
- More Resources
55
- --------------
56
-
57
- - `Home <https://github.com/dashingsoft/pyarmor>`_
58
- - `Website <http://pyarmor.dashingsoft.com>`_
59
- - `中文网站 <http://pyarmor.dashingsoft.com/index-zh.html>`_
60
- - `Issues <https://github.com/dashingsoft/pyarmor/issues>`_
61
- - `Documentation <https://pyarmor.readthedocs.io/>`_
62
-
63
- Keywords: protect obfuscate encrypt obfuscation distribute
64
- Platform: UNKNOWN
65
- Classifier: Programming Language :: Python :: 3
66
- Classifier: License :: Free To Use But Restricted
67
- Classifier: Operating System :: MacOS
68
- Classifier: Operating System :: Microsoft :: Windows
69
- Classifier: Operating System :: POSIX
70
- Classifier: Intended Audience :: Developers
71
- Classifier: Topic :: Software Development :: Build Tools
72
- Classifier: Topic :: Utilities
73
- Classifier: Topic :: Security
74
- Classifier: Topic :: System :: Software Distribution
@@ -1 +0,0 @@
1
- pyarmor.cli.core~=7.6.4
File without changes
File without changes