Skip to content

Commit 0995012

Browse files
committed
fixed issues 16 22 24
1 parent f76e005 commit 0995012

File tree

5 files changed

+89
-86
lines changed

5 files changed

+89
-86
lines changed

README.rst

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ Bugs fixed:
2525

2626
What's new:
2727

28+
- possibiliy to specify input dir, output dir and config file documented
29+
- skip_path_fragmens fixed
30+
- explanatory comments in config file made more clear
31+
- reasonable defaults provided for all configuration settings
32+
- -h an --help added in addition to ?
2833
- pep8_comments option added
2934
- support for obfuscation of names starting with __ added
3035
- license changed from QQuickLicense to Apache 2.0

opy/opy.py

+59-64
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
# =========== Initialize constants
3030

3131
programName = 'opy'
32-
programVersion = '1.1.26'
32+
programVersion = '1.1.27'
3333

3434
if __name__ == '__main__':
3535
print ('{} (TM) Configurable Multi Module Python Obfuscator Version {}'.format (programName.capitalize (), programVersion))
@@ -105,67 +105,62 @@ def unScramble{0} (keyedStringLiteral):
105105

106106
def printHelpAndExit (errorLevel):
107107
print (r'''
108-
109-
*******************************************************************************
108+
===============================================================================
110109
{0} will obfuscate your extensive, real world, multi module Python source code for free!
111110
And YOU choose per project what to obfuscate and what not, by editting the config file.
112111
113-
BACKUP YOUR CODE AND VALUABLE DATA TO AN OFF-LINE MEDIUM FIRST TO PREVENT ACCIDENTAL LOSS OF WORK!!!
114-
112+
- BACKUP YOUR CODE AND VALUABLE DATA TO AN OFF-LINE MEDIUM FIRST TO PREVENT ACCIDENTAL LOSS OF WORK!!!
115113
Then copy the default config file to the source top directory <topdir> and run {0} from there.
116114
It will generate an obfuscation directory <topdir>/../<topdir>_{1}
117115
118-
At first some identifiers may be obfuscated that shouldn't be, e.g. some of those imported from external modules.
116+
- At first some identifiers may be obfuscated that shouldn't be, e.g. some of those imported from external modules.
119117
Adapt your config file to avoid this, e.g. by adding external module names that will be recursively scanned for identifiers.
120118
You may also exclude certain words or files in your project from obfuscation explicitly.
121-
Source directory, obfuscation directory and config file path can also be supplied as command line parameters in that order.
122-
Comments and string literals can be marked as plain, bypassing obfuscation
119+
120+
- Source directory, obfuscation directory and config file path can also be supplied as command line parameters.
121+
The config file path should be something like C:/config_files/opy.cnf, so including the file name and extension.
122+
opy [<source directory> [<target directory> [<config file path>]]]
123+
124+
- Comments and string literals can be marked as plain, bypassing obfuscation
125+
Be sure to take a look at the comments in the config file opy_config.txt to discover all features.
123126
124127
Known limitations:
125128
126-
A comment after a string literal should be preceded by whitespace
127-
A ' or " inside a string literal should be escaped with \ rather then doubled
128-
If the pep8_comments option is False (the default), a {2} in a string literal can only be used at the start, so use 'p''{2}''r' rather than 'p{2}r'
129-
If the pep8_comments option is set to True, however, only a <blank><blank>{2}<blank> cannot be used in the middle or at the end of a string literal
130-
Obfuscation of string literals is unsuitable for sensitive information since it can be trivially broken
131-
No renaming backdoor support for methods starting with __ (non-overridable methods, also known as private methods)
129+
- A comment after a string literal should be preceded by whitespace
130+
- A ' or " inside a string literal should be escaped with \ rather then doubled
131+
- If the pep8_comments option is False (the default), a {2} in a string literal can only be used at the start, so use 'p''{2}''r' rather than 'p{2}r'
132+
- If the pep8_comments option is set to True, however, only a <blank><blank>{2}<blank> cannot be used in the middle or at the end of a string literal
133+
- Obfuscation of string literals is unsuitable for sensitive information since it can be trivially broken
134+
- No renaming backdoor support for methods starting with __ (non-overridable methods, also known as private methods)
132135
133136
Licence:
134137
{3}
135-
*******************************************************************************
138+
===============================================================================
139+
136140
'''.format (programName.capitalize (), programName, r'#', license))
137141
exit (errorLevel)
138142

