This is an experimental copy for testing Poikilos' issue mirroring system. Note that Gitea's migration tool can import issues, but the "Issues" checkbox is disabled when "This repository will be a mirror" is enabled (it is for this repo).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

658 lines
24 KiB

import os
import sys
import traceback
import copy
verbose_enable = False
os_name = "GNU/Linux"
if os.sep=="\\":
os_name = "windows"
print("Windows detected")
#TODO: deprecate os_name--still needed for detecting whether to use squote in singleimage.py (when not Windows, squote is "'" otherwise "")
#formerly pcttext:
#uppercase_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#lowercase_chars = uppercase_chars.lower()
#letter_chars = uppercase_chars+lowercase_chars
digit_chars = "0123456789"
#identifier_chars = letter_chars+"_"+digit_chars
#identifier_and_dot_chars = identifier_chars + "."
#formerly from expertmmregressionsuite:
alpha_upper_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
alpha_lower_chars = alpha_upper_chars.lower()
alpha_chars = alpha_upper_chars+alpha_lower_chars
#numeric_chars = "1234567890"
alnum_chars = alpha_chars+digit_chars
identifier_chars = alnum_chars+"_"
identifier_and_dot_chars = identifier_chars+"."
min_indent = ""
dict_entries_modified_count = 0
class InstalledFile:
source_dir_path = None
dest_dir_path = None
file_name = None
def __init__(self, file_name, source_dir_path, dest_dir_path):
self.file_name=file_name
self.source_dir_path=source_dir_path
self.dest_dir_path=dest_dir_path
class ConfigManager:
#config_name = None
_config_path = None
_data = None
_ao = None
#DOES load variables if path exists
def __init__(self, config_file_path, assignment_operator_string):
self._data = {}
self._config_path = config_file_path
self._ao = assignment_operator_string
self._data = get_dict_modified_by_conf_file(self._data, self._config_path, self._ao)
#DOES ask for user input if does not exist. If default_value is none, do not add to _data if not given
def load_var_or_ask_console_input(self, name, default_value, description):
is_changed = False
if name not in self._data:
print("")
if default_value is None:
print("WARNING: this program does not have a default value for "+name+".")
default_value = ""
answer = raw_input("Please enter "+description+" ("+name+") [blank for "+default_value+"]: ")
if answer is not None:
answer = answer.strip()
if answer is not None and len(answer)>0:
self._data[name] = answer
else:
self._data[name] = default_value
print("Using "+name+" '"+self._data[name]+"'")
is_changed = True
if not os.path.isfile(self._config_path):
is_changed = True
print("Creating '"+self._config_path+"'")
if is_changed:
self.save_yaml()
def prepare_var(self, name, default_value, description):
self.load_var_or_ask_console_input(name, default_value, description)
def contains(self, name):
return (name in self._data.keys())
def remove_var(self, name):
try:
del self._data[name]
self.save_yaml()
except KeyError:
pass
#DOES autosave IF different val
def set_var(self, name, val):
is_changed = False
if name not in self._data.keys():
print("WARNING to developer: run prepare_var before set_val, so that variable has a default.")
is_changed = True
elif self._data[name] != val:
is_changed = True
if is_changed:
self._data[name] = val
self.save_yaml()
def get_var(self, name):
result = None
if name in self._data:
result = self._data[name]
return result
def save_yaml(self):
save_conf_from_dict(self._config_path, self._data, self._ao, save_nulls_enable=False)
def get_dict_deepcopy(old_dict):
new_dict = None
if type(old_dict) is dict:
new_dict = {}
for this_key in old_dict.iterkeys():
new_dict[this_key] = copy.deepcopy(old_dict[this_key])
return new_dict
def is_dict_subset(new_dict, old_dict, verbose_messages_enable, verbose_dest_description="unknown file"):
is_changed = False
try:
if old_dict is not None:
if new_dict is not None:
old_dict_keys = old_dict.keys()
for this_key in new_dict.iterkeys():
if (this_key not in old_dict_keys):
is_changed = True
if verbose_messages_enable:
print("SAVING '"+verbose_dest_description+"' since "+str(this_key)+" not in saved version.")
break
elif new_dict[this_key] != old_dict[this_key]:
is_changed = True
if verbose_messages_enable:
print("SAVING '"+verbose_dest_description+"' since "+str(this_key)+" not same as saved version.")
break
#else new_dict is None so no change detected (no new information)
else:
if new_dict is not None:
is_changed = True
except:
print("Could not finish is_dict_subset:")
view_traceback()
return is_changed
def vec2_not_in(this_vec, this_list):
result = False
if this_list is not None and this_vec is not None:
for try_vec in this_list:
if try_vec[0]==this_vec[0] and try_vec[1]==this_vec[1]:
result = True
break
return result
def ivec2_equals(pos1, pos2):
return (int(pos1[0])==int(pos2[0])) and (int(pos1[1])==int(pos2[1]))
def get_dict_from_conf_file(path,assignment_operator="=",comment_delimiter="#",inline_comments_enable=False):
results = None
results = get_dict_modified_by_conf_file(results, path, assignment_operator, comment_delimiter=comment_delimiter, inline_comments_enable=inline_comments_enable)
return results
def RepresentsInt(s):
try:
int(s)
return True
except ValueError:
return False
def RepresentsFloat(s):
try:
float(s)
return True
except ValueError:
return False
def view_traceback():
ex_type, ex, tb = sys.exc_info()
print(min_indent+str(ex_type))
print(min_indent+str(ex))
traceback.print_tb(tb)
del tb
def print_file(path, min_indent=""):
line_count = 0
try:
if path is not None:
if os.path.isfile(path):
if min_indent is None:
min_indent = ""
ins = open(path, 'r')
line = True
while line:
line = ins.readline()
line_count += 1
if line:
print(min_indent+line)
ins.close()
#if line_count==0:
#print(min_indent+"print_file WARNING: "+str(line_count)+" line(s) in '"+path+"'")
#else:
#print(min_indent+"# "+str(line_count)+" line(s) in '"+path+"'")
else:
print (min_indent+"print_file: file does not exist")
else:
print (min_indent+"print_file: path is None")
except:
print(min_indent+"print_file: could not finish")
try:
ins.close()
except:
pass
return line_count
def singular_or_plural(singular, plural, count):
result = plural
if count==1:
result = singular
return str(count)+" "+result
def get_dict_entries_modified_count():
return dict_entries_modified_count
def get_dict_modified_by_conf_file(this_dict, path, assignment_operator="=", comment_delimiter="#", inline_comments_enable=False):
global dict_entries_modified_count
dict_entries_modified_count = 0
results = this_dict
#print ("Checking "+str(path)+" for settings...")
if (results is None) or (type(results) is not dict):
results = {}
if os.path.isfile(path):
ins = open(path, 'r')
line = True
while line:
line = ins.readline()
if line and len(line)>0:
line_strip=line.strip()
if len(line_strip)>0 and line_strip[0]!=comment_delimiter: # if not comment
if not line_strip[0]=="-": # ignore yaml arrays
if inline_comments_enable:
comment_index = line_strip.find(comment_delimiter)
ao_index = line_strip.find(assignment_operator)
if ao_index>=1: # intentionally skip zero-length variable names
if ao_index<len(line_strip)-1: # skip yaml implicit nulls or yaml objects
result_name = line_strip[:ao_index].strip()
result_value = line_strip[ao_index+1:].strip()
result_lower = result_value.lower()
if result_value=="None" or result_value=="null" or result_value=="~" or result_value=="NULL":
result_value = None
elif result_lower=="true":
result_value = True
elif result_lower=="false":
result_value = False
elif RepresentsInt(result_value):
result_value = int(result_value)
elif RepresentsFloat(result_value):
result_value = float(result_value)
#print (" CHECKING... "+result_name+":"+result_value)
if (result_name not in results) or (results[result_name]!=result_value):
dict_entries_modified_count += 1
#print(str(dict_entries_modified_count))
results[result_name]=result_value
ins.close()
return results
def save_conf_from_dict(path, this_dict, assignment_operator="=", save_nulls_enable=True):
try:
outs = open(path, 'w')
for this_key in this_dict.keys():
if save_nulls_enable or (this_dict[this_key] is not None):
if this_dict[this_key] is None:
outs.write(this_key+assignment_operator+"null\n")
else:
outs.write(this_key+assignment_operator+str(this_dict[this_key])+"\n")
outs.close()
except:
print("Could not finish saving chunk metadata to '"+str(path)+"': "+str(traceback.format_exc()))
try:
outs.close()
except:
pass
def get_list_from_hex(hex_string):
results = None
if hex_string is not None:
if len(hex_string)>=2:
if hex_string[:2]=="0x":
hex_string = hex_string[2:]
elif hex_string[:1]=="#":
hex_string = hex_string[1:]
if len(hex_string)>0 and hex_string[len(hex_string)-1:]=="h":
hex_string = hex_string[:len(hex_string)-1]
index = 0
while index<len(hex_string):
if results is None:
results = list()
if len(hex_string)-index >= 2:
results.append(int(hex_string[index:index+2], 16))
index += 2
return results
def get_tuple_from_notation(line, debug_src_name="<unknown object>"):
result = None
if line is not None:
# mark chunk
tuple_noparen_pos_string = line.strip("() \n\r")
pos_strings = tuple_noparen_pos_string.split(",")
if len(pos_strings) == 3:
try:
player_x = float(pos_strings[0])
player_y = float(pos_strings[1])
player_z = float(pos_strings[2])
except:
player_x = int(pos_strings[0])
player_y = int(pos_strings[1])
player_z = int(pos_strings[2])
result = player_x, player_y, player_z
else:
print("'"+debug_src_name+"' has bad position data--should be 3-length (x,y,z) in position value: "+str(pos_strings))
return result
def is_same_fvec3(list_a, list_b):
result = False
if list_a is not None and list_b is not None:
if len(list_a)>=3 and len(list_b)>=3:
result = (float(list_a[0]) == float(list_b[0])) and (float(list_a[1]) == float(list_b[1])) and (float(list_a[2]) == float(list_b[2]))
return False
def lastchar(val):
result = None
if (val is not None) and (len(val) > 0):
result = val[len(val)-1]
return result
def get_indent_string(line):
ender_index = find_any_not(line," \t")
result = ""
if ender_index > -1:
result = line[:ender_index]
return result
def is_identifier_valid(val, is_dot_allowed):
result = False
these_id_chars = identifier_chars
if is_dot_allowed:
these_id_chars = identifier_and_dot_chars
for index in range(0,len(val)):
if val[index] in these_id_chars:
result = True
else:
result = False
break
return result
#formerly get_params_len
def get_operation_chunk_len(val, start=0, step=1, line_counting_number=None):
result = 0
openers = "([{"
closers = ")]}"
quotes = "'\""
ender = len(val)
direction_msg = "after opening"
if step < 0:
tmp = openers
openers = closers
closers = tmp
ender = -1
direction_msg = "before closing"
opens = ""
closes = ""
index = start
in_quote = None
line_message = ""
if (line_counting_number is not None) and (line_counting_number>-1):
line_message = "line "+str(line_counting_number)+": "
while (step > 0 and index < ender) or (step < 0 and index > ender):
opener_number = openers.find(val[index])
closer_number = closers.find(val[index])
expected_closer = None
if (len(closes)>0):
expected_closer = lastchar(closes)
quote_number = quotes.find(val[index])
if (in_quote == None) and (opener_number > -1):
opens += openers[opener_number]
closes += closers[opener_number]
elif (in_quote == None) and (closer_number > -1):
if closers[closer_number] == expected_closer:
opens = opens[:len(opens)-1]
closes = closes[:len(closes)-1]
elif quote_number > -1:
if in_quote is None:
in_quote = val[index]
else:
if in_quote == val[index]:
if (index-1 == -1) or (val[index-1]!="\\"):
in_quote = None
index += step
result += 1
if (in_quote is None) and (len(opens)==0) and ((index>=len(val)) or (val[index] not in identifier_and_dot_chars)):
break
return result
def find_identifier(line, identifier_string, start=0):
result = -1
start_index = start
if (identifier_string is not None) and (len(identifier_string) > 0) and (line is not None) and (len(line) > 0):
while True:
try_index = find_unquoted_not_commented(line, identifier_string, start=start_index)
if (try_index > -1):
if ((try_index==0) or (line[try_index-1] not in identifier_chars)) and ((try_index+len(identifier_string)==len(line)) or (line[try_index+len(identifier_string)] not in identifier_chars)):
result = try_index
#input(identifier_string+"starts after '"+line[try_index]+"' ends before '"+line[try_index+len(identifier_string)]+"'")
break
else:
#match is part of a different identifier, so skip it
#input(identifier_string+" does not after '"+line[try_index]+"' ends before '"+line[try_index+len(identifier_string)]+"'")
start_index = try_index + len(identifier_string)
else:
break
return result
def get_newline_in_data(data):
newline = None
cr = "\r"
lf = "\n"
cr_index = -1
lf_index = -1
cr_index = data.find(cr)
lf_index = data.find(lf)
if (cr_index > -1) and (lf_index > -1):
if cr_index < lf_index:
newline = cr+lf
else:
newline = lf+cr
elif cr_index > -1:
newline = cr
elif lf_index > -1:
newline = lf
return newline
def re_escape_visible(val):
result = val.replace("\n","\\n").replace("\n","\\n")
return result
def get_newline(file_path):
data = None
with open (file_path, "r") as myfile:
data=myfile.read()
return get_newline_in_data(data)
def is_allowed_in_variable_name_char(one_char):
result = False
if len(one_char) == 1:
if one_char in identifier_chars:
result = True
else:
print("error in is_allowed_in_variable_name_char: one_char must be 1 character")
return result
def find_any_not(haystack, char_needles, start=None, step = 1):
result = -1
if (len(char_needles)>0) and (len(haystack)>0):
endbefore = len(haystack)
if start is None:
if step > 0:
start = 0
elif step < 0:
start = len(haystack)-1
if step < 0:
endbefore = -1
index = start
while (step>0 and index<endbefore) or (step<0 and index>endbefore):
if not haystack[index:index+1] in char_needles:
result = index
break
index += step
return result
def explode_unquoted(haystack, delimiter):
elements = list()
while True:
index = find_unquoted_not_commented(haystack, delimiter)
if index >= 0:
elements.append(haystack[:index])
haystack = haystack[index+1:]
else:
break
elements.append(haystack) #rest of haystack is the param after last comma, else beginning if none
return elements
#Finds needle in haystack where not quoted, taking into account escape
# sequence for single-quoted or double-quoted string inside haystack.
def find_unquoted_MAY_BE_COMMENTED(haystack, needle, start=0, endbefore=-1, step=1):
result = -1
prev_char = None
if (haystack is not None) and (needle is not None) and (len(needle)>0):
in_quote = None
if endbefore > len(haystack):
endbefore = len(haystack)
if endbefore<0:
endbefore = len(haystack)
index = start
if step<0:
index = endbefore - 1
if verbose_enable:
print(" find_unquoted_not_commented in "+haystack.strip()+":")
while (step>0 and index<=(endbefore-len(needle))) or (step<0 and (index>=0)):
this_char = haystack[index:index+1]
if verbose_enable:
print(" {"
+"index:"+str(index)+";"
+"this_char:"+str(this_char)+";"
+"in_quote:"+str(in_quote)+";"
+"}")
if in_quote is None:
if (this_char == '"') or (this_char == "'"):
in_quote = this_char
elif haystack[index:index+len(needle)] == needle:
result = index
break
else:
if (this_char == in_quote) and (prev_char != "\\"):
in_quote = None
elif haystack[index:index+len(needle)] == needle:
result = index
break
prev_char = this_char
index += step
return result
#DISCARDS whitespace, and never matches None to None
def find_dup(this_list, discard_whitespace_ignore_None_enable=True, ignore_list=None, ignore_numbers_enable=False):
result = -1
if type(this_list) is list:
for i1 in range(0,len(this_list)):
for i2 in range(0,len(this_list)):
i1_strip = None
i2_strip = None
if this_list[i1] is not None:
i1_strip = this_list[i1].strip()
if this_list[i2] is not None:
i2_strip = this_list[i2].strip()
if i1_strip!=None and len(i1_strip)>0 and i2_strip!=None and len(i2_strip)>0:
if (i1!=i2) and (ignore_list is None or i1_strip not in ignore_list) and i1_strip==i2_strip:
number1 = None
#number2 = None
if ignore_numbers_enable:
try:
number1 = int(i1_strip)
except:
try:
number1 = float(i1_strip)
except:
pass
#only need one since they already are known to match as text
#try:
#number2 = int(i2_strip)
#except:
#try:
#number2 = float(i2_strip)
#except:
#pass
if (ignore_numbers_enable and number1 is None) or ((not ignore_numbers_enable)):
result = i2
if verbose_enable:
print("["+str(i1)+"]:"+str(this_list[i1])+" matches ["+str(i2)+"]:"+str(this_list[i2]))
break
if result>-1:
break
else:
input("ERROR in has_dups: "+str(this_list)+" is not a list")
return result
def has_dups(this_list):
return find_dup(this_list)>-1
#region formerly pcttext.py
#gets first instance of name, gets its value, then stops reading file
def get_initial_value_from_conf(path, name, assignment_operator="="):
result = None
line_count = 0
if path is not None:
if os.path.isfile(path):
ins = open(path, 'r')
line = True
while line:
line = ins.readline()
line_count += 1
if line:
ao_i=line.find(assignment_operator)
if ao_i>0: # intentionall skip when 0
this_name = line[:ao_i].strip()
if this_name==name:
result = line[ao_i+1:].strip() #NOTE: blank is allowed
break
ins.close()
else:
print("ERROR in get_initial_value_from_conf: '"+str(path)+"' is not a file.")
else:
print("ERROR in get_initial_value_from_conf: path is None.")
return result
def find_unquoted_not_commented(haystack, needle, start=0, endbefore=-1, step=1, comment_delimiter="#"):
result = -1
prev_char = None
if (haystack is not None) and (needle is not None) and (len(needle)>0):
in_quote = None
if endbefore > len(haystack):
endbefore = len(haystack)
if endbefore<0:
endbefore = len(haystack)
index = start
if step<0:
index = endbefore - 1
if verbose_enable:
print(" find_unquoted_not_commented in "+haystack.strip()+":")
while (step>0 and index<=(endbefore-len(needle))) or (step<0 and (index>=0)):
this_char = haystack[index:index+1]
if verbose_enable:
print(" {"
+"index:"+str(index)+";"
+"this_char:"+str(this_char)+";"
+"in_quote:"+str(in_quote)+";"
+"}")
if in_quote is None:
if (this_char == comment_delimiter) or (haystack[index:index+3]=="\"\"\""):
break
elif (this_char == '"') or (this_char == "'"):
in_quote = this_char
elif haystack[index:index+len(needle)] == needle:
result = index
break
else:
if (this_char == in_quote) and (prev_char != "\\"):
in_quote = None
elif haystack[index:index+len(needle)] == needle:
result = index
break
prev_char = this_char
index += step
return result
#endregion formerly pcttext.py