From 56f525e2d01cb9e01d8a5d38130729c0a2072cd1 Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Thu, 9 Aug 2012 11:00:03 +0800 Subject: [PATCH] Automatic commit. Thu Aug 9 11:00:03 WST 2012 Q: What do you call a WASP who doesn't work for his father, isn't a lawyer, and believes in social causes? A: A failure. --- research/TCS/2012-08-08/checklist | 19 ++ research/TCS/2012-08-09/105433.dat | 46 ++++ research/TCS/2012-08-09/105927.dat | 40 ++++ research/TCS/2012-08-09/105938.dat | 41 ++++ research/TCS/2012-08-09/checklist | 23 ++ research/TCS/2012-08-09/checklist.old | 26 ++ research/TCS/interface.py | 121 +++++++--- research/TCS/interface.pyc | Bin 0 -> 16493 bytes research/TCS/odict.py | 328 ++++++++++++++++++++++++++ research/TCS/odict.pyc | Bin 0 -> 14412 bytes 10 files changed, 605 insertions(+), 39 deletions(-) create mode 100644 research/TCS/2012-08-08/checklist create mode 100644 research/TCS/2012-08-09/105433.dat create mode 100644 research/TCS/2012-08-09/105927.dat create mode 100644 research/TCS/2012-08-09/105938.dat create mode 100644 research/TCS/2012-08-09/checklist create mode 100644 research/TCS/2012-08-09/checklist.old create mode 100644 research/TCS/interface.pyc create mode 100644 research/TCS/odict.py create mode 100644 research/TCS/odict.pyc diff --git a/research/TCS/2012-08-08/checklist b/research/TCS/2012-08-08/checklist new file mode 100644 index 00000000..999e436d --- /dev/null +++ b/research/TCS/2012-08-08/checklist @@ -0,0 +1,19 @@ +# Accelerating Voltage = 38.4 +# Focus Voltage = 0.17 +# Deflection Voltage = 1.346 +# Venault Voltage = 12.0 +# Initial Voltage = Sweep +# Heating Current = 1.140 +# Heating Voltage = 1.141 +# Chamber Pressure = 3.89e-8 +# 610B Zero = 0.00 +# 602 Zero = 0.00 +# 610B Scale = 1e-7 x 0.03 +# 602 Scale = 1e-5 x 0.3 +# 602 0.1 Battery = 8.40 +# 602 0.03 Battery = 7.85 +# 602 0.01 Battery = 8.05 +# 602 0.003 Battery = 8.00 +# 602 0.001 Battery = 8.10 +# ADC Regulator = 3.30 +# Checklist updated 2012-08-09 10:32 diff --git a/research/TCS/2012-08-09/105433.dat b/research/TCS/2012-08-09/105433.dat new file mode 100644 index 00000000..4c0fe948 --- /dev/null +++ b/research/TCS/2012-08-09/105433.dat @@ -0,0 +1,46 @@ +# File opened at 2012-08-09 10:54:33.262534 +# aquire[DAC_Sweep] = 0.0 + 50.0*int(step/60) +# aquire[ADC_Averages] = 200 +# aquire[DAC_Settle] = 0.0 +# aquire[ADC_Ie] = 4 +# aquire[open_files] = [, ] +# aquire[ADC_Vi] = 5 +# aquire[start_date] = 2012-08-09 +# aquire[ADC_Is] = 4 +# Parameters: +# Accelerating Voltage = 38.4 +# Focus Voltage = 0.17 +# Deflection Voltage = 1.346 +# Venault Voltage = 12.0 +# Initial Voltage = Sweep +# Heating Current = 1.140 +# Heating Voltage = 1.141 +# Chamber Pressure = 3.89e-8 +# 610B Zero = 0.00 +# 602 Zero = 0.00 +# 610B Scale = 1e-7 x 0.03 +# 602 Scale = 1e-5 x 0.3 +# 602 0.1 Battery = 8.40 +# 602 0.03 Battery = 7.85 +# 602 0.01 Battery = 8.05 +# 602 0.003 Battery = 8.00 +# 602 0.001 Battery = 8.10 +# ADC Regulator = 3.30 +# Title = Testing +# Comment = test +# Data = TEST +# Update Parameters = 2012-08-09 10:32 + +# Experiment 2012-08-09 10:54:33.263732 +# Polling for Nones. + +# Data: +# time DAC ADC4 ADC5 +0.792319536209 0 8.0 7.56 914.9 111.90 +1.472417593 0 7.56 6.80 913.88 112.16 +2.151907444 0 7.44 7.39 913.9 112.9 +2.83043909073 0 7.90 7.88 914.20 112.15 +3.51444911957 0 9.28 8.47 910.77 112.90 +4.19797611237 0 9.13 7.97 914.16 112.9 +# Program exits. +# File closed at 2012-08-09 10:54:41.063138 diff --git a/research/TCS/2012-08-09/105927.dat b/research/TCS/2012-08-09/105927.dat new file mode 100644 index 00000000..6658feef --- /dev/null +++ b/research/TCS/2012-08-09/105927.dat @@ -0,0 +1,40 @@ +# File opened at 2012-08-09 10:59:27.786538 +# aquire[DAC_Sweep] = 0.0 + 50.0*int(step/60) +# aquire[ADC_Averages] = 200 +# aquire[DAC_Settle] = 0.0 +# aquire[ADC_Ie] = 4 +# aquire[open_files] = [, ] +# aquire[ADC_Vi] = 5 +# aquire[start_date] = 2012-08-09 +# aquire[ADC_Is] = 4 +# Parameters: +# Accelerating Voltage = 38.4 +# Focus Voltage = 0.17 +# Deflection Voltage = 1.346 +# Venault Voltage = 12.0 +# Initial Voltage = Sweep +# Heating Current = 1.140 +# Heating Voltage = 1.141 +# Chamber Pressure = 3.89e-8 +# 610B Zero = 0.00 +# 602 Zero = 0.00 +# 610B Scale = 1e-7 x 0.03 +# 602 Scale = 1e-5 x 0.3 +# 602 0.1 Battery = 8.40 +# 602 0.03 Battery = 7.85 +# 602 0.01 Battery = 8.05 +# 602 0.003 Battery = 8.00 +# 602 0.001 Battery = 8.10 +# ADC Regulator = 3.30 +# Title = Testing +# Comment = test +# Data = TEST +# Parameters last checked = None + +# Experiment 2012-08-09 10:59:27.787749 +# Polling for Nones. + +# Data: +# time DAC ADC4 ADC5 +0.792268037796 0 7.89 7.42 913.27 111.80 +1.47629332542 0 7.48 7.16 913.85 111.99 diff --git a/research/TCS/2012-08-09/105938.dat b/research/TCS/2012-08-09/105938.dat new file mode 100644 index 00000000..44f65f51 --- /dev/null +++ b/research/TCS/2012-08-09/105938.dat @@ -0,0 +1,41 @@ +# File opened at 2012-08-09 10:59:38.870702 +# aquire[DAC_Sweep] = 0.0 + 50.0*int(step/60) +# aquire[ADC_Averages] = 200 +# aquire[DAC_Settle] = 0.0 +# aquire[ADC_Ie] = 4 +# aquire[open_files] = [, , ] +# aquire[ADC_Vi] = 5 +# aquire[start_date] = 2012-08-09 +# aquire[ADC_Is] = 4 +# Parameters: +# Accelerating Voltage = 38.4 +# Focus Voltage = 0.17 +# Deflection Voltage = 1.346 +# Venault Voltage = 12.0 +# Initial Voltage = Sweep +# Heating Current = 1.140 +# Heating Voltage = aasdf +# Chamber Pressure = 3.89e-8 +# 610B Zero = 0.00 +# 602 Zero = 0.00 +# 610B Scale = 1e-7 x 0.03 +# 602 Scale = 1e-5 x 0.3 +# 602 0.1 Battery = 8.40 +# 602 0.03 Battery = 7.85 +# 602 0.01 Battery = 8.05 +# 602 0.003 Battery = 8.00 +# 602 0.001 Battery = 8.10 +# ADC Regulator = 3.30 +# Title = Testing +# Comment = adsf +# Data = TEST +# Parameters last checked = 2012-08-09 10:59:38.865447 + +# Experiment 2012-08-09 10:59:38.871908 +# Polling for Nones. + +# Data: +# time DAC ADC4 ADC5 +0.785655975342 0 8.30 7.56 913.95 111.67 +# Recieved KILL signal. +# Reason: test again# File closed at 2012-08-09 10:59:41.581763 diff --git a/research/TCS/2012-08-09/checklist b/research/TCS/2012-08-09/checklist new file mode 100644 index 00000000..7f273a62 --- /dev/null +++ b/research/TCS/2012-08-09/checklist @@ -0,0 +1,23 @@ +# File opened at 2012-08-09 10:59:38.865710 +# Accelerating Voltage = 38.4 +# Focus Voltage = 0.17 +# Deflection Voltage = 1.346 +# Venault Voltage = 12.0 +# Initial Voltage = Sweep +# Heating Current = 1.140 +# Heating Voltage = aasdf +# Chamber Pressure = 3.89e-8 +# 610B Zero = 0.00 +# 602 Zero = 0.00 +# 610B Scale = 1e-7 x 0.03 +# 602 Scale = 1e-5 x 0.3 +# 602 0.1 Battery = 8.40 +# 602 0.03 Battery = 7.85 +# 602 0.01 Battery = 8.05 +# 602 0.003 Battery = 8.00 +# 602 0.001 Battery = 8.10 +# ADC Regulator = 3.30 +# Title = Testing +# Comment = adsf +# Data = TEST +# Parameters last checked = 2012-08-09 10:59:38.865447 diff --git a/research/TCS/2012-08-09/checklist.old b/research/TCS/2012-08-09/checklist.old new file mode 100644 index 00000000..f2a8467b --- /dev/null +++ b/research/TCS/2012-08-09/checklist.old @@ -0,0 +1,26 @@ +# File opened at 2012-08-09 10:59:34.993650 +# Accelerating Voltage = 38.4 +# Focus Voltage = 0.17 +# Deflection Voltage = 1.346 +# Venault Voltage = 12.0 +# Initial Voltage = Sweep +# Heating Current = 1.140 +# Heating Voltage = 1.141 +# Chamber Pressure = 3.89e-8 +# 610B Zero = 0.00 +# 602 Zero = 0.00 +# 610B Scale = 1e-7 x 0.03 +# 602 Scale = 1e-5 x 0.3 +# 602 0.1 Battery = 8.40 +# 602 0.03 Battery = 7.85 +# 602 0.01 Battery = 8.05 +# 602 0.003 Battery = 8.00 +# 602 0.001 Battery = 8.10 +# ADC Regulator = 3.30 +# Title = Testing +# Comment = test +# Data = TEST +# Parameters last checked = None + +# Recieved KILL signal. +# Reason: test again# File closed at 2012-08-09 10:59:41.581576 diff --git a/research/TCS/interface.py b/research/TCS/interface.py index 86c5e823..11ed9b29 100755 --- a/research/TCS/interface.py +++ b/research/TCS/interface.py @@ -12,7 +12,7 @@ import time import serial import datetime -import collections +import odict # TODO: Insert variables for calibration purposes here. @@ -57,7 +57,7 @@ ser = serial.Serial( rtscts=0 ) -parameters = OrderedDict([ +parameters = odict.odict([ ("Accelerating Voltage" , None), ("Focus Voltage" , None), ("Deflection Voltage" , None), @@ -75,11 +75,11 @@ parameters = OrderedDict([ ("602 0.01 Battery" , None), ("602 0.003 Battery" , None), ("602 0.001 Battery" , None), - ("ADC Battery" , None), ("ADC Regulator" , None), ("Title" , None), ("Comment" , None), ("Data" , None), + ("Parameters last checked", None) ]) def getTime(): @@ -95,17 +95,48 @@ def set_exit_handler(func): import win32api win32api.SetConsoleCtrlHandler(func, True) except ImportError: - version = “.”.join(map(str, sys.version_info[:2])) - raise Exception(”pywin32 not installed for Python ” + version) + version = ".".join(map(str, sys.version_info[:2])) + raise Exception("pywin32 not installed for Python " + version) else: import signal signal.signal(signal.SIGTERM, func) + signal.signal(signal.SIGINT, func) + +def killed_handler(signal, frame): + reason = "" + sys.stdout.write("\n# Reason for killing program? ") + reason = sys.stdin.readline().strip("\r\n ") + for out in aquire["open_files"]: + sys.stdout.write("# Closing file " + str(out) + "\n") + out.write("# Recieved KILL signal.\n# Reason: " + str(reason)) + log_close(out) -def exit_handler(): -def init(): +def cleanup(): + for out in aquire["open_files"]: + out.write("# Program exits.\n") + log_close(out) + +def log_open(a, b): + result = open(a, b,0) + if (b == "w"): + result.write("# File opened at " + str(datetime.datetime.now()) + "\n") + aquire["open_files"].append(result) + return result + +def log_close(afile): + if (afile in aquire["open_files"]): + afile.write("# File closed at " + str(datetime.datetime.now()) + "\n") + aquire["open_files"].remove(afile) + + afile.close() + +def init(): + #import atexit + #atexit.register(cleanup) + set_exit_handler(killed_handler) aquire["start_date"] = getDate() @@ -126,19 +157,19 @@ def init(): print(ser.readline().strip("\r\n")) print(ser.readline().strip("\r\n")) - print("Writing config information to config.dat...") - output = open("config.dat", "w", 1) + #print("Writing config information to config.dat...") + #output = log_open("config.dat", "w", 1) - output.write("# Initialise " + str(datetime.datetime.now()) + "\n") + #output.write("# Initialise " + str(datetime.datetime.now()) + "\n") #for field in calibrate: # output.write("# calibrate["+str(field)+"] = "+str(calibrate[field]) + "\n") #output.write("\n") - for field in aquire: - output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n") + #for field in aquire: + # output.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n") - output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n") - output.close() + #output.write("# Ready " + str(datetime.datetime.now()) + "\n# EOF\n") + #output.close() def main(): @@ -178,46 +209,55 @@ def main(): sweep += 1 -def checkList(output_file): +def checkList(): try: - input_file = open(getDate()+"/checklist", "r") + input_file = log_open(getDate()+"/checklist", "r") except: input_file = None if (input_file != None): for line in input_file: k = line.split("=") - item = k[0].strip(" \r\n") - value = k[1].strip(" \r\n") + item = None + if (len(k) >= 2): + item = k[0].strip("# \r\n") + value = k[1].strip("# \r\n") + if (item in parameters): parameters[item] = value print("Checklist found. Overwrite? [Y/n]") response = sys.stdin.readline().strip(" \r\n") if (response == "" or response == "y" or response == "Y"): + input_file = log_open(getDate()+"/checklist.old", "w") + for item in parameters: + input_file.write("# " + str(item) + " = " + str(parameters[item]) + "\n") + input_file.write("\n") input_file = None if (input_file == None): for item in parameters: + if item == "Parameters last checked": + continue sys.stdout.write("\""+str(item)+"\" = " + str(parameters[item]) + " New value?: ") response = sys.stdin.readline().strip("\r\n ") if (response != ""): parameters[item] = response sys.stdout.write("\n") + parameters["Parameters last checked"] = str(datetime.datetime.now()) - checklist = open(getDate()+"/checklist", "w", 0) + checklist = log_open(getDate()+"/checklist", "w") for item in parameters: checklist.write("# "+str(item) + " = " + str(parameters[item]) + "\n") - output_file.write("# "+str(item) + " = " + str(parameters[item]) + "\n") - + #output_file.write("# "+str(item) + " = " + str(parameters[item]) + "\n") + def record_data(ADC_channels, output, pollTime = None, dac_max = None): if (output != None): - output = [open(output, "w", 0), sys.stdout] - checkList(output[0]) + output = [log_open(output, "w"), sys.stdout] else: output = [sys.stdout] @@ -226,11 +266,11 @@ def record_data(ADC_channels, output, pollTime = None, dac_max = None): out.write("# aquire["+str(field)+"] = "+str(aquire[field]) + "\n") for out in output: - out.write("# Checklist:\n") + out.write("# Parameters:\n") - for field in checklist: + for field in parameters: for out in output: - out.write("# "+str(field)+" = " + str(checklist[field]) + "\n") + out.write("# "+str(field)+" = " + str(parameters[field]) + "\n") start_time = time.time() @@ -271,8 +311,11 @@ def record_data(ADC_channels, output, pollTime = None, dac_max = None): for channel in ADC_channels: read = readADC(channel) if read == False: - print("Abort data collection") - return False + for out in output: + print("# Abort data collection due to failed ADC read") + if out != sys.stdout: + log_close(out) + return False raw_adc.append((channel, read[0], read[1])) end_time = time.time() @@ -287,12 +330,12 @@ def record_data(ADC_channels, output, pollTime = None, dac_max = None): for out in output: if out != sys.stdout: - out.close() + log_close(out) return True def loadCalibration_ADC(channel): try: - input_file = open("calibrateADC"+str(channel)+".dat") + input_file = log_open("calibrateADC"+str(channel)+".dat") except: print("Couldn't find calibration file for ADC " + str(channel)) return False @@ -305,7 +348,7 @@ def loadCalibration_ADC(channel): else: split_line = l.split("\t") calibrate["ADC"][channel].append((float(split_line[0]), float(split_line[1]))) - input_file.close() + log_close(input_file) if (len(calibrate["ADC"][channel]) <= 0): print("Empty calibration file for ADC " + str(channel)) @@ -314,7 +357,7 @@ def loadCalibration_ADC(channel): def loadCalibration_DAC(): try: - input_file = open("calibrateDAC.dat") + input_file = log_open("calibrateDAC.dat") except: print("Couldn't find calibration file for DAC") return False @@ -333,7 +376,7 @@ def loadCalibration_DAC(): calibrate["DAC"].append((int(split_line[0]), float(split_line[1]))) - input_file.close() + log_close(input_file) if (len(calibrate["DAC"]) <= 0): print("Empty calibration file for DAC") @@ -442,7 +485,7 @@ def calibrateADC_usingDAC(channel, gain = True): return False calibrate["ADC"][channel] = [] - outfile = open("calibrateADC"+str(channel)+".dat", "w", 1) + outfile = log_open("calibrateADC"+str(channel)+".dat", "w") outfile.write("# Calibrate ADC " + str(channel) + "\n") outfile.write("# Start " + str(datetime.datetime.now()) + "\n") @@ -481,7 +524,7 @@ def calibrateADC_usingDAC(channel, gain = True): calibrate["ADC"][channel].append((value[0], input_value, value[1])) outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n") - outfile.close() + log_close(outfile) if (setDAC(0) == False): return False if (len(calibrate["ADC"][channel]) <= 0): @@ -491,7 +534,7 @@ def calibrateADC_usingDAC(channel, gain = True): def calibrateADC(channel): calibrate["ADC"][channel] = [] - outfile = open("calibrateADC"+str(channel)+".dat", "w", 1) + outfile = log_open("calibrateADC"+str(channel)+".dat", "w") outfile.write("# Calibrate ADC " + str(channel) + "\n") outfile.write("# Start " + str(datetime.datetime.now()) + "\n") @@ -516,7 +559,7 @@ def calibrateADC(channel): outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n") - outfile.close() + log_close(outfile) if (len(calibrate["ADC"][channel]) <= 0): print("Error: No calibration points taken for ADC " + str(channel)) return False @@ -526,7 +569,7 @@ def calibrateADC(channel): def calibrateDAC(): calibrate["DAC"] = [] - outfile = open("calibrateDAC.dat", "w", 1) + outfile = log_open("calibrateDAC.dat", "w") outfile.write("# Calibrate DAC\n") outfile.write("# Start " + str(datetime.datetime.now()) + "\n") @@ -568,7 +611,7 @@ def calibrateDAC(): level += stepSize outfile.write("# Stop " + str(datetime.datetime.now()) + "\n# EOF\n") - outfile.close() + log_close(outfile) if (len(calibrate["DAC"]) <= 0): print("Error: No calibration points taken for DAC") return False diff --git a/research/TCS/interface.pyc b/research/TCS/interface.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7ce380cdfdf88105b9f9965e7bc7ab8c0437096 GIT binary patch literal 16493 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 odict([('a', 1), ('b', 2), ('a', 3)]) + odict.odict([('a', 3), ('b', 2)]) + + This behavor is consistent with existing implementation in Python + and the PHP array and the hashmap in Ruby 1.9. + + This odict doesn't scale! + + Yes it doesn't. The delitem operation is O(n). This is file is a + mockup of a real odict that could be implemented for collections + based on an linked list. + + Why is there no .insert()? + + There are few situations where you really want to insert a key at + an specified index. To now make the API too complex the proposed + solution for this situation is creating a list of items, manipulating + that and converting it back into an odict: + + >>> d = odict([('a', 42), ('b', 23), ('c', 19)]) + >>> l = d.items() + >>> l.insert(1, ('x', 0)) + >>> odict(l) + odict.odict([('a', 42), ('x', 0), ('b', 23), ('c', 19)]) + + :copyright: (c) 2008 by Armin Ronacher and PEP 273 authors. + :license: modified BSD license. +""" +from itertools import izip, imap +from copy import deepcopy + +missing = object() + + +class odict(dict): + """ + Ordered dict example implementation. + + This is the proposed interface for a an ordered dict as proposed on the + Python mailinglist (proposal_). + + It's a dict subclass and provides some list functions. The implementation + of this class is inspired by the implementation of Babel but incorporates + some ideas from the `ordereddict`_ and Django's ordered dict. + + The constructor and `update()` both accept iterables of tuples as well as + mappings: + + >>> d = odict([('a', 'b'), ('c', 'd')]) + >>> d.update({'foo': 'bar'}) + >>> d + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')]) + + Keep in mind that when updating from dict-literals the order is not + preserved as these dicts are unsorted! + + You can copy an odict like a dict by using the constructor, `copy.copy` + or the `copy` method and make deep copies with `copy.deepcopy`: + + >>> from copy import copy, deepcopy + >>> copy(d) + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')]) + >>> d.copy() + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')]) + >>> odict(d) + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')]) + >>> d['spam'] = [] + >>> d2 = deepcopy(d) + >>> d2['spam'].append('eggs') + >>> d + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])]) + >>> d2 + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', ['eggs'])]) + + All iteration methods as well as `keys`, `values` and `items` return + the values ordered by the the time the key-value pair is inserted: + + >>> d.keys() + ['a', 'c', 'foo', 'spam'] + >>> d.values() + ['b', 'd', 'bar', []] + >>> d.items() + [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])] + >>> list(d.iterkeys()) + ['a', 'c', 'foo', 'spam'] + >>> list(d.itervalues()) + ['b', 'd', 'bar', []] + >>> list(d.iteritems()) + [('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])] + + Index based lookup is supported too by `byindex` which returns the + key/value pair for an index: + + >>> d.byindex(2) + ('foo', 'bar') + + You can reverse the odict as well: + + >>> d.reverse() + >>> d + odict.odict([('spam', []), ('foo', 'bar'), ('c', 'd'), ('a', 'b')]) + + And sort it like a list: + + >>> d.sort(key=lambda x: x[0].lower()) + >>> d + odict.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar'), ('spam', [])]) + + .. _proposal: http://thread.gmane.org/gmane.comp.python.devel/95316 + .. _ordereddict: http://www.xs4all.nl/~anthon/Python/ordereddict/ + """ + + def __init__(self, *args, **kwargs): + dict.__init__(self) + self._keys = [] + self.update(*args, **kwargs) + + def __delitem__(self, key): + dict.__delitem__(self, key) + self._keys.remove(key) + + def __setitem__(self, key, item): + if key not in self: + self._keys.append(key) + dict.__setitem__(self, key, item) + + def __deepcopy__(self, memo=None): + if memo is None: + memo = {} + d = memo.get(id(self), missing) + if d is not missing: + return d + memo[id(self)] = d = self.__class__() + dict.__init__(d, deepcopy(self.items(), memo)) + d._keys = self._keys[:] + return d + + def __getstate__(self): + return {'items': dict(self), 'keys': self._keys} + + def __setstate__(self, d): + self._keys = d['keys'] + dict.update(d['items']) + + def __reversed__(self): + return reversed(self._keys) + + def __eq__(self, other): + if isinstance(other, odict): + if not dict.__eq__(self, other): + return False + return self.items() == other.items() + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __cmp__(self, other): + if isinstance(other, odict): + return cmp(self.items(), other.items()) + elif isinstance(other, dict): + return dict.__cmp__(self, other) + return NotImplemented + + @classmethod + def fromkeys(cls, iterable, default=None): + return cls((key, default) for key in iterable) + + def clear(self): + del self._keys[:] + dict.clear(self) + + def copy(self): + return self.__class__(self) + + def items(self): + return zip(self._keys, self.values()) + + def iteritems(self): + return izip(self._keys, self.itervalues()) + + def keys(self): + return self._keys[:] + + def iterkeys(self): + return iter(self._keys) + + def pop(self, key, default=missing): + if default is missing: + return dict.pop(self, key) + elif key not in self: + return default + self._keys.remove(key) + return dict.pop(self, key, default) + + def popitem(self, key): + self._keys.remove(key) + return dict.popitem(key) + + def setdefault(self, key, default=None): + if key not in self: + self._keys.append(key) + dict.setdefault(self, key, default) + + def update(self, *args, **kwargs): + sources = [] + if len(args) == 1: + if hasattr(args[0], 'iteritems'): + sources.append(args[0].iteritems()) + else: + sources.append(iter(args[0])) + elif args: + raise TypeError('expected at most one positional argument') + if kwargs: + sources.append(kwargs.iteritems()) + for iterable in sources: + for key, val in iterable: + self[key] = val + + def values(self): + return map(self.get, self._keys) + + def itervalues(self): + return imap(self.get, self._keys) + + def index(self, item): + return self._keys.index(item) + + def byindex(self, item): + key = self._keys[item] + return (key, dict.__getitem__(self, key)) + + def reverse(self): + self._keys.reverse() + + def sort(self, *args, **kwargs): + self._keys.sort(*args, **kwargs) + + def __repr__(self): + return 'odict.odict(%r)' % self.items() + + __copy__ = copy + __iter__ = iterkeys + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/research/TCS/odict.pyc b/research/TCS/odict.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ade9ba7b56085d7f0e927992bd0b18fcef19202 GIT binary patch literal 14412 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