Index: server/lib/ippvalue.py
===================================================================
--- server/lib/ippvalue.py	(revision c269bc7fb4d107878536921b17ef5bd63edabf68)
+++ server/lib/ippvalue.py	(revision fa0d0ef6955888c258d847870c365800a54618c5)
@@ -7,5 +7,5 @@
 logger = logging.getLogger("ippLogger")
 
-class Value():
+class Value(object):
     """
     An IPP value consists of a tag and a value.
@@ -23,5 +23,5 @@
     """
 
-    def __init__(self, value_tag=None, value=None, unpack=True):
+    def __init__(self, value_tag=None, value=None):
         """
         Initialize a Value.  There are three different ways you can
@@ -31,12 +31,9 @@
 
             Value(value_tag, value) -- creates a Value instance from
-            binary values
-
-            Value(value_tag, value, unpack=False) -- creates a Value
-            instance from non-binary values
+            a non-binary value
 
         If you create an empty Value instance, once you have set
-        value_tag and value, you should call pack(), or if you set
-        value_tag and binary_value, call unpack().
+        value_tag and value, you can retrieve the packed value from
+        the packed_value property.
 
         Arguments:
@@ -44,81 +41,63 @@
             value_tag -- one byte, identifying the type of value
 
-            value -- variable size, containing the actual value.  If
-            unpack is True, then this should be a binary value,
-            otherwise it should be a string or number.
-
-            unpack -- boolean which indicates whether the value passed
-            in is binary (i.e., should be unpacked) or not (i.e., does
-            not need to be unpacked)
+            value -- variable size, containing the actual value.
+            It should be a string or number.
         """
 
         # make sure the arguments are valid
-        if value_tag is not None:
-            assert value is not None, \
-                   "value must not be null because " + \
-                   "value_tag is not null!"
-        elif value_tag is None:
-            assert value is None, \
-                   "value must be null because value_tag is null!"
+        if value is not None:
+            assert value_tag is not None, \
+                   "value_tag must not be null because " + \
+                   "value is not null!"
 
         # initialize member variables
-        self.value_tag    = None # one byte, the type of value
-        self.value        = None # non-binary value of self.value
-        self.tag_size     = 1    # length of the tag in bytes
-        self.value_size   = None # size of self.value in bytes
-        self.binary_value = None # binary value of self.value
-
-        if value_tag is not None and value is not None:
-            if unpack:
-                self.value_tag = value_tag
-                self.binary_value = value
-                self.value_size, self.value = self.unpack()
-            else:
-                self.value_tag = value_tag
-                self.value = value
-                self.value_size, self.binary_value = self.pack()
-
-            self.verify()
-
-    def unpack(self):
-        """
-        Given self.value_tag and self.binary_value, unpack the binary
+        self.value_tag = value_tag # one byte, the type of value
+        self.value     = value     # non-binary value of self.value
+
+    @classmethod
+    def unpack(cls, value_tag, packed_value):
+        """Unpack a binary IPP value
+
+        Unpacks a binary string into a Value object.
+
+        """
+        return cls(value_tag, cls._unpack(value_tag, packed_value))
+
+    @staticmethod
+    def _unpack(value_tag, packed_value):
+        """
+        Given self.value_tag and self.packed_value, unpack the binary
         value into either a string or number.  These values MUST NOT
-        be null.  Furthermore, if self.value and self.value_size are
-        set, this method will check to make sure that unpacking
-        self.binary_value matches up.
-
-        Returns: tuple of (value_size, value)
-        """
-
-        assert self.value_tag is not None, \
+        be null.
+
+        Returns: unpacked value
+
+        """
+
+        assert value_tag is not None, \
                "Cannot unpack values with unspecified value tag!"
-        assert self.binary_value is not None, \
+        assert packed_value is not None, \
                "Cannot unpack null values!"
 
-        value_size = None
         value = None
 
         # out-of-band value tags