139143
# ============ Assign directories ============
140144

141145
if len (sys.argv) > 1:
142-
if '?' in sys.argv [1]:
143-
printHelpAndExit (0)
144-
sourceRootDirectory = sys.argv [1]
146+
for switch in '?', '-h', '--help':
147+
if switch in sys.argv [1]:
148+
printHelpAndExit (0)
149+
sourceRootDirectory = sys.argv [1] .replace ('\\', '/')
145150
else:
146151
sourceRootDirectory = os.getcwd () .replace ('\\', '/')
147152

148153
if len (sys.argv) > 2:
149-
targetRootDirectory = sys.argv [2]
154+
targetRootDirectory = sys.argv [2] .replace ('\\', '/')
150155
else:
151156
targetRootDirectory = '{0}/{1}_{2}'.format (* (sourceRootDirectory.rsplit ('/', 1) + [programName]))
152157

153158
if len (sys.argv) > 3:
154-
configFilePath = sys.argv [3]
159+
configFilePath = sys.argv [3] .replace ('\\', '/')
155160
else:
156161
configFilePath = '{0}/{1}_config.txt'.format (sourceRootDirectory, programName)
157-
162+
158163
# =========== Read config file
159-
160-
# Default values for config items to preserve backward compatibility if items are added
161-
obfuscate_strings = False
162-
obfuscated_name_tail = '_{}_'.format (programName)
163-
plain_marker = '_{}_'.format (programName)
164-
source_extensions = ''
165-
skip_extensions = ''
166-
external_modules = ''
167-
plain_files = ''
168-
plain_names = ''
169164

170165
try:
171166
configFile = open (configFilePath)
@@ -175,45 +170,40 @@ def printHelpAndExit (errorLevel):
175170

176171
exec (configFile.read ())
177172
configFile.close ()
178-
179-
try:
180-
obfuscateStrings = obfuscate_strings
181-
except:
182-
obfuscateStrings = False
183-
184-
try:
185-
asciiStrings = ascii_strings
186-
except:
187-
asciiStrings = False
188-
189-
try:
190-
obfuscatedNameTail = obfuscated_name_tail
191-
except:
192-
obfuscatedNameTail = ''
193-
194-
try:
195-
plainMarker = plain_marker
196-
except:
197-
plainMarker = '_{0}_'.format (programName)
198-
199-
try:
200-
pep8Comments = pep8_comments
201-
except:
202-
pep8Comments = True
203-
204-
sourceFileNameExtensionList = source_extensions.split ()
205-
skipFileNameExtensionList = skip_extensions.split ()
206-
externalModuleNameList = external_modules.split ()
207-
plainFileRelPathList = plain_files.split ()
208-
extraPlainWordList = plain_names.split ()
173+
174+
def getConfig (parameter, default):
175+
try:
176+
return eval (parameter)
177+
except:
178+
return default
179+
180+
obfuscateStrings = getConfig ('obfuscate_strings', False)
181+
asciiStrings = getConfig ('ascii_strings', False)
182+
obfuscatedNameTail = getConfig ('obfuscated_name_tail', '_{}_')
183+
plainMarker = getConfig ('plain_marker', '_{}_'.format (programName))
184+
pep8Comments = getConfig ('pep8_comments', True)
185+
sourceFileNameExtensionList = getConfig ('source_extensions.split ()', ['py', 'pyx'])
186+
skipFileNameExtensionList = getConfig ('skip_extensions.split ()', ['pyc'])
187+
skipPathFragmentList = getConfig ('skip_path_fragments.split ()', [])
188+
externalModuleNameList = getConfig ('external_modules.split ()', [])
189+
plainFileRelPathList = getConfig ('plain_files.split ()', [])
190+
extraPlainWordList = getConfig ('plain_names.split ()', [])
209191

210192
# ============ Gather source file names
211193

