pyarmor 8.2.8__zip → 8.2.9__zip

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 (119) hide show
  1. {pyarmor-8.2.8 → pyarmor-8.2.9}/PKG-INFO +1 -1
  2. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/__init__.py +1 -1
  3. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/__main__.py +3 -4
  4. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/default.cfg +3 -3
  5. pyarmor-8.2.9/pyarmor/cli/docker.py +148 -0
  6. pyarmor-8.2.8/src/cli/docker.py → pyarmor-8.2.9/pyarmor/cli/group.py +2 -54
  7. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/register.py +3 -1
  8. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/config.py +1 -1
  9. pyarmor-8.2.9/pyarmor/pyarmor-deprecated.py +843 -0
  10. pyarmor-8.2.9/pyarmor/pyimcore.py +65 -0
  11. {pyarmor-8.2.8 → pyarmor-8.2.9}/pyarmor.egg-info/PKG-INFO +1 -1
  12. pyarmor-8.2.9/pyarmor.egg-info/SOURCES.txt +115 -0
  13. {pyarmor-8.2.8 → pyarmor-8.2.9}/pyarmor.egg-info/entry_points.txt +1 -0
  14. pyarmor-8.2.9/pyarmor.egg-info/requires.txt +1 -0
  15. {pyarmor-8.2.8 → pyarmor-8.2.9}/setup.py +3 -2
  16. pyarmor-8.2.8/pyarmor.egg-info/SOURCES.txt +0 -112
  17. pyarmor-8.2.8/pyarmor.egg-info/requires.txt +0 -1
  18. {pyarmor-8.2.8 → pyarmor-8.2.9}/MANIFEST.in +0 -0
  19. {pyarmor-8.2.8 → pyarmor-8.2.9}/README.md +0 -0
  20. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/README.rst +0 -0
  21. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/__init__.py +0 -0
  22. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/benchmark.py +0 -0
  23. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/build_meta.py +0 -0
  24. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/bootstrap.py +0 -0
  25. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/config.py +0 -0
  26. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/context.py +0 -0
  27. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/generate.py +0 -0
  28. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/merge.py +0 -0
  29. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/mixer.py +0 -0
  30. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/plugin.py +0 -0
  31. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/public_capsule.zip +0 -0
  32. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/repack.py +0 -0
  33. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/resource.py +0 -0
  34. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cli/shell.py +0 -0
  35. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/cobuilder.py +0 -0
  36. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/README-ZH.md +0 -0
  37. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/README.md +0 -0
  38. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/build-for-exe.bat +0 -0
  39. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/build-for-freeze.bat +0 -0
  40. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/build-with-project.bat +0 -0
  41. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/build-with-project.sh +0 -0
  42. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/cx_Freeze/hello.py +0 -0
  43. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/cx_Freeze/queens.py +0 -0
  44. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/cx_Freeze/setup.py +0 -0
  45. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/helloworld/foo.py +0 -0
  46. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/obfuscate-app.bat +0 -0
  47. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/obfuscate-app.sh +0 -0
  48. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/obfuscate-pkg.bat +0 -0
  49. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/obfuscate-pkg.sh +0 -0
  50. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pack-obfuscated-scripts.bat +0 -0
  51. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pack-obfuscated-scripts.sh +0 -0
  52. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/py2exe/hello.py +0 -0
  53. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/py2exe/queens.py +0 -0
  54. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/py2exe/setup.py +0 -0
  55. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Arithmetic.py +0 -0
  56. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Calls.py +0 -0
  57. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/CommandLine.py +0 -0
  58. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Constructs.py +0 -0
  59. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Dict.py +0 -0
  60. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Exceptions.py +0 -0
  61. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Imports.py +0 -0
  62. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Instances.py +0 -0
  63. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Lists.py +0 -0
  64. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Lookups.py +0 -0
  65. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/NewInstances.py +0 -0
  66. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Numbers.py +0 -0
  67. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Setup.py +0 -0
  68. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Strings.py +0 -0
  69. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/Tuples.py +0 -0
  70. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/With.py +0 -0
  71. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/clockres.py +0 -0
  72. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/package/__init__.py +0 -0
  73. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/package/submodule.py +0 -0
  74. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/pybench.py +0 -0
  75. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/pybench/systimes.py +0 -0
  76. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/simple/queens.py +0 -0
  77. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/testmod/hello.py +0 -0
  78. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/testmod/queens.py +0 -0
  79. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/testpkg/main.py +0 -0
  80. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/testpkg/mypkg/__init__.py +0 -0
  81. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/examples/testpkg/mypkg/foo.py +0 -0
  82. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/__init__.py +0 -0
  83. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/build_data_module.py +0 -0
  84. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/buildext.py +0 -0
  85. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/get_bind_key.py +0 -0
  86. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/get_license_info.py +0 -0
  87. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/get_platform_name.py +0 -0
  88. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/merge.py +0 -0
  89. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/repack.py +0 -0
  90. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/helper/superuntime.py +0 -0
  91. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/license.tri +0 -0
  92. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/packer.py +0 -0
  93. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/platforms/darwin/x86_64/_pytransform.dylib +0 -0
  94. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/platforms/linux/x86/_pytransform.so +0 -0
  95. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/platforms/linux/x86_64/_pytransform.so +0 -0
  96. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/platforms/windows/x86/_pytransform.dll +0 -0
  97. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/platforms/windows/x86_64/_pytransform.dll +0 -0
  98. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/plugins/README.md +0 -0
  99. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/plugins/check_ntp_time.py +0 -0
  100. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/polyfills/__init__.py +0 -0
  101. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/polyfills/argparse.py +0 -0
  102. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/product.key +0 -0
  103. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/project.py +0 -0
  104. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/protect_code.pt +0 -0
  105. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/protect_code2.pt +0 -0
  106. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/public.key +0 -0
  107. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/public_capsule.zip +0 -0
  108. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/pyarmor-webui.py +0 -0
  109. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/pyarmor.py +0 -0
  110. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/pyshield.key +0 -0
  111. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/pyshield.lic +0 -0
  112. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/pytransform.py +0 -0
  113. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/reform.py +0 -0
  114. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/register.py +0 -0
  115. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/sppmode.py +0 -0
  116. {pyarmor-8.2.8/src → pyarmor-8.2.9/pyarmor}/utils.py +0 -0
  117. {pyarmor-8.2.8 → pyarmor-8.2.9}/pyarmor.egg-info/dependency_links.txt +0 -0
  118. {pyarmor-8.2.8 → pyarmor-8.2.9}/pyarmor.egg-info/top_level.txt +0 -0
  119. {pyarmor-8.2.8 → pyarmor-8.2.9}/setup.cfg +0 -0