-        if self.value_tag == OutOfBandTags.UNSUPPORTED or \
-               self.value_tag == OutOfBandTags.DEFAULT or \
-               self.value_tag == OutOfBandTags.UNKNOWN or \
-               self.value_tag == OutOfBandTags.NO_VALUE:
+        if value_tag == OutOfBandTags.UNSUPPORTED or \
+               value_tag == OutOfBandTags.DEFAULT or \
+               value_tag == OutOfBandTags.UNKNOWN or \
+               value_tag == OutOfBandTags.NO_VALUE:
             value_size = 0
             value = ''
 
         # integer value tags
-        elif self.value_tag == IntegerTags.INTEGER:
-            value_size = 4
-            value = struct.unpack('>i', self.binary_value)[0]
-        elif self.value_tag == IntegerTags.BOOLEAN:
-            value_size = 1
-            value = struct.unpack('>?', self.binary_value)[0]
-        elif self.value_tag == IntegerTags.ENUM:
-            value_size = 4
-            value = struct.unpack('>i', self.binary_value)[0]
+        elif value_tag == IntegerTags.INTEGER:
+            value = struct.unpack('>i', packed_value)[0]
+        elif value_tag == IntegerTags.BOOLEAN:
+            value = struct.unpack('>?', packed_value)[0]
+        elif value_tag == IntegerTags.ENUM:
+            value = struct.unpack('>i', packed_value)[0]
 
         
-        elif self.value_tag == OctetStringTags.DATETIME:
+        elif value_tag == OctetStringTags.DATETIME:
             # field  octets  contents                  range
             # -----  ------  --------                  -----
@@ -135,8 +114,7 @@
             #  10      11    minutes from UTC          0..59
 
-            value_size = 11
-            value = struct.unpack('>hbbbbbbcbb', self.binary_value)[0]
-            
-        elif self.value_tag == OctetStringTags.RESOLUTION:
+            value = struct.unpack('>hbbbbbbcbb', packed_value)
+            
+        elif value_tag == OctetStringTags.RESOLUTION:
             # OCTET-STRING consisting of nine octets of 2
             # SIGNED-INTEGERs followed by a SIGNED-BYTE. The first
@@ -146,65 +124,51 @@
             # contains the units
 
-            value_size = 9
-            value = struct.unpack('>iib', self.binary_value)
-            
-        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
+            value = struct.unpack('>iib', packed_value)
+            
+        elif value_tag == OctetStringTags.RANGE_OF_INTEGER:
             # Eight octets consisting of 2 SIGNED-INTEGERs.  The first
             # SIGNED-INTEGER contains the lower bound and the second
             # SIGNED-INTEGER contains the upper bound.
 
-            value_size = 8
-            value = struct.unpack('>ii', self.binary_value)
-
-        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
-                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
-            a = struct.unpack('>h', self.binary_value[:2])[0]
-            b = struct.unpack('>%ss' % a, self.binary_value[2:a+2])[0]
-            c = struct.unpack('>h', self.binary_value[a+2:a+4])[0]
-            d = struct.unpack('>%ss' % c, self.binary_value[a+4:][0])
-            value_size = 4 + a + c
+            value = struct.unpack('>ii', packed_value)
+
+        elif value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
+                 value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
+            a = struct.unpack('>h', packed_value[:2])[0]
+            b = struct.unpack('>%ss' % a, packed_value[2:a+2])[0]
+            c = struct.unpack('>h', packed_value[a+2:a+4])[0]
+            d = struct.unpack('>%ss' % c, packed_value[a+4:][0])
             value = (a, b, c, d)
 
         # character string value tags
-        elif self.value_tag == \
+        elif value_tag == \
                  CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
-                 self.value_tag == \
+                 value_tag == \
                  CharacterStringTags.NAME_WITHOUT_LANGUAGE:
