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.
- pyarmor_cli-9.2.1/PKG-INFO +74 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/__init__.py +1 -1
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/__main__.py +3 -3
- pyarmor_cli-9.2.1/pyarmor/cli/batch.py +62 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/command.py +42 -11
- pyarmor_cli-9.2.1/pyarmor/cli/core.data.1 +0 -0
- pyarmor_cli-9.2.1/pyarmor/cli/core.data.2 +0 -0
- pyarmor_cli-9.2.1/pyarmor/cli/core.data.3 +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/default.cfg +34 -7
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/project.py +41 -12
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/register.py +274 -40
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/repack.py +1 -1
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/resource.py +21 -9
- pyarmor_cli-9.2.1/pyarmor.cli.egg-info/PKG-INFO +74 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/SOURCES.txt +1 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/entry_points.txt +0 -1
- pyarmor_cli-9.2.1/pyarmor.cli.egg-info/requires.txt +1 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/setup.py +2 -2
- pyarmor.cli-9.1.2/PKG-INFO +0 -74
- pyarmor.cli-9.1.2/pyarmor/cli/core.data.1 +0 -0
- pyarmor.cli-9.1.2/pyarmor/cli/core.data.2 +0 -0
- pyarmor.cli-9.1.2/pyarmor/cli/core.data.3 +0 -0
- pyarmor.cli-9.1.2/pyarmor.cli.egg-info/PKG-INFO +0 -74
- pyarmor.cli-9.1.2/pyarmor.cli.egg-info/requires.txt +0 -1
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/README.rst +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/bootstrap.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/bug.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/config.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/context.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/docker.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/generate.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/hdinfo.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/merge.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/mixer.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/model.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/plugin.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/public_capsule.zip +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor/cli/shell.py +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/dependency_links.txt +0 -0
- {pyarmor.cli-9.1.2 → pyarmor_cli-9.2.1}/pyarmor.cli.egg-info/top_level.txt +0 -0
- {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/>`_
|
|
@@ -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.
|
|
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 <
|
|
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='
|
|
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
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
;; Pyarmor version
|
|
4
4
|
major = 9
|
|
5
|
-
minor =
|
|
6
|
-
patch =
|
|
5
|
+
minor = 2
|
|
6
|
+
patch = 1
|
|
7
7
|
|
|
8
|
-
;; Compatible core version
|
|
9
|
-
cli.core =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
|
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
|
-
|
|
252
|
-
files, dirs = scan_path(self.abspath, 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 = [
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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=
|
|
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
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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 ('
|
|
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
|
-
|
|
501
|
-
|
|
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
|
-
|
|
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
|
-
|
|
670
|
-
|
|
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
|
-
|
|
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
|
|
731
|
+
def request_device_regfile(self, regfile, devid):
|
|
727
732
|
from zipfile import ZipFile
|
|
728
733
|
devfile = self.ctx.group_device_file(devid)
|
|
729
|
-
|
|
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
|
|
796
|
-
|
|
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 = (
|
|
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
|
-
|
|
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/>`_
|
|
@@ -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
|
|
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~=
|
|
54
|
+
'pyarmor.cli.core~=8.1.0'
|
|
55
55
|
],
|
|
56
56
|
|
|
57
57
|
entry_points={
|
pyarmor.cli-9.1.2/PKG-INFO
DELETED
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|