@@ -0,0 +1,843 @@
1
+ #! /usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ #############################################################
5
+ # #
6
+ # Copyright @ 2013 - 2017 Dashingsoft corp. #
7
+ # All rights reserved. #
8
+ # #
9
+ # pyarmor #
10
+ # #
11
+ # Version: 1.7.0 - 3.3.0 #
12
+ # #
13
+ #############################################################
14
+ #
15
+ # DEPRECATED from v3.4. It will be replaced by pyarmor2.py
16
+ # from v4.
17
+ #
18
+ # @File: pyarmor.py
19
+ #
20
+ # @Author: Jondy Zhao(jondy.zhao@gmail.com)
21
+ #
22
+ # @Create Date: 2013/07/24
23
+ #
24
+ # @Description:
25
+ #
26
+ # A tool used to import or un encrypted python scripts.
27
+ #
28
+ from distutils.filelist import FileList
29
+ from distutils.text_file import TextFile
30
+ from distutils.util import get_platform
31
+
32
+ import fnmatch
33
+ import getopt
34
+ import glob
35
+ import imp
36
+ import logging
37
+ import os
38
+ import shutil
39
+ import sys
40
+ import tempfile
41
+ import time
42
+ from zipfile import ZipFile
43
+
44
+ try:
45
+ unhexlify = bytes.fromhex
46
+ except Exception:
47
+ from binascii import a2b_hex as unhexlify
48
+
49
+ from config import (version, version_info)
50
+
51
+ dll_name = '_pytransform'
52
+ dll_ext = '.dylib' if sys.platform == 'darwin' \
53
+ else '.dll' if sys.platform in ('win32', 'cygwin') else '.so'
54
+
55
+ # Extra suffix char for encrypted python scripts
56
+ ext_char = 'e'
57
+
58
+ wrap_runner = '''import pyimcore
59
+ from pytransform import exec_file
60
+ exec_file('%s')
61
+ '''
62
+
63
+ trial_info = '''
64
+ You're using trail version. Free trial version never expires,
65
+ the limitations are
66
+
67
+ - The maximum size of code object is 35728 bytes in trial version
68
+ - The scripts obfuscated by trial version are not private. It means
69
+ anyone could generate the license file which works for these
70
+ obfuscated scripts.
71
+
72
+ A registration code is required to obfuscate big code object or
73
+ generate private obfuscated scripts.
74
+
75
+ If PyArmor is helpful for you, please purchase one by visiting
76
+
77
+ https://order.shareit.com/cart/add?vendorid=200089125&PRODUCT[300871197]=1
78
+
79
+ If you have received a registration code, run the following command to
80
+ make it effective::
81
+
82
+ pyarmor register REGISTRATION_CODE
83
+
84
+ Enjoy it!
85
+
86
+ '''
87
+
88
+ help_footer = '''
89
+ For more information, refer to http://pyarmor.dashingsoft.com
90
+ '''
91
+
92
+ # The last three components of the filename before the extension are
93
+ # called "compatibility tags." The compatibility tags express the
94
+ # package's basic interpreter requirements and are detailed in PEP
95
+ # 425(https://www.python.org/dev/peps/pep-0425).
96
+ plat_name = get_platform().split('-')
97
+ plat_name = '_'.join(plat_name if len(plat_name) < 3 else plat_name[0:3:2])
98
+ plat_name = plat_name.replace('i586', 'i386') \
99
+ .replace('i686', 'i386') \
100
+ .replace('armv7l', 'armv7') \
101
+ .replace('intel', 'x86_64')
102
+
103
+ def _import_pytransform():
104
+ try:
105
+ m = __import__('pytransform')
106
+ if hasattr(m, 'plat_path'):
107
+ m.plat_path = 'platforms'
108
+ m.pyarmor_init(is_runtime=1)
109
+ return m
110
+ except Exception:
111
+ pass
112
+ logging.info('Searching pytransform library ...')
113
+ path = sys.rootdir
114
+ pname = plat_name.replace('i586', 'i386').replace('i686', 'i386')
115
+ src = os.path.join(path, 'platforms', pname, dll_name + dll_ext)
116
+ if os.path.exists(src):
117
+ logging.info('Find pytransform library "%s"', src)
118
+ logging.info('Copy %s to %s', src, path)
119
+ shutil.copy(src, path)
120
+ m = __import__('pytransform')
121
+ if hasattr(m, 'plat_path'):
122
+ m.plat_path = 'platforms'
123
+ m.pyarmor_init(is_runtime=1)
124
+ logging.info('Load pytransform OK.')
125
+ return m
126
+ logging.error('No library %s found', src)
127
+
128
+ def _get_registration_code():
129
+ try:
130
+ code = pytransform.get_registration_code()
131
+ except Exception:
132
+ code = ''
133
+ return code
134
+
135
+ def checklicense(func):
136
+ # Fix python25 no "as" keyword in statement "except"
137
+ exc_msg = lambda : str(sys.exc_info()[1])
138
+ def wrap(*arg, **kwargs):
139
+ code = _get_registration_code()
140
+ if code == '':
141
+ sys.stderr.write('PyArmor Trial Version %s\n' % version)
142
+ sys.stderr.write(trial_info)
143
+ else:
144
+ sys.stderr.write('PyArmor Version %s\n' % version)
145
+ try:
146
+ func(*arg, **kwargs)
147
+ except RuntimeError:
148
+ logging.error(exc_msg())
149
+ except getopt.GetoptError:
150
+ logging.error(exc_msg())
151
+ except pytransform.PytransformError:
152
+ logging.error(exc_msg())
153
+ wrap.__doc__ = func.__doc__
154
+ return wrap
155
+
156
+ def show_version_info(verbose=True):
157
+ code = _get_registration_code()
158
+ trial = ' Trial' if code == '' else ''
159
+ print('PyArmor%s Version %s' % (trial, version))
160
+ if verbose:
161
+ print(version_info)
162
+ if code == '':
163
+ print(trial_info)
164
+ print(help_footer)
165
+
166
+ def show_hd_info():
167
+ pytransform.show_hd_info()
168
+
169
+ def usage(command=None):
170
+ '''
171
+ Usage: pyarmor [command name] [options]
172
+
173
+ Command name can be one of the following list
174
+
175
+ help Show this usage
176
+ version Show version information
177
+ capsule Generate project capsule used to encrypted files
178
+ encrypt Encrypt the scripts
179
+ license Generate registration code
180
+
181
+ If you want to know the usage of each command, type the
182
+ following command:
183
+
184
+ pyarmor help [command name]
185
+
186
+ and you can type the left match command, such as
187
+
188
+ pyarmor c
189
+ or pyarmor cap
190
+ or pyarmor capsule
191
+
192
+ '''
193
+ show_version_info(verbose=False)
194
+
195
+ if command is None:
196
+ print(usage.__doc__)
197
+ else:
198
+ funcname = 'do_' + command
199
+ func = globals().get(funcname, usage)
200
+ print(func.__doc__)
201
+ print(help_footer)
202
+
203
+ def make_capsule(rootdir=None, filename='project.zip'):
204
+ '''Generate all the files used by running encrypted scripts, pack all
205
+ of them to a zip file.
206
+
207
+ rootdir pyarmor root dir, where you can find license files,
208
+ auxiliary library and pyshield extension module.
209
+
210
+ filename output filename, the default value is project.zip
211
+
212
+ Return True if sucess, otherwise False or raise exception
213
+ '''
214
+ try:
215
+ if rootdir is None:
216
+ rootdir = sys.rootdir
217
+ except AttributeError:
218
+ rootdir = os.path.dirname(os.path.abspath(sys.argv[0]))
219
+ logging.info('Rootdir is %s', rootdir)
220
+ filelist = 'public.key', 'pyimcore.py', 'pytransform.py'
221
+ for x in filelist:
222
+ src = os.path.join(rootdir, x)
223
+ if not os.path.exists(src):
224
+ raise RuntimeError('No %s found in the rootdir' % src)
225
+
226
+ licfile = os.path.join(rootdir, 'license.lic')
227
+ if not os.path.exists(licfile):
228
+ raise RuntimeError('Missing license file %s' % licfile)
229
+
230
+ logging.info('Generating project key ...')
231
+ pri, pubx, capkey, lic = pytransform.generate_project_capsule(licfile)
232
+ logging.info('Generating project OK.')
233
+ logging.info('Writing capsule to %s ...', filename)
234
+ myzip = ZipFile(filename, 'w')
235
+ try:
236
+ myzip.write(os.path.join(rootdir, 'public.key'), 'pyshield.key')
237
+ myzip.writestr('pyshield.lic', capkey)
238
+ myzip.write(os.path.join(rootdir, 'pyimcore.py'), 'pyimcore.py')
239
+ myzip.write(os.path.join(rootdir, 'pytransform.py'), 'pytransform.py')
240
+ myzip.writestr('private.key', pri)
241
+ myzip.writestr('product.key', pubx)
242
+ myzip.writestr('license.lic', lic)
243
+ finally:
244
+ myzip.close()
245
+ logging.info('Write project capsule OK.')
246
+
247
+ def encrypt_files(files, prokey, mode=8, output=None):
248
+ '''Encrypt all the files, all the encrypted scripts will be plused with
249
+ a suffix 'e', for example, hello.py -> hello.pye
250
+
251
+ files list all the scripts
252
+ prokey project key file used to encrypt scripts
253
+ output output directory. If None, the output file will be saved
254
+ in the same path as the original script
255
+
256
+ Return None if sucess, otherwise raise exception
257
+ '''
258
+ ext = '.py' if mode in (7, 8, 9, 10, 11, 12, 13, 14) else \
259
+ '.pyc' if mode in (1, 3, 4, 5, 6) else '.py' + ext_char
260
+ if output is None:
261
+ fn = lambda a, b: b[1] + ext
262
+ else:
263
+ # fn = lambda a, b : os.path.join(a, os.path.basename(b) + ch)
264
+ # fn = lambda a, b: os.path.join(a, b[1] + ext)
265
+ if not os.path.exists(output):
266
+ os.makedirs(output)
267
+ def _get_path(a, b):
268
+ p = os.path.join(a, b[1] + ext)
269
+ d = os.path.dirname(p)
270
+ if not os.path.exists(d):
271
+ os.makedirs(d)
272
+ return p
273
+ fn = _get_path
274
+ flist = []
275
+ for x in files:
276
+ flist.append((x[0], fn(output, x)))
277
+ logging.info('Encrypt %s to %s', *flist[-1])
278
+
279
+ if len(flist[:1]) == 0:
280
+ logging.info('No any script specified')
281
+ else:
282
+ if not os.path.exists(prokey):
283
+ raise RuntimeError('Missing project key "%s"' % prokey)
284
+ pytransform.encrypt_project_files(prokey, tuple(flist), mode)
285
+ logging.info('Encrypt all scripts OK.')
286
+
287
+ def make_license(capsule, filename, code):
288
+ myzip = ZipFile(capsule, 'r')
289
+ myzip.extract('private.key', tempfile.gettempdir())
290
+ prikey = os.path.join(tempfile.tempdir, 'private.key')
291
+ try:
292
+ pytransform.generate_license_file(filename, prikey, code)
293
+ finally:
294
+ os.remove(prikey)
295
+
296
+ @checklicense
297
+ def do_capsule(argv):
298
+ '''Usage: pyarmor capsule [OPTIONS] [NAME]
299
+
300
+ Generate a capsule which used to encrypt/decrypt python scripts later,
301
+ it will generate random capsule when run this command again. Note that
302
+ the trial version of PyArmor will always generate same project capsule
303
+
304
+ Generately, one project, one capsule.
305
+
306
+ Available options:
307
+
308
+ -O, --output=DIR [option] The path used to save license file.
309
+
310
+ -f, --force [option] Overwrite output file even it exists.
311
+
312
+ For example,
313
+
314
+ - Generate default capsule "project.zip":
315
+
316
+ pyarmor capsule project
317
+
318
+ - Generate a capsule "mycapsules/foo.zip":
319
+
320
+ pyarmor capsule --output mycapsules foo
321
+
322
+ '''
323
+ opts, args = getopt.getopt(argv, 'fO:', ['force', 'output='])
324
+
325
+ output = os.getcwd()
326
+ overwrite = False
327
+ for o, a in opts:
328
+ if o in ('-O', '--output'):
329
+ output = a
330
+ elif o in ('-f', '--force'):
331
+ overwrite = True
332
+
333
+ if len(args) == 0:
334
+ filename = os.path.join(output, 'project.zip')
335
+ else:
336
+ filename = os.path.join(output, '%s.zip' % args[0])
337
+
338
+ if os.path.exists(filename) and not overwrite:
339
+ logging.info("Specify -f to overwrite it if you really want to do")
340
+ raise RuntimeError("Capsule %s already exists" % filename)
341
+
342
+ if not os.path.exists(output):
343
+ logging.info("Make output path %s", output)
344
+ os.makedirs(output)
345
+
346
+ logging.info('Output filename is %s', filename)
347
+ make_capsule(sys.rootdir, filename)
348
+ logging.info('Generate capsule OK.')
349
+
350
+ def _parse_template_file(filename, path=None):
351
+ template = TextFile(filename,
352
+ strip_comments=1,
353
+ skip_blanks=1,
354
+ join_lines=1,
355
+ lstrip_ws=1,
356
+ rstrip_ws=1,
357
+ collapse_join=1)
358
+ lines = template.readlines()
359
+
360
+ filelist = FileList()
361
+ try:
362
+ if path is not None and not path == os.getcwd():
363
+ oldpath = os.getcwd()
364
+ os.chdir(path)
365
+ else:
366
+ oldpath = None
367
+
368
+ for line in lines:
369
+ filelist.process_template_line(line)
370
+ finally:
371
+ if oldpath is not None:
372
+ os.chdir(oldpath)
373
+ return filelist.files
374
+
375
+ def _parse_file_args(args, srcpath=None):
376
+ filelist = []
377
+
378
+ if srcpath is None:
379
+ path, n = '', 0
380
+ else:
381
+ path, n = srcpath, len(srcpath) + 1
382
+
383
+ if len(args) == 1 and args[0][0] == '@' and args[0].endswith('MANIFEST.in'):
384
+ for x in _parse_template_file(args[0][1:], path=srcpath):
385
+ filelist.append((os.path.join(path, x), os.path.splitext(x)[0]))
386
+ return filelist
387
+
388
+ patterns = []
389
+ for arg in args:
390
+ if arg[0] == '@':
391
+ f = open(arg[1:], 'r')
392
+ for pattern in f.read().splitlines():
393
+ if not pattern.strip() == '':
394
+ patterns.append(pattern.strip())
395
+ f.close()
396
+ else:
397
+ patterns.append(arg)
398
+
399
+ for pat in patterns:
400
+ for name in glob.glob(os.path.join(path, pat)):
401
+ p = os.path.splitext(name)
402
+ filelist.append((name, p[0][n:]))
403
+
404
+ return filelist
405
+
406
+ @checklicense
407
+ def do_encrypt(argv):
408
+ '''Usage: pyarmor encrypt [OPTIONS] [File Patterns or @Filename]
409
+
410
+ Encrpty the files list in the command line, you can use a specified
411
+ pattern according to the rules used by the Unix shell. No tilde
412
+ expansion is done, but *, ?, and character ranges expressed with []
413
+ will be correctly matched.
414
+
415
+ You can either list file patterns in one file, one pattern one line,
416
+ then add a prefix '@' to the filename.
417
+
418
+ All the files will be encrypted and saved as orginal file name plus
419
+ 'e'. By default, the encrypted scripts and all the auxiliary files
420
+ used to run the encrypted scripts are save in the path "dist".
421
+
422
+ Available options:
423
+
424
+ -O, --output=DIR Output path for runtime files and encrypted
425
+ files (if no --in-place)
426
+
427
+ The default value is "build".
428
+
429
+ -C, --with-capsule=FILENAME Specify the filename of capsule generated
430
+ before.
431
+
432
+ The default value is "project.zip".
433
+
434
+ -i, --in-place [option], the encrypted scripts will be
435
+ saved in the original path (same as source).
436
+ Otherwise, save to --output specified.
437
+
438
+ -s, --src=DIR [option], the source path of python scripts.
439
+ The default value is current path.
440
+
441
+ -p, --plat-name [option] platform name to run encrypted
442
+ scripts. Only used when encrypted scripts
443
+ will be run in different platform.
444
+
445
+ -m, --main=NAME Generate wrapper file to run encrypted script
446
+
447
+ -e, --mode=MODE Encrypt mode, available value:
448
+ 0 Encrypt both source and bytecode
449
+ 1 Encrypt bytecode only.
450
+ 2 Encrypt source code only.
451
+ 3 Obfuscate bytecodes.
452
+ 5 Obfuscate code object of module.
453
+ 6 Combine mode 3 and 4
454
+ 7 Obfuscate code object of module,
455
+ output wrapper scripts
456
+ 8 Obfuscate both code object and bytecode,
457
+ output wrapper scripts
458
+ Mode 0, 1, 2 is deprecated from v3.2.0, this
459
+ option can be ignored in general.
460
+
461
+ -d, --clean Clean output path at start.
462
+
463
+ --manifest FILENAME Write file list to FILENAME
464
+
465
+ For examples:
466
+
467
+ - Encrypt a.py and b.py as a.pyx and b.pyx, saved in the path "dist":
468
+
469
+ pyarmor encrypt a.py b.py
470
+
471
+ - Use file pattern to specify files:
472
+
473
+ pyarmor encrypt a.py *.py src/*.py lib/*.pyc
474
+
475
+ - Save encrypted files in the directory "/tmp/build" other than "dist":
476
+
477
+ pyarmor encrypt --output=/tmp/build a.py
478
+
479
+ - Encrypt python scripts by project capsule "project.zip" in the
480
+ current directory:
481
+
482
+ pyarmor encrypt --with-capsule=project.zip src/*.py
483
+
484
+ - Encrypt python scripts to run in different platform:
485
+
486
+ pyarmor encrypt --plat-name=linux_x86_64 a.py b.py
487
+
488
+ Use MANIFEST.in to list files
489
+
490
+ pyarmor encrypt --with-capsule=project.zip @myproject/MANIFEST.in
491
+
492
+ It's Following the Distutils’ own manifest template
493
+
494
+ '''
495
+ opts, args = getopt.getopt(
496
+ argv, 'C:de:im:O:p:s:',
497
+ ['in-place', 'output=', 'src=', 'with-capsule=', 'plat-name=',
498
+ 'main=', 'clean', 'mode=', 'manifest=']
499
+ )
500
+
501
+ output = 'build'
502
+ srcpath = None
503
+ capsule = 'project.zip'
504
+ inplace = False
505
+ pname = None
506
+ extfile = None
507
+ mainname = []
508
+ clean = False
509
+ mode = 8
510
+ manifest = None
511
+
512
+ for o, a in opts:
513
+ if o in ('-O', '--output'):
514
+ output = a
515
+ elif o in ('-s', '--src'):
516
+ srcpath = a
517
+ elif o in ('-i', '--in-place'):
518
+ inplace = True
519
+ elif o in ('-C', '--with-capsule'):
520
+ capsule = a
521
+ elif o in ('-p', '--plat-name'):
522
+ pname = a
523
+ elif o in ('-d', '--clean'):
524
+ clean = True
525
+ elif o in ('-e', '--mode'):
526
+ if a not in ('0', '1', '2', '3', '5', '6',
527
+ '7', '8', '9', '10', '11', '12',
528
+ '13', '14'):
529
+ raise RuntimeError('Invalid mode "%s"' % a)
530
+ mode = int(a)
531
+ elif o in ('-m', '--main'):
532
+ mainname.append(a)
533
+ elif o in ('--manifest', ):
534
+ manifest = a
535
+
536
+ if srcpath is not None and not os.path.exists(srcpath):
537
+ raise RuntimeError('No found specified source path "%s"' % srcpath)
538
+
539
+ if capsule is None or not os.path.exists(capsule):
540
+ raise RuntimeError('No found capsule file %s' % capsule)
541
+
542
+ # Maybe user specify an empty path
543
+ if output == '':
544
+ output = 'build'
545
+
546
+ logging.info('Output path is %s' % output)
547
+ if os.path.exists(output) and clean:
548
+ logging.info('Removing output path %s', output)
549
+ shutil.rmtree(output)
550
+ logging.info('Remove output path OK.')
551
+ if not os.path.exists(output):
552
+ logging.info('Make output path %s', output)
553
+ os.makedirs(output)
554
+
555
+ if pname is None:
556
+ extfile = os.path.join(sys.rootdir, dll_name + dll_ext)
557
+ else:
558
+ logging.info("Cross publish, target platform is %s", pname)
559
+ name = dll_name + ('.so' if pname.startswith('linux') else '.dll')
560
+ extfile = os.path.join(sys.rootdir, 'platforms', pname, name)
561
+ if not os.path.exists(extfile):
562
+ # Need to download platforms/... from pyarmor homepage
563
+ logging.info('You need download prebuilt library files '
564
+ 'from pyarmor homepage first.')
565
+ raise RuntimeError('Missing cross platform library %s' % extfile)
566
+ logging.info('Copy %s to %s' % (extfile, output))
567
+ shutil.copy(extfile, output)
568
+
569
+ logging.info('Extract capsule %s ...', capsule)
570
+ ZipFile(capsule).extractall(path=output)
571
+ logging.info('Extract capsule to %s OK.', output)
572
+
573
+ # Fix bootstrap restrict issue from v5.7.0
574
+ make_license(capsule, os.path.join(output, 'license.lic'),
575
+ '*FLAGS:A*CODE:PyArmor')
576
+
577
+ if mode >= 3:
578
+ logging.info('Encrypt mode: %s', mode)
579
+ with open(os.path.join(output, 'pyimcore.py'), 'w') as f:
580
+ lines = 'from pytransform import old_init_runtime', \
581
+ 'old_init_runtime(0, 0, 0, 0)', ''
582
+ f.write('\n'.join(lines))
583
+ elif mode:
584
+ logging.info('Encrypt mode: %s', mode)
585
+ with open(os.path.join(output, 'pyimcore.py'), 'r') as f:
586
+ lines = f.read()
587
+ with open(os.path.join(output, 'pyimcore.py'), 'w') as f:
588
+ i = lines.rfind('\n\n')
589
+ if i == -1:
590
+ raise RuntimeError('Invalid pyimcore.py')
591
+ f.write(lines[:i])
592
+ if mode == 1:
593
+ f.write('\n\nold_init_runtime()\n')
594
+ elif mode == 2:
595
+ f.write('\n\nsys.meta_path.append(PyshieldImporter())\n'
596
+ 'old_init_runtime(0, 0, 0, 0)\n')
597
+
598
+ prikey = os.path.join(output, 'private.key')
599
+ if os.path.exists(prikey):
600
+ logging.info('Remove private key %s in the output', prikey)
601
+ os.remove(prikey)
602
+
603
+ if mode not in (7, 8, 9, 10, 11, 12, 13, 14):
604
+ for name in mainname:
605
+ n = name.find(':')
606
+ if n == -1:
607
+ script = os.path.join(output, name + '.py')
608
+ else:
609
+ script = os.path.join(output, name[n+1:])
610
+ name = name[:n]
611
+ logging.info('Writing script wrapper %s ...', script)
612
+ ch = 'c' if mode == 1 or mode == 3 else ext_char
613
+ with open(script, 'w') as f:
614
+ f.write(wrap_runner % (name + '.py' + ch))
615
+ logging.info('Write script wrapper OK.')
616
+
617
+ filelist = _parse_file_args(args, srcpath=srcpath)
618
+ if manifest is not None:
619
+ logging.info('Write filelist to %s', manifest)
620
+ with open(manifest, 'w') as fp:
621
+ fp.write('\n'.join([x[0] for x in filelist]))
622
+
623
+ if len(filelist[:1]) == 0:
624
+ logging.info('Generate extra files OK.')
625
+ else:
626
+ prokey = os.path.join(output, 'product.key')
627
+ if not os.path.exists(prokey):
628
+ raise RuntimeError('Missing project key %s' % prokey)
629
+ logging.info('Encrypt files ...')
630
+ encrypt_files(filelist, prokey, mode, None if inplace else output)
631
+ if mode in (7, 8, 9, 10, 11, 12, 13, 14):
632
+ for name in mainname:
633
+ script = os.path.join(
634
+ output, name + ('' if name.endswith('.py') else '.py'))
635
+ with open(script, 'r') as f:
636
+ source = f.read()
637
+ logging.info('Patch entry script %s.', script)
638
+ with open(script, 'w') as f:
639
+ f.write('import pyimcore\n')
640
+ f.write(source)
641
+ logging.info('Encrypt files OK.')
642
+
643
+ @checklicense
644
+ def do_license(argv):
645
+ '''
646
+ Usage: pyarmor license [Options] [CODE]
647
+
648
+ Generate a registration code for project capsule, save it to "license.txt"
649
+ by default.
650
+
651
+ Available options:
652
+
653
+ -O, --output=DIR Path used to save license file.
654
+
655
+ -B, --bind-disk="XX" [optional] Generate license file bind to
656
+ harddisk of one machine.
657
+
658
+ --bind-mac="XX:YY" [optional] Generate license file bind to
659
+ mac address of one machine.
660
+
661
+ --bind-ip="a.b.c.d" [optional] Generate license file bind to
662
+ ipv4 of one machine.
663
+
664
+ --bind-domain="domain" [optional] Generate license file bind to
665
+ domain of one machine.
666
+
667
+ -F, --bind-file=FILENAME [option] Generate license file bind to
668
+ fixed file, for example, ssh private key.
669
+
670
+ -e, --expired-date=YYYY-MM-NN [option] Generate expired license file.
671
+ It could be combined with "--bind"
672
+
673
+ -C, --with-capsule=FILENAME [required] Specify the filename of capsule
674
+ generated before.
675
+
676
+ For example,
677
+
678
+ - Generate a license file "license.lic" for project capsule "project.zip":
679
+
680
+ pyarmor license --wth-capsule=project.zip MYPROJECT-0001
681
+
682
+ - Generate a license file "license.lic" expired in 05/30/2015:
683
+
684
+ pyarmor license --wth-capsule=project.zip -e 2015-05-30 MYPROJECT-0001
685
+
686
+ - Generate a license file "license.lic" bind to machine whose harddisk's
687
+ serial number is "PBN2081SF3NJ5T":
688
+
689
+ pyarmor license --wth-capsule=project.zip --bind-disk PBN2081SF3NJ5T
690
+
691
+ - Generate a license file "license.lic" bind to ssh key file id_rsa:
692
+
693
+ pyarmor license --wth-capsule=project.zip \
694
+ --bind-file src/id_rsa ~/.ssh/my_id_rsa
695
+
696
+ File "src/id_rsa" is in the develop machine, pyarmor will read data
697
+ from this file when generating license file.
698
+
699
+ Argument "~/.ssh/id_rsa" means full path filename in target machine,
700
+ pyarmor will find this file as key file when decrypting python scripts.
701
+
702
+ You shuold copy "license.lic" to target machine, at the same time,
703
+ copy "src/id_rsa" to target machine as "~/.ssh/my_id_rsa"
704
+
705
+ '''
706
+ opts, args = getopt.getopt(
707
+ argv, 'B:C:e:F:O:',
708
+ ['bind-disk=', 'bind-mac=', 'bind-ip=', 'bind-domain=',
709
+ 'expired-date=', 'bind-file=', 'with-capsule=', 'output=']
710
+ )
711
+
712
+ filename = 'license.lic.txt'
713
+ bindfile = None
714
+ capsule = 'project.zip'
715
+ bindfileflag = False
716
+ binddisk = None
717
+ bindip = None
718
+ bindmac = None
719
+ binddomain = None
720
+ expired = None
721
+ for o, a in opts:
722
+ if o in ('-C', '--with-capsule'):
723
+ capsule = a
724
+ elif o in ('-B', '--bind-disk'):
725
+ binddisk = a
726
+ elif o in ('-B', '--bind-mac'):
727
+ bindmac = a
728
+ elif o in ('-B', '--bind-ip'):
729
+ bindip = a
730
+ elif o in ('-B', '--bind-domain'):
731
+ binddomain = a
732
+ elif o in ('-F', '--bind-file'):
733
+ bindfileflag = True
734
+ bindfile = a
735
+ elif o in ('-e', '--expired-date'):
736
+ expired = a
737
+ elif o in ('-O', '--output'):
738
+ if os.path.exists(a) and os.path.isdir(a):
739
+ filename = os.path.join(a, 'license.lic.txt')
740
+ else:
741
+ filename = a
742
+
743
+ if len(args) == 0:
744
+ key = 'POWERD-BY-PYARMOR'
745
+ else:
746
+ key = args[0]
747
+
748
+ if expired is None:
749
+ fmt = ''
750
+ else:
751
+ logging.info('License file expired at %s', expired)
752
+ fmt = '*TIME:%.0f\n' % time.mktime(time.strptime(expired, '%Y-%m-%d'))
753
+
754
+ # Fix bootstrap restrict issue from v5.7.0
755
+ if key.find('FLAGS') == -1:
756
+ fmt = '%s*FLAGS:A' % fmt
757
+
758
+ if binddisk:
759
+ logging.info('License file bind to harddisk "%s"', binddisk)
760
+ fmt = '%s*HARDDISK:%s' % (fmt, binddisk)
761
+
762
+ if bindmac:
763
+ logging.info('License file bind to mac addr "%s"', key)
764
+ fmt = '%s*IFMAC:%s' % (fmt, bindmac)
765
+
766
+ if bindip:
767
+ logging.info('License file bind to ip "%s"', key)
768
+ fmt = '%s*IFIPV4:%s' % (fmt, bindip)
769
+
770
+ if binddomain:
771
+ logging.info('License file bind to domain "%s"', key)
772
+ fmt = '%s*DOMAIN:%s' % (fmt, binddomain)
773
+
774
+ if bindfileflag:
775
+ if os.path.exists(bindfile):
776
+ logging.info('You need copy %s to target machine as %s '
777
+ 'with license file.', bindfile, key)
778
+ f = open(bindfile, 'rb')
779
+ s = f.read()
780
+ f.close()
781
+ if sys.version_info[0] == 3:
782
+ fmt = '%s*FIXKEY:%s;%s' % (fmt, key, s.decode())
783
+ else:
784
+ fmt = '%s*FIXKEY:%s;%s' % (fmt, key, s)
785
+ else:
786
+ raise RuntimeError('Bind file %s not found' % bindfile)
787
+
788
+ logging.info('Output filename is %s', filename)
789
+ make_license(capsule, filename, fmt if fmt else key)
790
+ logging.info('Generate license file "%s" OK.', filename)
791
+
792
+ if __name__ == '__main__':
793
+ sys.rootdir = os.path.dirname(os.path.abspath(sys.argv[0]))
794
+
795
+ logging.basicConfig(
796
+ level=logging.INFO,
797
+ format='%(levelname)-8s %(message)s',
798
+ # filename=os.path.join(sys.rootdir, 'pyarmor.log'),
799
+ # filemode='w',
800
+ )
801
+
802
+ # if (len(sys.argv) == 1 or
803
+ # sys.argv[1] not in ('help', 'encrypt', 'capsule', 'license')):
804
+ # from pyarmor import main as main2
805
+ # main2(sys.argv[1:])
806
+ # sys.exit(0)
807
+
808
+ if len(sys.argv) == 1:
809
+ usage()
810
+ sys.exit(0)
811
+
812
+ command = sys.argv[1]
813
+ if len(sys.argv) >= 3 and sys.argv[2] == 'help':
814
+ usage(command)
815
+ sys.exit(0)
816
+
817
+ pytransform = _import_pytransform()
818
+ if pytransform is None:
819
+ sys.exit(1)
820
+
821
+ if 'help'.startswith(command) or sys.argv[1].startswith('-h'):
822
+ try:
823
+ usage(sys.argv[2])
824
+ except IndexError:
825
+ usage()
826
+
827
+ elif 'version'.startswith(command) or sys.argv[1].startswith('-v'):
828
+ show_version_info()
829
+
830
+ elif 'capsule'.startswith(command):
831
+ do_capsule(sys.argv[2:])
832
+
833
+ elif 'encrypt'.startswith(command):
834
+ do_encrypt(sys.argv[2:])
835
+
836
+ elif 'license'.startswith(command):
837
+ do_license(sys.argv[2:])
838
+
839
+ elif 'hdinfo'.startswith(command):
840
+ show_hd_info()
841
+
842
+ else:
843
+ usage(command)