import os import math class RichFile(file): # # A subclass of python's file type that has additional functionality # def readlines_non(self): # Returns a list of all the lines in the file WITHOUT the eol character self.seek(0) alist = [ line.rstrip('\n') for line in self ] return alist def write_n(self, text): # Writes text with end of line characters (\n). If text is a list, an eol is placed at the end of each list item. # Converts text to str before writing if isinstance(text, (list, tuple)): for item in text: self.write(str(item) + '\n') else: self.write(str(text) + '\n') def writetypes(self, *args, **keyw): # Writes out all the arguments on a single line regardless of what type they are # Use sep='x' to change the delimiter to 'x' # No eol character is added to the line sep = keyw.get('sep', ', ') astring = sep.join([str(x) for x in args]) self.write(astring) def writetypes_n(self, *args, **keyw): # Writes out all the arguments on a single line regardless of what type they are # Use sep='x' to change the delimiter to 'x' # Adds an eol character to the line self.writetypes(*args, **keyw) self.write('\n') def next_non(self): # This is an iterator: use in "for x in self.next_non():" or x = self.next_non() and get next line via x.next() # raises StopIteration when it hits the end of file # # Iterates over the lines of the file without the end of line characters for line in self: line = line.rstrip(os.linesep) yield line def next_split_line(self, splitter='\t'): # This is an iterator: use in "for x in self.next_split_line():" or x = self.next_split_line() and get next line via x.next() # raises StopIteration when it hits the end of file # # Iterates over the lines of the file without the end of line characters, and returns each line as a list of items split by # the splitter character (by default, the tab character) for line in self: # The fix below is for old dos files that used a '\r' as a linesep character line = line.rstrip('\r' + os.linesep) multi_lines = line.split('\r') for mline in multi_lines: if splitter: tokens = mline.split(splitter) else: tokens = mline.split() yield tokens class RichDict(dict): # # A subclass of the python dict type that has keeps the entries in the same order they are added. Can also return them as sorted by # some key instead of in order. # def __init__(self, *args, **kwargs): # Note - it is impossible to preserve the order of a set of data provided by a dictionary or by kwargs, as the order of # the keys is already randomized when we get our hands on it. Lists are OK. # RichDict({'a':1, 'd':2, 'b':3}) a, d, b order is not preserved # RichDict(a=1, d=2, b=3) a, d, b order is not preserved # RichDict([('a',1), ('d',2), ('b',3)]) a, d, b order IS preserved # RichDict(existing_richdict) preserves the order from existing_richdict self.ordered_key_list = [] dict.__init__(self) if args: self.update(*args) if kwargs: self.update(**kwargs) def __setitem__(self, key, value): # Items should always be added via this method dict.__setitem__(self, key, value) self.add_ordered_key(key) def __delitem__(self, key): # Items should always be deleted via this method dict.__delitem__(self, key) self.remove_ordered_key(key) def fromkeys(self, alist, value=None): for key in alist: self.__setitem__(key, value) def add_container(self, container): if isinstance(container, (list, tuple)): # container might be a list or a tuple for key, value in container: self.__setitem__(key, value) else: # container might be a RichDict or a dictionary if isinstance(container, RichDict): # Preserve the order if this is an ordered dictionary for key in container.ordered_keys(): self.__setitem__(key, container[key]) else: for key, value in container.iteritems(): self.__setitem__(key, value) def update(self, *arg, **kwarg): if arg: for container in arg: self.add_container(container) elif kwarg: # User might have supplied keyword arguments self.add_container(kwarg) def remove_ordered_key(self, key): try: if key in self.ordered_key_list: self.ordered_key_list.remove(key) except AttributeError: self.ordered_key_list = [] def add_ordered_key(self, key): try: if key not in self.ordered_key_list: self.ordered_key_list.append(key) except AttributeError: self.ordered_key_list = [key] def clear(self): self.ordered_key_list = [] dict.clear(self) def popitem(self): key, value = dict.popitem(self) self.remove_ordered_key(key) return key, value def pop(self, *args): value = dict.pop(self, *args) self.remove_ordered_key(args[0]) return value def setdefault(self, key, default=None): self.add_ordered_key(key) return dict.setdefault(self, key, default) def sorted_keys(self, **keyw): # Returns a list of sorted keys - accepts the same arguments as the list.sort() function does mylist = self.keys() mylist.sort(**keyw) return mylist def sorted_items(self, sort_item='keys', **keyw): # returns a list of items ((key, value) tuples) # if sort_item = 'keys', then the item list is sorted by keys (default) # if sort_item = 'values', then the item list is sorted by values # if sort_item = anything else, then the item list itself is sorted # The user may pass additional keyword arguments to the sort function def get_value(item): return item[1] if sort_item == 'keys': keylist = self.keys() keylist.sort(**keyw) itemlist = [ (x, self[x]) for x in keylist ] else: itemlist = self.items() if sort_item == 'values': itemlist.sort(key=get_value, **keyw) else: itemlist.sort(**keyw) return itemlist def sorted_items_by_value(self, **keyw): # returns a list of items ((key, value) tuples), the item list is sorted by values # The user may pass additional keyword arguments to the sort function return self.sorted_items(sort_item='values', **keyw) def sorted_values(self, **keyw): # Returns a list of sorted values - accepts the same arguments as the list.sort() function does valuelist = self.values() valuelist.sort(**keyw) return valuelist def ordered_keys(self): # Returns the list of keys in the order they were added return self.ordered_key_list def ordered_items(self): # Returns the list of items in the order they were added items = [ (x, self[x]) for x in self.ordered_key_list ] return items def ordered_values(self): # Returns the list of values in the order they were added items = [ self[x] for x in self.ordered_key_list ] return items def iter_ordered_keys(self): # Iterates on the ordered keylist for key in self.ordered_key_list: yield key return def iter_ordered_items(self): # Iterates on the ordered itemlist for key in self.ordered_key_list: yield (key, self[key]) return def iter_ordered_values(self): # Iterates on the ordered valuelist for key in self.ordered_key_list: yield self[key] return def copy(self): return RichDict(self) class RichList(list): # Adds some frequent functionality to lists def __init__(self, alist=[]): for aval in alist: self.append(aval) def sum(self): # Simple sum of the list - returns 0 if any of the items are not numerical data sum = 0 try: for data in self: sum = sum + data except TypeError: sum = 0 return sum def max_and_indmax(self): # Returns the maximum item and its index mymax = max(self) return mymax, self.index(mymax) def min_and_indmin(self): # Returns the minimum item and its index mymin = min(self) return mymin, self.index(mymin) def indmax(self): # Returns the index of the maximum item return self.index(max(self)) def indmin(self): # Returns the index of the minimum item return self.index(min(self)) def backwards(self, start=None): # An iterator that goes backwards through the list # Use such as "for value in myRichList.backwards(): # start can be which index to start counting backwards from (either + or - index works properly) # returns the next list item in the (backwards) series if len(self) == 0: return elif start == None: start = len(self) - 1 elif start < 0: start = len(self) - start for count in xrange(start, -1, -1): yield self[count] return def enumerate(self, start=0, stop=None, step=1): # Use such as "for ind, value in myRichList.enumerate(): # returns ind, v where # ind = the index of v and v = the next list item in the series # start can be which index to start counting backwards from (either + or - index works properly) # stop can be + or - and works like the ending range of any other range/xrange count # step is the step to increment the loop counter by if len(self) == 0: return if not stop: stop = len(self) if start < 0: start = len(self) + start if stop < 0: stop = len(self) + stop for count in xrange(start, stop, step): yield count, self[count] return def backwards_enumerate(self, start=None, stop=None, step=-1): # An iterator that goes backwards through the list, returning items and their index # Use such as "for ind, value in myRichList.backwards_enumerate(): # start can be which index to start counting backwards from (either + or - index works properly) # stop can be + or - and works like the ending range of any other range/xrange count # step is the step to increment the loop counter by # returns ind, v where # ind = the index of v and v = the next list item in the (backwards) series if len(self) == 0: return if start == None: start = len(self) - 1 elif start < 0: start = len(self) + start if stop == None: stop = -1 elif stop < 0: stop = len(self) + stop for count in xrange(start, stop, step): yield count, self[count] return def xrange(self): return xrange(len(self)) class NullClass(object): # This class does absolutely nothing, no matter what method you call on it # Note that if you ask for a PROPERTY of this class, you will get the do_nothing function, while will evaluate as True def __getattr__(self, attribute): return self.do_nothing def do_nothing(self, *args, **keyw): pass def distance(a, b): # Accepts and a and b as arbitrary-length lists or tuples and calculates the distance between them squares = RichList([ (a[x] - b[x])**2 for x in xrange(len(a)) ]) dist = math.sqrt(squares.sum()) return dist most_symbols='abKLMN-_+=OPQ#$RSVcdefp890!@%qrsyzABCDijkEFGHTUIJWXYZ123[}| ;:4567^&*ghlmno()<>,tuv]{wx./?' def confuse(astring, keystring, confuse=True): # converts astring to a nonsense string by simply converting each character to a new character based on the relative positions of the # old and new characters in a string of (most) accepted characters. The offset between old and new is set based on the length of # keystring. # # The offset is raised by one for each letter processed, so that the same letter doesn't give the same answer twice # # The string is also turned backwards just for a little more confusion. # # confuse = True: confuses the string # confuse = False: returns the original string (IF astring is the confused string, and keystring is the same length as the string # originally used to confuse astring!) offset = len(keystring) if not offset: offset = 10 if offset == 90: # We don't want a full cycle, as that would just return the same string offset = 10 length = len(most_symbols) if not confuse: offset = -offset - (len(astring)-1) length = -length newstring = "" for letter in astring: ind = most_symbols.find(letter) if ind >= 0: try: letter = most_symbols[ind + offset] except: letter = most_symbols[ind + offset - length] newstring = letter + newstring offset = offset + 1 return newstring def joiner(glue, *args, **keyw): # Works like the python built-in function join, but with the following differences/improvements # glue = character(s) to connect each item with # the items to be joined need not not be in a list, all the arguments are considered to be items to be joined # the items to be joined need not be strings, they will be converted to strings during this process # the items to be joined can be a mix of lists, tuples and other data. Any lists or tuples will be unrolled and each item # joined with the glue characters # if the keyword argument digits=x is included, any float value will be converted to a string via the strfl routine # joiner('#', 'a', 1, ('bob', 'joe'), 3.08746, digits=2) returns 'a#1#bob#joe#3.09' def converted(val): if not isinstance(val, str): if isinstance(val, float) and 'digits' in keyw: val = strfl(val, digits=keyw['digits']) else: val = str(val) return val alist = [] for val in args: if isinstance(val, (list, tuple)): alist = alist + [ converted(iterval) for iterval in val ] else: alist.append(converted(val)) return glue.join(alist) def strfl(afloat, digits=2, width=0, zero=False, left=False, space=False, sign=False): # Converts a float to a string: # digits = # of digits after the decimal # if digits < 0, then the conversion attempts to keep at least -digit significant figures # for example, digits=-2, then 5.3, 0.12, 0.0045, etc. # width = minimum width of the conversion # zero = zero-padded # left = left justified # space = space before positive numbers # sign = + before positive numbers def getdigits(val): numdigs = 0 if digits >= 0: numdigs = digits if digits < 0: try: amag = math.log10(abs(val)) except: amag = 0.0 mag = int(amag) if amag >= 0: numdigs = abs(min(mag + digits+1, 0)) if amag < 0: numdigs = abs(mag) - digits return numdigs azero = aleft = aspace = asign = "" if zero: azero = '0' if left: aleft = '-' if space: aspace = ' ' if sign: asign = '+' if isinstance(afloat, (list, tuple)): astring = [] for data in afloat: adig = getdigits(data) astring.append("".join(['%', azero, aleft, aspace, asign, str(width), '.', str(adig), 'f']) % data) else: adig = getdigits(afloat) astring = "".join(['%', azero, aleft, aspace, asign, str(width), '.', str(adig), 'f']) % afloat return astring def params_to_attributes(clobject, varlist, local_dict): # Call from the __init__ function of a class object by: # params_to_attributes(class_object, variable_name_list, locals()) # class_object = the 'self' that is being __init__'d # variable_name_list = list of strings that are the names of the parameters to __init__ that are to be converted to # attributes of class_object # locals() = builtin python function that returns the local namespace as a dictionary # Example: # class testparm: # def __init__(self, bobby, joe='a', sue='ink'): # varlist = ['bobby', 'joe', 'sue'] # params_to_attributes(self, varlist, locals()) # atest = testparm(6) # Yields: # atest.bobby = 6 (can be accessed from within the body of testparm as self.bobby) # atest.joe = 'a' (can be accessed from within the body of testparm as self.joe) # atest.sue = 'ink' (can be accessed from within the body of testparm as self.sue) for key in varlist: clobject.__dict__[key] = local_dict[key] # Although the routine below is clever, it is not reliable enough to use. In particular, it fails when being used with py2exe because # inspect fails with py2exe. Therefore, it is kept here but commented out. Best to use params_to_attributes instead. #def params_to_attrib2(*args, **keyw): ## Call from the __init__ function of a class object by: ## params_to_attrib2(variables) ## variables = variables that are to be converted to attributes of the class object ## if the class object refers to itself as a name other than 'self' (as in jimbo.variable to access the attribute ## 'variable' rather than self.variable), then that name should be supplied as a keyword parameter self='jimbo' ## Example: ## class testparm: ## def __init__(self, bobby, joe='a', sue='ink'): ## params_to_attrib2(bobby, joe, sue) ## atest = testparm(6) ## Yields: ## atest.bobby = 6 (can be accessed from within the body of testparm as self.bobby) ## atest.joe = 'a' (can be accessed from within the body of testparm as self.joe) ## atest.sue = 'ink' (can be accessed from within the body of testparm as self.sue) ## ## This routine does the same thing as params_to_attributes but has a simpler calling syntax # ## This gets stack information for the calling routine (stack()[0] is the stack information for this routine, etc.) #mycaller = inspect.stack()[1] # ## This gets the actual source code of the calling line so we can grab the variable names from it #calling_line = mycaller[4][0] # ## Now we parse out the variable names and clean up and spaces, etc. #index = calling_line.index('params_to_attrib2(') #arg_names_list = calling_line[index+18: calling_line.index(')', index)] #mynames = [] #for name in arg_names_list.split(','): #clean_name = name.strip() #if name.find('=') == -1: #mynames.append(clean_name) # ## mycaller[0] is the frame of the calling routine, f_locals is the dictionary of local variables for that frame #local_dict = mycaller[0].f_locals ## The class object itself is stored in the local_dict, usually under the name 'self', but the user can supply self='x' if the class ## object refers to itself as 'x' rather than 'self'. dict.get(x, y) returns y if x is not a key in the dictionary. #clobject = local_dict[keyw.get('self', 'self')] #for key in mynames: #clobject.__dict__[key] = local_dict[key] # These are the various text "special" characters which get replaced in HTML webchars = { '"' : ('"', '"'), \ "'" : ('&apos', ''', '%27'), \ "&" : ('&', '&'), \ "<" : ('<', '<'), \ ">" : ('>', '>'), \ " " : (' ', ' ', '%20')} def no_webchars(astring): # Remove things like '<' from HTML and replace them with their actual character for akey, avalue in webchars.items(): for count in range(len(avalue)): astring = astring.replace(avalue[count], akey) return astring def to_webchars(astring, type=0): # type = 0 for replacement with character references, 1 for replacement with numerical references for akey, avalue in webchars.items(): astring = astring.replace(akey, avalue[type]) return astring def readlines_noeol(afile): # Takes a file descriptor as input and returns a list of all the lines in the file WITHOUT the eol character alist = [] for line in afile: alist.append(line.rstrip('\n')) return alist def write_eol(afile, text): # Writes text to a file with end of line characters (\n). If text is a list, an eol is placed at the end of each list item. if isinstance(text, (list, tuple)): for item in joe: afile.write(item + '\n') else: afile.write(text + '\n') def combine_files(filename1, filename2, filename3): # Combines filename1 and filename2 into filename3 file1 = RichFile(filename1, 'r') file2 = RichFile(filename2, 'r') file3 = RichFile(filename3, 'w') for line in file1: file3.write(line) for line in file2: file3.write(line) file1.close() file2.close() file3.close() def frange(first, last, step=1.0, nsteps=None): # # An iterator that yields a range of floating point numbers. # first = the initial value to return # last = the final value to return # step = the increment between returned values # nsteps = the total number of points to return (including the first and last values) # # frange is inclusive in that it will return the last value (or some number close to it depending on rounding error) # if nsteps is given, it will override the value of step. # span = float(last - first) if nsteps != None: step = span/(nsteps-1) else: nsteps = int(span/step) + 1 value = first for count in range(nsteps): value = first + count * step yield value