pyarmor.cli 9.1.2__tar.gz → 9.2.1__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 (41) hide show
  1. pyarmor_cli-9.2.1/PKG-INFO +74 -0
  2. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/__init__.py +1 -1
  3. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/__main__.py +3 -3
  4. pyarmor_cli-9.2.1/pyarmor/cli/batch.py +62 -0
  5. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/command.py +42 -11
  6. pyarmor_cli-9.2.1/pyarmor/cli/core.data.1 +0 -0
  7. pyarmor_cli-9.2.1/pyarmor/cli/core.data.2 +0 -0
  8. pyarmor_cli-9.2.1/pyarmor/cli/core.data.3 +0 -0
  9. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/default.cfg +34 -7
  10. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/project.py +41 -12
  11. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/register.py +274 -40
  12. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/repack.py +1 -1
  13. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/resource.py +21 -9
  14. pyarmor_cli-9.2.1/pyarmor.cli.egg-info/PKG-INFO +74 -0
  15. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/SOURCES.txt +1 -0
  16. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/entry_points.txt +0 -1
  17. pyarmor_cli-9.2.1/pyarmor.cli.egg-info/requires.txt +1 -0
  18. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/setup.py +2 -2
  19. pyarmor.cli-9.1.2/PKG-INFO +0 -74
  20. pyarmor.cli-9.1.2/pyarmor/cli/core.data.1 +0 -0
  21. pyarmor.cli-9.1.2/pyarmor/cli/core.data.2 +0 -0
  22. pyarmor.cli-9.1.2/pyarmor/cli/core.data.3 +0 -0
  23. pyarmor.cli-9.1.2/pyarmor.cli.egg-info/PKG-INFO +0 -74
  24. pyarmor.cli-9.1.2/pyarmor.cli.egg-info/requires.txt +0 -1
  25. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/README.rst +0 -0
  26. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/bootstrap.py +0 -0
  27. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/bug.py +0 -0
  28. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/config.py +0 -0
  29. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/context.py +0 -0
  30. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/docker.py +0 -0
  31. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/generate.py +0 -0
  32. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/hdinfo.py +0 -0
  33. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/merge.py +0 -0
  34. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/mixer.py +0 -0
  35. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/model.py +0 -0
  36. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/plugin.py +0 -0
  37. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/public_capsule.zip +0 -0
  38. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/shell.py +0 -0
  39. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/dependency_links.txt +0 -0
  40. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/top_level.txt +0 -0
  41. {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/setup.cfg +0 -0
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyarmor.cli
3
+ Version: 9.2.1
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.1'
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:
@@ -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:
@@ -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:
@@ -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,27 @@ 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',
171
+ help='generate scripts by combining --rft and --ecc'
162
172
  )
