--- /dev/null
+# 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
--- /dev/null
+# 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] = [<open file '2012-08-09/checklist', mode 'w' at 0x7f6530b1e660>, <open file '2012-08-09/105433.dat', mode 'w' at 0x7f6530b1e5d0>]
+# 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
--- /dev/null
+# 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] = [<open file '2012-08-09/checklist', mode 'w' at 0x7f5d9cedc660>, <open file '2012-08-09/105927.dat', mode 'w' at 0x7f5d9cedc5d0>]
+# 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
--- /dev/null
+# 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] = [<open file '2012-08-09/checklist.old', mode 'w' at 0x7f1990cef660>, <open file '2012-08-09/checklist', mode 'w' at 0x7f1990cef5d0>, <open file '2012-08-09/105938.dat', mode 'w' at 0x7f1990cef6f0>]
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
import serial
import datetime
-import collections
+import odict
# TODO: Insert variables for calibration purposes here.
rtscts=0
)
-parameters = OrderedDict([
+parameters = odict.odict([
("Accelerating Voltage" , None),
("Focus Voltage" , None),
("Deflection Voltage" , None),
("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():
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()
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():
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]
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()
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()
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
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))
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
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")
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")
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):
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")
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
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")
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
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+ odict
+ ~~~~~
+
+ This module is an example implementation of an ordered dict for the
+ collections module. It's not written for performance (it actually
+ performs pretty bad) but to show how the API works.
+
+
+ Questions and Answers
+ =====================
+
+ Why would anyone need ordered dicts?
+
+ Dicts in python are unordered which means that the order of items when
+ iterating over dicts is undefined. As a matter of fact it is most of
+ the time useless and differs from implementation to implementation.
+
+ Many developers stumble upon that problem sooner or later when
+ comparing the output of doctests which often does not match the order
+ the developer thought it would.
+
+ Also XML systems such as Genshi have their problems with unordered
+ dicts as the input and output ordering of tag attributes is often
+ mixed up because the ordering is lost when converting the data into
+ a dict. Switching to lists is often not possible because the
+ complexity of a lookup is too high.
+
+ Another very common case is metaprogramming. The default namespace
+ of a class in python is a dict. With Python 3 it becomes possible
+ to replace it with a different object which could be an ordered dict.
+ Django is already doing something similar with a hack that assigns
+ numbers to some descriptors initialized in the class body of a
+ specific subclass to restore the ordering after class creation.
+
+ When porting code from programming languages such as PHP and Ruby
+ where the item-order in a dict is guaranteed it's also a great help
+ to have an equivalent data structure in Python to ease the transition.
+
+ Where are new keys added?
+
+ At the end. This behavior is consistent with Ruby 1.9 Hashmaps
+ and PHP Arrays. It also matches what common ordered dict
+ implementations do currently.
+
+ What happens if an existing key is reassigned?
+
+ The key is *not* moved. This is consitent with existing
+ implementations and can be changed by a subclass very easily::
+
+ class movingodict(odict):
+ def __setitem__(self, key, value):
+ self.pop(key, None)
+ odict.__setitem__(self, key, value)
+
+ Moving keys to the end of a ordered dict on reassignment is not
+ very useful for most applications.
+
+ Does it mean the dict keys are sorted by a sort expression?
+
+ That's not the case. The odict only guarantees that there is an order
+ and that newly inserted keys are inserted at the end of the dict. If
+ you want to sort it you can do so, but newly added keys are again added
+ at the end of the dict.
+
+ I initializes the odict with a dict literal but the keys are not
+ ordered like they should!
+
+ Dict literals in Python generate dict objects and as such the order of
+ their items is not guaranteed. Before they are passed to the odict
+ constructor they are already unordered.
+
+ What happens if keys appear multiple times in the list passed to the
+ constructor?
+
+ The same as for the dict. The latter item overrides the former. This
+ has the side-effect that the position of the first key is used because
+ the key is actually overwritten:
+
+ >>> 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()