Skip to content

Instantly share code, notes, and snippets.

@csm10495
Last active March 22, 2017 05:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save csm10495/b912286f477211fe6b3862b331fe97bc to your computer and use it in GitHub Desktop.
Save csm10495/b912286f477211fe6b3862b331fe97bc to your computer and use it in GitHub Desktop.
A Python based folder extractor. Save a folder to a .py or .exe, use the result to extract anywhere, even if you don't have Python!
"""
Brief:
This script makes a self-extracting .py/,exe of a given folder
Author:
Charles Machalow
"""
import argparse
import base64
import os
import subprocess
import sys
import shutil
FILE_DIR = os.path.dirname(os.path.abspath(__file__))
# Get correct executable extension per the OS
if os.name == 'nt':
EXE = '.exe'
else:
EXE = ''
selfExtractScript=\
"""binaryData=b''''''
'''
Brief:
This binary file was created by the selfExtractorMakerPyinstaller.py script.
When run, it will extract the enclosed zip file to a given location.
If no location is given as a parameter, uses the current working directory.
Author:
Charles Machalow
'''
import zipfile
import sys
import os
import base64
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Creates a self extracting exe via Pyinstaller")
parser.add_argument("-e", "--extraction_directory", help="Location to extract contents to")
args = parser.parse_args()
if not args.extraction_directory:
args.extraction_directory = os.getcwd()
print ("Decoding the Base64 Compressed Format...")
# todo: come up with piecewise way to create b64 since this won't fit in RAM always.
# probably write binaryData to a file then decode the file itself then unzip, etc.
b64 = base64.b64decode(binaryData)
print ("Completed Decoding")
print ("Writing Temp Zip File...")
with open('tmp2.zip', 'wb') as f:
f.write(b64)
print ("Completed Writing Zip File")
print ("Getting a File Pointer...")
with open('tmp2.zip', 'rb') as f:
z = zipfile.ZipFile(f)
print ("Extracting to %s" % args.extraction_directory)
z.extractall(args.extraction_directory)
print ("Closing the File Pointer")
os.remove('tmp2.zip')
print ("Deleting Temp Zip File")
print ("Done!")
"""
class CwdRecursionError(Exception):
pass
def checkForCwdRecursionError(folderToLookFor):
folderToLookFor = os.path.abspath(folderToLookFor)
cwd = os.path.abspath(os.getcwd())
for root, dirs, files in os.walk(folderToLookFor):
for name in dirs:
testDirectory = os.path.join(root, name)
if os.path.abspath(testDirectory) == cwd:
raise CwdRecursionError(("The given path (%s) to turn into an extractable file includes where" +
" the zip would go! This would lead to an infinite loop!") % folderToLookFor)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Creates a self extracting exe via Pyinstaller")
parser.add_argument("-f", "--folder", required=True, help="Choose folder to grab")
parser.add_argument("-p", "--pyinstaller", action='store_true', default=False, help="Choose folder to grab")
parser.add_argument("-n", "--name", help="Output file base name")
args = parser.parse_args()
directory = args.folder
if not os.path.isdir(directory):
print ('%s is not a valid directory' % directory)
sys.exit(1)
checkForCwdRecursionError(directory) # Will throw on infinite recursion error
if not args.name:
args.name = "autoExtract"
print ("Making Temp Zip...")
shutil.make_archive('tmp','zip', directory)
print ("Creating base64 encode of data...")
# Create base64 of file
with open('tmp.zip', 'rb') as tz:
with open('tmp.base64', 'wb') as t64:
base64.encode(tz, t64)
# At this point we have tmp.base64 which is the base64 of the raw data
with open('%s.py' % args.name, 'w') as f:
afterZip = selfExtractScript[15:]
beforeZip = selfExtractScript[:15]
print ("Writing Python header...")
f.write(beforeZip)
print ("Beginning piecewise write of base64 data...")
with open('tmp.base64', 'r') as t64:
# piecewise write to not have all in RAM at once
while True:
rawBase64Data = str(t64.read(8092)).replace("\r\n", "").replace("\n", "")
if len(rawBase64Data) == 0:
break
f.write(rawBase64Data)
print ("Completed piecewise write of base64 data...")
print ("Writing Python completer...")
f.write(afterZip)
if args.pyinstaller:
delPyFile = True
print ("Beginning pyinstaller build...")
try:
try:
output = subprocess.check_output("pyinstaller %s.py --onefile --log-level=ERROR" % args.name, shell=True, stderr=subprocess.STDOUT).decode()
except Exception as ex:
output = ex.output.decode()
print ("Failed to use pyinstaller to create the exe")
if 'MemoryError' in output:
raise MemoryError("The pyinstaller system call led to a MemoryError")
else:
sys.exit(1)
print ("Pyinstaller build completed successfully")
shutil.copy(FILE_DIR + '/dist/%s%s' % (args.name, EXE), FILE_DIR + '/%s%s' % (args.name, EXE))
except MemoryError as ex:
print ("E" * 60)
print ("Pyinstaller ran into a MemoryError: %s" % str(ex))
print ("It's quite possible that this file is too large for Pyinstaller to turn into an exe!")
print ("Will leave behind the .py file as opposed to deleting since we couldn't generate an exe.")
print ("E" * 60)
delPyFile = False
finally:
# Cleanup
print ("Cleaning up after pyinstaller")
shutil.rmtree(FILE_DIR + '/dist/')
shutil.rmtree(FILE_DIR + '/build/')
os.remove('%s.spec' % args.name)
if delPyFile:
os.remove('%s.py' % args.name)
print ("Cleaning up tmps...")
os.remove('tmp.zip')
os.remove("tmp.base64")
print ("Done!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment