<commit_before>"""\
Build the current project as a Factorio mod.

Not required, but handy for work on the mod. The basic process is this:

 1. If the git status isn't clean, throw the current stuff into the stash
 2. Get the current version from info.json for the zip name
 3. Zip the current working directory into name_version.zip, in the containing directory
 4. Unstash to restore the directory status. 
"""

import subprocess
import json
from zipfile import ZipFile, ZIP_DEFLATED
import os
import codecs
import sys

def git_is_clean():
	"""
	Return True if `git status` reports clean, otherwise False
	"""
	
	proc = subprocess.Popen(['git', 'status', '--porcelain'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	out, err = proc.communicate()
	success = proc.wait() == 0 # gets the return code, False on nonzero
	success = success and len(out) == 0 # false if any standard output
	success = success and len(err) == 0 # false if any standard error
	return success 

def stash():
	"""
	If the current working directory isn't clean as reported by `git status`, stash it and return True.
	
	Otherwise return False.
	"""
	need_unstash = not git_is_clean()
	if need_unstash:
		subprocess.check_call(['git', 'stash'])
		assert git_is_clean()
	
	return need_unstash
	
def unstash():
	subprocess.check_call(['git', 'stash', 'apply'])
	
def form_name(fn='info.json'):
	with file(fn, 'r') as fp:
		info = json.load(fp)
	return '_'.join([info['name'], info['version']])
	
def get_default_path():
	"""\
	Returns the system default path for Factorio mods. 
	
	Derived from <http://www.factorioforums.com/wiki/index.php?title=Modding_overview#Mods_directory>
	
	If it can't figure out the right place to go, defaults to the current working directory. 
	"""
	ret = None
	if sys.platform.startswith('win'):
		ret = '%appdata%\Factorio\mods'
	elif sys.platform.startwsith('linux'):
		ret = '~/.factorio/mods'
	elif sys.platform.startswith('darwin'):
		ret = '~/Library/Application Support/factorio/mods'
	else:
		ret = '.'
	return os.path.expanduser(os.path.expandvars(ret))
		
	
def makezip(name, destpath=None):
	if destpath is None:
		destpath = get_default_path()
	if not os.path.exists(destpath):
		print >> sys.stderr, "'{}' does not exist. Can't write data there!".format(destpath)
		return
	elif not os.path.isdir(destpath):
		print >> sys.stderr, "'{}' is not a directory. Can't write data there!".format(destpath)
		return
		
	path = os.path.join(destpath, name + '.zip')
	failed = None
	with file(path, 'wb') as fp:
		with ZipFile(fp, 'w', compression=ZIP_DEFLATED) as zip:
			for dirpath, dirnames, filenames in os.walk('.'):
				# don't add .git to the packed archive
				if '.git' in dirnames:
					dirnames.remove('.git')
				# don't add .gitignore to the packed archive
				# also don't add the current zipfile, if we happen to be working in the CWD
				ignorefiles = ['.gitignore', name + '.zip']
				for ifn in ignorefiles:
					if ifn in filenames:
						filenames.remove(ifn)
				
				for fn in filenames:
					filepath = os.path.join(dirpath, fn)
					# documentation here: http://www.factorioforums.com/wiki/index.php?title=Modding_overview
					# lies about required structure. It says it must be in a folder inside the zip file, but
					# that causes an error on game load.
					
					# zippath  = os.path.join(name, filepath)

					try:
						# zipfile requires that file names be encoded in cp437. 
						filepath = codecs.encode(filepath, 'cp437') 
						# zippath = codecs.encode(zippath, 'cp437') 
					except ValueError, e:
						print >> sys.stderr, "Failed to encode", name
						print >> sys.stderr, e
						failed = e
						break;
						
					# actually add the item to the archive
					zip.write(filepath)
	
	if failed is None:
		with file(path, 'rb') as fp:
			with ZipFile(fp, 'r') as zip:
				failed = zip.testzip()
	
	if failed is not None:
		# we had a catastrophic error and don't want to leave a partial file hanging around
		os.remove(path)
		print >> sys.stderr, "Failed to write", path
	else:
		print "Successfully wrote and tested", path

if __name__ == '__main__':
	import argparse
	
	desc = __doc__.strip()
	
	parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawDescriptionHelpFormatter)
	
	parser.add_argument('-o', '--output-directory', metavar="OD", dest='od',
	                    help="specify a directory in which to place the output zip")<commit_msg>fix error when factorio tries to load the zip file.<commit_after>"""\
Build the current project as a Factorio mod.

Not required, but handy for work on the mod. The basic process is this:

 1. If the git status isn't clean, throw the current stuff into the stash
 2. Get the current version from info.json for the zip name
 3. Zip the current working directory into name_version.zip, in the containing directory
 4. Unstash to restore the directory status. 
"""

import subprocess
import json
from zipfile import ZipFile, ZIP_DEFLATED
import os
import codecs
import sys

def git_is_clean():
	"""
	Return True if `git status` reports clean, otherwise False
	"""
	
	proc = subprocess.Popen(['git', 'status', '--porcelain'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	out, err = proc.communicate()
	success = proc.wait() == 0 # gets the return code, False on nonzero
	success = success and len(out) == 0 # false if any standard output
	success = success and len(err) == 0 # false if any standard error
	return success 

def stash():
	"""
	If the current working directory isn't clean as reported by `git status`, stash it and return True.
	
	Otherwise return False.
	"""
	need_unstash = not git_is_clean()
	if need_unstash:
		subprocess.check_call(['git', 'stash'])
		assert git_is_clean()
	
	return need_unstash
	
def unstash():
	subprocess.check_call(['git', 'stash', 'apply'])
	
def form_name(fn='info.json'):
	with file(fn, 'r') as fp:
		info = json.load(fp)
	return '_'.join([info['name'], info['version']])
	
def get_default_path():
	"""\
	Returns the system default path for Factorio mods. 
	
	Derived from <http://www.factorioforums.com/wiki/index.php?title=Modding_overview#Mods_directory>
	
	If it can't figure out the right place to go, defaults to the current working directory. 
	"""
	ret = None
	if sys.platform.startswith('win'):
		ret = '%appdata%\Factorio\mods'
	elif sys.platform.startwsith('linux'):
		ret = '~/.factorio/mods'
	elif sys.platform.startswith('darwin'):
		ret = '~/Library/Application Support/factorio/mods'
	else:
		ret = '.'
	return os.path.expanduser(os.path.expandvars(ret))
		
	
def makezip(name, destpath=None):
	if destpath is None:
		destpath = get_default_path()
	if not os.path.exists(destpath):
		print >> sys.stderr, "'{}' does not exist. Can't write data there!".format(destpath)
		return
	elif not os.path.isdir(destpath):
		print >> sys.stderr, "'{}' is not a directory. Can't write data there!".format(destpath)
		return
		
	path = os.path.join(destpath, name + '.zip')
	failed = None
	with file(path, 'wb') as fp:
		with ZipFile(fp, 'w', compression=ZIP_DEFLATED) as zip:
			for dirpath, dirnames, filenames in os.walk('.'):
				# don't add .git to the packed archive
				if '.git' in dirnames:
					dirnames.remove('.git')
				# don't add .gitignore to the packed archive
				# also don't add the current zipfile, if we happen to be working in the CWD
				ignorefiles = ['.gitignore', name + '.zip']
				for ifn in ignorefiles:
					if ifn in filenames:
						filenames.remove(ifn)
				
				for fn in filenames:
					filepath = os.path.join(dirpath, fn)
					# documentation here: http://www.factorioforums.com/wiki/index.php?title=Modding_overview
					
					zippath  = os.path.join(name, filepath)

					try:
						# zipfile requires that file names be encoded in cp437. 
						filepath = codecs.encode(filepath, 'cp437') 
						zippath = codecs.encode(zippath, 'cp437') 
					except ValueError, e:
						print >> sys.stderr, "Failed to encode", name
						print >> sys.stderr, e
						failed = e
						break;
						
					# actually add the item to the archive
					zip.write(filepath, zippath)
	
	if failed is None:
		with file(path, 'rb') as fp:
			with ZipFile(fp, 'r') as zip:
				failed = zip.testzip()
	
	if failed is not None:
		# we had a catastrophic error and don't want to leave a partial file hanging around
		os.remove(path)
		print >> sys.stderr, "Failed to write", path
	else:
		print "Successfully wrote and tested", path

if __name__ == '__main__':
	import argparse
	
	desc = __doc__.strip()
	
	parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawDescriptionHelpFormatter)
	
	parser.add_argument('-o', '--output-directory', metavar="OD", dest='od',
	                    help="specify a directory in which to place the output zip")
{'fraction_non_alphanumeric': 0.11618873529075746, 'fraction_numerical': 0.0036558894093453673, 'mean_word_length': 3.6025236593059935, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1927635', 'n_tokens_mistral': 3185, 'n_tokens_neox': 2771, 'n_words': 961}