-            value_size = len(str(self.binary_value))
-            value = str(self.binary_value)
-        elif self.value_tag == CharacterStringTags.GENERIC or \
-                 self.value_tag == CharacterStringTags.KEYWORD or \
-                 self.value_tag == CharacterStringTags.URI or \
-                 self.value_tag == CharacterStringTags.URI_SCHEME or \
-                 self.value_tag == CharacterStringTags.CHARSET or \
-                 self.value_tag == CharacterStringTags.NATURAL_LANGUAGE or \
-                 self.value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
-            value_size = len(str(self.binary_value))
-            value = str(self.binary_value)
+            value = str(packed_value)
+        elif value_tag == CharacterStringTags.GENERIC or \
+                 value_tag == CharacterStringTags.KEYWORD or \
+                 value_tag == CharacterStringTags.URI or \
+                 value_tag == CharacterStringTags.URI_SCHEME or \
+                 value_tag == CharacterStringTags.CHARSET or \
+                 value_tag == CharacterStringTags.NATURAL_LANGUAGE or \
+                 value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
+            value = str(packed_value)
 
         # anything else that we didn't handle
         else:
-            if value_size is None and value is None:
-                value_size = len(self.binary_value)
-                value = self.binary_value
-
-        if self.value_size is not None:
-            assert value_size == self.value_size, \
-                   "unpacked value_size is not the same " + \
-                   "as self.value_size!"
-        if self.value is not None:
-            assert value == self.value, \
-                   "unpacked value is not the same as self.value!"
-
-        return value_size, value
-
-    def pack(self):
+            if value is None:
+                value = packed_value
+
+        return value
+
+    @property
+    def packed_value(self):
         """
         Given self.value_tag and self.value, pack the value into
-        binary formo.  These values MUST NOT be null.  Furthermore, if
-        self.binary_value and self.value_size are set, this method
-        will check to make sure that packing self.value matches up.
-
-        Returns: tuple of (value_size, binary_value)
+        binary form.  These values MUST NOT be null.
+
+        Returns: packed_value
+
         """
         
@@ -214,6 +178,5 @@
                "cannot pack null value!"
 
-        value_size = None
-        binary_value = None
+        packed_value = None
 
         # out-of-band value tags
@@ -222,17 +185,13 @@
                self.value_tag == OutOfBandTags.UNKNOWN or \
                self.value_tag == OutOfBandTags.NO_VALUE:
-            value_size = 0
-            binary_value = ''
+            packed_value = ''
 
         # integer value tags
         elif self.value_tag == IntegerTags.INTEGER:
-            value_size = 4
-            binary_value = struct.pack('>i', self.value)
+            packed_value = struct.pack('>i', self.value)
         elif self.value_tag == IntegerTags.BOOLEAN:
-            value_size = 1
-            binary_value = struct.pack('>?', self.value)
+            packed_value = struct.pack('>?', self.value)
         elif self.value_tag == IntegerTags.ENUM:
-            value_size = 4
-            binary_value = struct.pack('>i', self.value)
+            packed_value = struct.pack('>i', self.value)
 
         # octet string value tags
@@ -252,6 +211,5 @@
             #  10      11    minutes from UTC          0..59
 
-            value_size = 11
-            binary_value = struct.pack('>hbbbbbbcbb', self.value)
+            packed_value = struct.pack('>hbbbbbbcbb', *self.value)
             
         elif self.value_tag == OctetStringTags.RESOLUTION:
@@ -263,6 +221,5 @@
             # contains the units
 
-            value_size = 9
-            binary_value = truct.pack('>iib', self.value)
+            packed_value = truct.pack('>iib', self.value)
             
         elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
@@ -271,6 +228,5 @@
             # SIGNED-INTEGER contains the upper bound.
 
-            value_size = 8
-            binary_value = struct.pack('>ii', self.value)
+            packed_value = struct.pack('>ii', self.value)
 
         elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
@@ -282,6 +238,5 @@
             d_bin = struct.pack('>%ss' % self.value[2], self.value[3])
 