163
173
  group.add_argument(
164
174
  '--list', dest='target', default='std',
@@ -412,6 +422,8 @@ class Commander:
412
422
  logger.info('build target %s ...', args.target)
413
423
  output = args.output if args.output else 'dist'
414
424
  self._build(project, args.target, output)
425
+ if args.target.startswith('std'):
426
+ self._cmd_gen(args)
415
427
  logger.info('build target %s end', args.target)
416
428
 
417
429
  def _build(self, project, target, output, value=None):
@@ -420,6 +432,25 @@ class Commander:
420
432
  m = Pytransform3.init(self.ctx)
421
433
  m.pre_build(args)
422
434
 
435
+ def _cmd_gen(self, args):
436
+ """If build target is std, then call `pyarmor gen`
437
+
438
+ It will obfuscate the scripts with `pyarmor_runtime.so`
439
+
440
+ Most of obfuscated options are got from config file, not from
441
+ command line
442
+
443
+ Option `--pack` may be still got from command line
444
+ """
445
+ from .generate import Builder
446
+ from .plugin import Plugin
447
+
448
+ ctx = self.ctx
449
+ builder = Builder(ctx)
450
+ Plugin.install(ctx)
451
+ builder.process(args)
452
+ Plugin.post_build(ctx)
453
+
423
454
  def cmd_env(self, ctx, args):
424
455
  """Check and change pyarmor settings
425
456
 
@@ -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 = 1
7
7
 
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
8
+ ;; Compatible core version for package: pyarmor.cli.core
9
+ cli.core = 8.1.0
10
+
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
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://')
@@ -486,7 +498,7 @@ class WebRegister(Register):
486
498
  conn.request("GET", url[k:])
487
499
  return conn.getresponse()
488
500
 
489
- def check_request_interval(self, delta=30.0):
501
+ def check_request_interval(self, delta=30.0, activation=False):
490
502
  """Make sure no more than 2 requests in 1 minute"""
491
503
  tspath = os.path.join(self.ctx.reg_path, 'last_register')
492
504
  try:
@@ -497,8 +509,11 @@ 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
+ if activation:
513
+ logger.warning(
514
+ 'caution: this activation file "%s" can only '
515
+ 'be used no more than 10 times', activation)
516
+ logger.info('waiting for %d seconds', d)
502
517
  while time() - st.st_mtime < delta:
503
518
  logger.info('waiting ...')
504
519
  sleep(3.0)
@@ -566,6 +581,8 @@ class WebRegister(Register):
566
581
  else:
567
582
  lines.append(upgrade_to_basic_info.substitute())
568
583
  else:
584
+ if info['lictype'] == 'OLD':
585
+ raise CliError('old license only works for Pyarmor <= 7.7.4')
569
586
  if info['lictype'] not in ('BASIC', 'PRO', 'GROUP', 'CI'):
570
587
  raise CliError('unknown license type %s' % info['lictype'])
571
588
  lines.append('This license registration information will be')
@@ -611,7 +628,7 @@ class WebRegister(Register):
611
628
 
612
629
  logger.info('send upgrade request to server')
613
630
  res = self._send_request(url)
614
- regfile = self._handle_response(res)
631
+ regfile, lictype = self._handle_response(res)
615
632
 
616
633
  logger.info('update license token')
617
634
  self.update_token()
@@ -631,15 +648,14 @@ class WebRegister(Register):
631
648
  reginfo = self.parse_keyfile(keyfile)
632
649
 
633
650
  url = self.regurl(reginfo[1], product=product)
634
- # Request license file with extra info by rev 2
635
- url += '&rev=2'
651
+ url += '&rev=' + str(self.LICENSE_REVSION)
636
652
  if upgrade:
637
653
  url += '&upgrade_to_basic=1'
638
654
  logger.debug('url: %s', url)
639
655
 
640
656
  logger.info('send request to server')
641
657
  res = self._send_request(url)
642
- regfile = self._handle_response(res)
658
+ regfile, lictype = self._handle_response(res)
643
659
 
644
660
  logger.info('')
645
661
  logger.info('the registration file "%s" has been generated', regfile)
@@ -651,35 +667,22 @@ class WebRegister(Register):
651
667
  '* Please backup regfile "%s", and '
652
668
  'use this file for next any registration' % regfile,
653
669
  '* Do not use this file in docker and CI/CD pipeline',
670
+ '',
654
671
  ]
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
672
  logger.info('\n\nImport Notes:\n%s\n', '\n'.join(notes))
668
673
 
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)
674
+ input('Type Enter to continue ...')
675
+ LicenseHelper(self).run(lictype, regfile)
675
676
 
676
677
  def _handle_response(self, res):
677
678
  if res and res.code == 200:
679
+ lictype = None
678
680
  dis = res.headers.get('Content-Disposition')
679
681
  filename = dis.split('"')[1] if dis else 'pyarmor-regfile.zip'
680
682
  logger.info('write registration file "%s"', filename)
681
683
  data = res.read()
682
684
  if data.startswith(b'{"group":'):
685
+ lictype = 'G'
683
686
  n = data.find(b'}') + 1
684
687
  with open(filename, 'wb') as f:
685
688
  f.write(data[n:])
@@ -691,13 +694,15 @@ class WebRegister(Register):
691
694
  n += i
692
695
  with open(filename, 'wb') as f:
693
696
  f.write(data[n:])
694
- self._write_reg_info(filename, data[i:n])
697
+ reginfo = data[i:n]
698
+ lictype = json_loads(reginfo).get('type', None)
699
+ self._write_reg_info(filename, reginfo)
695
700
  else:
696
701
  # Only for request group token
697
702
  logger.debug('no REGINFO found')
698
703
  with open(filename, 'wb') as f:
699
704
  f.write(data)
700
- return filename
705
+ return filename, lictype
701
706
 
702
707
  elif res:
703
708
  data = res.read()
@@ -723,10 +728,11 @@ class WebRegister(Register):
723
728
  with ZipFile(filename, 'a') as f:
724
729
  f.writestr('group.info', data)
725
730
 
726
- def register_group_device(self, regfile, devid, rev=2):
731
+ def request_device_regfile(self, regfile, devid):
727
732
  from zipfile import ZipFile
728
733
  devfile = self.ctx.group_device_file(devid)
729
- logger.info('register device file "%s"', devfile)
734
+ rev = self.LICENSE_REVSION
735
+ logger.info('request device regfile "%s" (v%d)', devfile, rev)
730
736
  logger.info('use group license "%s"', regfile)
731
737
  if not os.path.exists(devfile):
732
738
  logger.error('please generate device file in offline device by')
@@ -770,7 +776,7 @@ class WebRegister(Register):
770
776
  logger.debug('url: %s', url)
771
777
 
772
778
  res = self._send_request(url)
773
- filename = self._handle_response(res)
779
+ filename, lictype = self._handle_response(res)
774
780
  with open(filename, 'rb') as f:
775
781
  data = f.read()
776
782
  os.makedirs(os.path.dirname(tokencache), exist_ok=True)
@@ -786,14 +792,18 @@ class WebRegister(Register):
786
792
  logger.info('please copy deivce regfile to offline device and run')
787
793
  logger.info(' pyarmor reg %s', filename)
788
794
 
795
+ register_group_device = request_device_regfile
796
+
789
797
  def _write_ci_info(self, filename, data):
790
798
  from zipfile import ZipFile
791
799
  logger.info('write ci information')
792
800
  with ZipFile(filename, 'a') as f:
793
801
  f.writestr('ci.token', data)
794
802
 
795
- def request_ci_regfile(self, regfile, rev=2):
796
- logger.info('request ci regfile by "%s"', regfile)
803
+ def request_ci_regfile(self, regfile):
804
+ rev = self.LICENSE_REVSION
805
+ cirev = self.CI_LICENSE_REVSION
806
+ logger.info('request ci regfile (v%d) by "%s"', cirev, regfile)
797
807
  from zipfile import ZipFile
798
808
 
799
809
  with ZipFile(regfile, 'r') as f:
@@ -810,7 +820,7 @@ class WebRegister(Register):
810
820
  raise CliError('invalid registration file "%s"', regfile)
811
821
 
812
822
  url = self.regurl('ci/%s' % ucode)
813
- paras = (('rev', str(rev)),)
823
+ paras = ('rev', str(rev)), ('cirev', str(cirev))
814
824
  url += '&'.join(['='.join(x) for x in paras])
815
825
  logger.debug('url: %s', url)
816
826
 
@@ -850,3 +860,227 @@ class WebRegister(Register):
850
860
  '\n\tpip install pyarmor==%s\n'
851
861
  '\tpyarmor reg %s\n',
852
862
  cifile, ver, cifile)
863
+
864
+
865
+ BASIC_LICENSE_HELP_INFO = Template('''
866
+ Using Basic license in CI/CD pipeline or docker container need extra steps, please check this page
867
+
868
+ $docurl/how-to/ci.html
869
+
870
+ More usage about Basic License, check this page
871
+
872
+ $docurl/how-to/register.html
873
+
874
+ If need register Pyarmor in build device, run this command:
875
+
876
+ pyarmor reg $regfile
877
+
878
+ ''')
879
+
880
+ PRO_LICENSE_HELP_INFO = Template('''
881
+ Pro license can't be used in CI/CD pipeline or docker container direclty.
882
+
883
+ A few times for debug purpose, about 60 runs per month, may work.
884
+
885
+ But there is one workaroud for Pro licnese in CI/CD pipeline, please check this page
886
+
887
+ $docurl/how-to/ci.html
888
+
889
+ More usage about Pro License, check this page
890
+
891
+ $docurl/how-to/register.html
892
+
893
+ If need register Pyarmor in build device, run this command:
894
+
895
+ pyarmor reg $regfile
896
+
897
+ ''')
898
+
899
+ GROUP_LICENSE_HELP_INFO = Template('''
900
+ Group License file `$regfile` is only used to request device regfile
901
+
902
+ In order to register Pyarmor in offline device:
903
+
904
+ 1. In the build device (may be offline), generate device info by this command
905
+
906
+ pyarmor reg -g 1
907
+
908
+ 2. In any online device, copy device file generated by first step, then request device regfile for this device
909
+
910
+ cp pyarmor-group-device.1 .pyarmor/group/
911
+ pyarmor reg -g 1 $regfile
912
+
913
+ 3. In the build device, register Pyarmor by device regfile
914
+
915
+ pyarmor reg pyarmor-device-regfile-xxxx.1.zip
916
+
917
+ More usage about CI License, check section `Using group licnese`
918
+
919
+ $docurl/how-to/register.html
920
+ ''')
921
+
922
+ CI_LICENSE_HELP_INFO = Template('''
923
+ CI license file `$regfile` is only used to request CI regfile
924
+
925
+ CI regfile is used to register Pyarmor in the CI/CD pipeline
926
+
927
+ If need request CI regfile, run this command:
928
+
929
+ pyarmor reg -C $regfile
930
+
931
+ 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.
932
+
933
+ It may need request new CI regfile after Pyarmor is upgraded, please check section `When need to request new CI regfile` in this page
934
+
935
+ $docurl/how-to/ci.html
936
+
937
+ More usage about CI License, check these pages
938
+
939
+ $docurl/how-to/register.html
940
+ $docurl/how-to/ci.html
941
+ ''')
942
+
943
+
944
+ class LicenseHelper(object):
945
+ """Only used for first activate license.
946
+
947
+ Help the beginner to understand how to use different licenses.
948
+
949
+ """
950
+
951
+ def __init__(self, parent):
952
+ self.parent = parent
953
+ self.docurl = parent.ctx.cfg.get(
954
+ 'pyarmor', 'docurl').rstrip('/').replace('{lang}', 'en')
955
+ self.print = print
956
+
957
+ def run(self, lictype, regfile):
958
+ if lictype == 'G':
959
+ self._group_license_helper(regfile)
960
+
961
+ elif lictype == 'C':
962
+ self._ci_license_helper(regfile)
963
+
964
+ elif lictype == 'Z':
965
+ self._pro_license_helper(regfile)
966
+
967
+ elif lictype in ('J', 'B', 'P'):
968
+ self._basic_license_helper(regfile)
969
+
970
+ else:
971
+ raise CliError('unknown license type "%s"' % lictype)
972
+
973
+ def _basic_license_helper(self, regfile):
974
+ self.print(BASIC_LICENSE_HELP_INFO.substitute(
975
+ docurl=self.docurl, regfile=regfile))
976
+
977
+ prompt = 'Yes (y), No (n), Quit (q): '
978
+ self.print('Show basic license usage in webbrowser? (n)')
979
+ choice = show_help_page([prompt], self.docurl + '/how-to/register.html')
980
+ if choice == 'q':
981
+ return
982
+
983
+ self.print('Show basic license for CI/CD pipeline or docker? (n)')
984
+ choice = show_help_page([prompt], self.docurl + '/how-to/ci.html')
985
+ if choice == 'q':
986
+ return
987
+
988
+ self.print('Do you want register Pyarmor in this machine? (y)')
989
+ choice = input(prompt).lower()[:1]
990
+ if choice == 'q':
991
+ return
992
+
993
+ if choice in ('y', ''):
994
+ self.print('register "%s"' % regfile)
995
+ self.parent.register_regfile(regfile)
996
+ self.print('This license registration information:\n\n'
997
+ '%s' % self.parent)
998
+
999
+ def _pro_license_helper(self, regfile):
1000
+ self.print(PRO_LICENSE_HELP_INFO.substitute(
1001
+ docurl=self.docurl, regfile=regfile))
1002
+
1003
+ prompt = 'Yes (y), No (n), Quit (q): '
1004
+ self.print('Show pro license usage in webbrowser? (n)')
1005
+ choice = show_help_page([prompt], self.docurl + '/how-to/register.html')
1006
+ if choice == 'q':
1007
+ return
1008
+
1009
+ self.print('Show pro license for CI/CD pipeline or docker? (n)')
1010
+ choice = show_help_page([prompt], self.docurl + '/how-to/ci.html')
1011
+ if choice == 'q':
1012
+ return
1013
+
1014
+ self.print('Do you want register Pyarmor in this machine? (y)')
1015
+ choice = input(prompt).lower()[:1]
1016
+ if choice == 'q':
1017
+ return
1018
+
1019
+ if choice in ('y', ''):
1020
+ self.print('register "%s"' % regfile)
1021
+ self.parent.register_regfile(regfile)
1022
+ self.print('This license registration information:\n\n'
1023
+ '%s' % self.parent)
1024
+
1025
+ def _group_license_helper(self, regfile):
1026
+ self.print(GROUP_LICENSE_HELP_INFO.substitute(
1027
+ docurl=self.docurl, regfile=regfile))
1028
+
1029
+ prompt = 'Yes (y), No (n), Quit (q): '
1030
+ self.print('Show group license usage in webbrowser? (n)')
1031
+ choice = show_help_page([prompt], self.docurl + '/how-to/register.html')
1032
+ if choice == 'q':
1033
+ return
1034
+
1035
+ self.print('Do you want register Pyarmor in this machine? (n)')
1036
+ choice = input(prompt).lower()[:1]
1037
+ if choice == 'q':
1038
+ return
1039
+
1040
+ if choice != 'y':
1041
+ return
1042
+
1043
+ self.print('Please assign one unused device no. to this device, '
1044
+ 'starts from 1')
1045
+ devid = None
1046
+ while devid is None:
1047
+ a = input('Type device no. (default is 1): ')
1048
+ if a == '':
1049
+ devid = 1
1050
+ elif (not a.isdigit()) or int(a) < 1 or int(a) > 100:
1051
+ self.print('invalid input')
1052
+ else:
1053
+ devid = int(a)
1054
+
1055
+ if devid:
1056
+ sep = '-' * 16
1057
+ devinfo = self.parent.ctx.group_device_file(devid)
1058
+ self.print('%s 1. generate device info' % sep)
1059
+ if os.path.exists(devinfo):
1060
+ logger.warning('old device file has been exists')
1061
+ else:
1062
+ self.parent.generate_group_device(devid)
1063
+ self.print('%s 2. request device regfile' % sep)
1064
+ self.parent.request_device_regfile(regfile, devid)
1065
+ self.print('%s 3. register Pyarmor with device regfile' % sep)
1066
+ self.parent.register(regfile.
1067
+ replace('.zip', '.%d.zip' % devid).
1068
+ replace('-regfile', '-device-regfile'))
1069
+
1070
+ def _ci_license_helper(self, regfile):
1071
+ self.print(CI_LICENSE_HELP_INFO.substitute(
1072
+ docurl=self.docurl, regfile=regfile))
1073
+
1074
+ prompt = 'Yes (y), No (n), Quit (q): '
1075
+ self.print('Show ci license usage in webbrowser? (n)')
1076
+ choice = show_help_page([prompt], self.docurl + '/how-to/ci.html')
1077
+ if choice == 'q':
1078
+ return
1079
+
1080
+ self.print('Do you want request one CI Regfile now? (y)')
1081
+ choice = input(prompt).lower()[:1]
1082
+ if choice == 'q':
1083
+ return
1084
+
1085
+ if choice in ('y', ''):
1086
+ 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
 
@@ -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):
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.1
2
+ Name: pyarmor.cli
3
+ Version: 9.2.1
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/>`_
@@ -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,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.1'
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