212-
sourceFilePathList = [
194+
rawSourceFilePathList = [
213195
'{0}/{1}'.format (directory.replace ('\\', '/'), fileName)
214196
for directory, subDirectories, fileNames in os.walk (sourceRootDirectory)
215197
for fileName in fileNames
216198
]
199+
200+
def hasSkipPathFragment (sourceFilePath):
201+
for skipPathFragment in skipPathFragmentList:
202+
if skipPathFragment in sourceFilePath:
203+
return True
204+
return False
205+
206+
sourceFilePathList = [sourceFilePath for sourceFilePath in rawSourceFilePathList if not hasSkipPathFragment (sourceFilePath)]
217207

218208
# =========== Define comment swapping tools
219209

@@ -315,7 +305,12 @@ def moveFromFuture (matchObject):
315305

316306
skipWordSet = set (keyword.kwlist + ['__init__'] + extraPlainWordList) # __init__ should be in, since __init__.py is special
317307

318-
plainFilePathList = ['{0}/{1}'.format (sourceRootDirectory, plainFileRelPath) for plainFileRelPath in plainFileRelPathList]
308+
rawPlainFilePathList = ['{0}/{1}'.format (sourceRootDirectory, plainFileRelPath) for plainFileRelPath in plainFileRelPathList]
309+
310+
# Prevent e.g. attempt to open opy_config.txt if it is in a different location but still listed under plain_files
311+
312+
plainFilePathList = [plainFilePath for plainFilePath in rawPlainFilePathList if os.path.exists (plainFilePath)]
313+
319314
for plainFilePath in plainFilePathList:
320315
plainFile = open (plainFilePath)
321316
content = plainFile.read ()

opy/opy_config.txt

+4-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pyc
5858
#====================================================================================================
5959

6060
skip_path_fragments = '''
61-
/test_skip_path_fragments/ # This fragment included for test purposes only
61+
test_dummy
6262
'''
6363

6464

@@ -87,6 +87,7 @@ shutil
8787
# Relative path + name of Python source files containing identifiers that should not be obfuscated
8888
# Typically these are human readable settings files loaded and exec'ed at runtime by your program
8989
# Do not use this facility for files that are imported, that's what external_modules is for
90+
# Also don't use it to keep the original file name for your main module, that what plain_names is for
9091
#====================================================================================================
9192

9293
plain_files = '''
@@ -98,9 +99,10 @@ opy_config.txt
9899
#====================================================================================================
99100
# Extra identifiers and module names (as opposed to contents) that should not be obfuscated
100101
# Probably at least the names of your main modules (so not their filenames) go here
102+
# If e.g. your main module is in my_main_module.py, include my_main_module in plain_names
101103
#====================================================================================================
102104

103105
plain_names = '''
104106
opy
105-
currentModule
107+
poly_walker_test
106108
'''
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import random # One of Python's many standard modules
2-
3-
import bosses
4-
import dogs
5-
6-
# Create a list of random bosses
7-
humanBeings = [] # Create an emptpy list
8-
for index in range (10): # Repeat the following 10 times, index running from 0 to 9
9-
humanBeings.append ( # Append a random HumanBeing to the list by
10-
random.choice ((bosses.NatureLover, bosses.CouchPotato)) () # randomly selecting its class
11-
) # and calling its contructor
12-
13-
# Let them all walk a new dog with an random sound
14-
for humanBeing in humanBeings: # Repeat the following for every humanBeing in the list
15-
humanBeing.walk ( # Call implementation of walk method for that type of humanBeing
16-
dogs.Dog ( # Construct a new dog as parameter to the walk method
17-
random.choice ( # Pick a random sound
18-
('Wraff', 'Wooff', 'Howl', 'Kaii', 'Shreek') # fom this tuple of sounds
19-
)
20-
)
1+
import random # One of Python's many standard modules
2+
3+
import bosses
4+
import dogs
5+
6+
# Create a list of random bosses
7+
humanBeings = [] # Create an emptpy list
8+
for index in range (10): # Repeat the following 10 times, index running from 0 to 9
9+
humanBeings.append ( # Append a random HumanBeing to the list by
10+
random.choice ((bosses.NatureLover, bosses.CouchPotato)) () # randomly selecting its class
11+
) # and calling its contructor
12+
13+
# Let them all walk a new dog with an random sound
14+
for humanBeing in humanBeings: # Repeat the following for every humanBeing in the list
15+
humanBeing.walk ( # Call implementation of walk method for that type of humanBeing
16+
dogs.Dog ( # Construct a new dog as parameter to the walk method
17+
random.choice ( # Pick a random sound
18+
('Wraff', 'Wooff', 'Howl', 'Kaii', 'Shreek') # fom this tuple of sounds
19+
)
20+
)
2121
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
print ('Never here')

0 commit comments

Comments
 (0)