-            value_size = 4 + self.value[0] + self.value[2]
-            binary_value = a_bin + b_bin + c_bin + d_bin
+            packed_value = a_bin + b_bin + c_bin + d_bin
 
         # character string value tags
@@ -291,6 +246,5 @@
                  CharacterStringTags.NAME_WITHOUT_LANGUAGE:
 
-            value_size = len(self.value)
-            binary_value = struct.pack('>%ss' % len(self.value),
+            packed_value = struct.pack('>%ss' % len(self.value),
                                        self.value)
                     
@@ -303,118 +257,41 @@
                  self.value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
             
-            value_size = len(self.value)
-            binary_value = struct.pack('>%ss' % len(self.value),
+            packed_value = struct.pack('>%ss' % len(self.value),
                                        self.value)
 
         else:
-            value_size = len(self.value)
-            binary_value = self.value
-
-        return value_size, binary_value
-
-    def verify(self):
-        """
-        Verifies that the binary and non-binary values for this Value
-        instance line up.  All of self.value, self.binary_value,
-        self.value_size, and self.value_tag must be defined.
-
-        This function will throw an assertion error if the Value
-        instance is not valid.
-        """
-
-        assert self.value is not None, \
-               "value is null!"
-        assert self.binary_value is not None, \
-               "binary value is null!"
-        assert self.value_size is not None, \
-               "value size is unknown!"
-        assert self.value_tag is not None, \
-               "value type is unknown!"
-
-        value_size1, binary_value = self.pack()
-        value_size2, value = self.unpack()
-
-        assert value_size1 == value_size2, \
-               "packed value size is not the same as " + \
-               "unpacked value size!"
-        assert value_size == self.value_size, \
-               "packed value size is not the same as " + \
-               "self.value_size!"
-        assert binary_value == self.binary_value, \
-               "packed binary value is not the same as " + \
-               "self.binary_value!"
-
-    def getValue(self):
-        """
-        Get the non-binary value.
-        """
-
-        return self.value
-
-    def getBinaryValue(self):
-        """
-        Get the binary value.
+            packed_value = self.value
+
+        return packed_value
+
+    @packed_value.setter
+    def set_packed_value(self, packed_value):
+        """Replace a value using a new packed value
+
+        Unpacks a new packed_value (of the same value_tag).
+
+        """
+        self.value = self._unpack(self.value_tag, packed_value)
+
+    @property
+    def packed_value_size(self):
+        """
+        Get the size of the value in bytes.
         """
         
-        return self.binary_value
-
-    def getValueTag(self):
-        """
-        Get the value tag (type of value). This is an integer
-        corresponding to a value in ippconstants.
-        """
-        
-        return self.value_tag
-
-    def getValueSize(self):
-        """
-        Get the size of the value in bytes.
-        """
-        
-        return self.value_size
-
-    def getTagSize(self):
-        """
-        Get the size of the value tag in bytes.
-        """
-
-        return self.tag_size
-
-    def setValue(self, value):
-        """
-        Set the non-binary value.
-        """
-
-        self.value = value
-
-    def setBinaryValue(self, binary_value):
-        """
-        Set the binary value.
-        """
-
-        self.binary_value = binary_value
-
-    def setValueTag(self, value_tag):
-        """
-        Set the type (tag) of the value.  This should correspond to an
-        integer defined in ippconstants.
-        """
-        
-        self.value_tag = value_tag
-
-    def setValueSize(self, size):
-        """
-        Set the size of the value in bytes.
-        """
-        
-        self.value_size = size
-
-    def getTotalSize(self):
+        return len(self.packed_value)
+
+    @property
+    def total_size(self):
         """
         Get the total size of the IPP value.
         """
 
-        return self.value_size + self.tag_size
+        # 1 byte for the tag
+        return self.packed_value_size + 1
 
     def __str__(self):
-        return self.value
+        return str(self.value)
+
+    def __repr__(self):
+        return '<IPPValue (%r, %r)>' % (self.value_tag, self.value)
