Utilities/binplistlib.py

branch
Py2 comp.
changeset 3057
10516539f238
parent 2525
8b507a9a2d40
parent 2997
7f0ef975da9e
child 3058
0a02c433f52d
--- a/Utilities/binplistlib.py	Tue Oct 15 22:03:54 2013 +0200
+++ b/Utilities/binplistlib.py	Fri Oct 18 23:00:41 2013 +0200
@@ -4,7 +4,8 @@
 #
 
 """
-Module implementing a library for reading and writing binary property list files.
+Module implementing a library for reading and writing binary property list
+files.
 
 Binary Property List (plist) files provide a faster and smaller serialization
 format for property lists on OS X. This is a library for generating binary
@@ -79,14 +80,15 @@
 #
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
 #
 
 from collections import namedtuple
@@ -112,12 +114,18 @@
     This is used in keyed archiving.
     """
     def __repr__(self):
+        """
+        Public method to return an object representation.
+        
+        @return object representation (string)
+        """
         return "Uid(%d)" % self
 
 
 class Data(bytes):
     """
-    Class implementing a wrapper around bytes types for representing Data values.
+    Class implementing a wrapper around bytes types for representing Data
+    values.
     """
     pass
 
@@ -140,7 +148,8 @@
     """
     Module function to read a plist file.
     
-    @param pathOrFile name of the plist file (string) or an open file (file object)
+    @param pathOrFile name of the plist file (string) or an open file
+        (file object)
     @return reference to the read object
     @exception InvalidPlistException raised to signal an invalid plist file
     """
@@ -168,8 +177,10 @@
     Module function to write a plist file.
     
     @param rootObject reference to the object to be written
-    @param pathOrFile name of the plist file (string) or an open file (file object)
-    @param binary flag indicating the generation of a binary plist file (boolean)
+    @param pathOrFile name of the plist file (string) or an open file
+        (file object)
+    @param binary flag indicating the generation of a binary plist file
+        (boolean)
     """
     if not binary:
         plistlib.writePlist(rootObject, pathOrFile)
@@ -192,7 +203,6 @@
     
     @param data plist data (bytes)
     @return reference to the read object
-    @exception InvalidPlistException raised to signal an invalid plist file
     """
     return readPlist(BytesIO(data))
 
@@ -202,7 +212,9 @@
     Module function to write a plist bytes object.
     
     @param rootObject reference to the object to be written
-    @param binary flag indicating the generation of a binary plist bytes object (boolean)
+    @param binary flag indicating the generation of a binary plist bytes
+        object (boolean)
+    @return bytes object containing the plist data
     """
     if not binary:
         return plistlib.writePlistToBytes(rootObject)
@@ -228,10 +240,11 @@
         return False
 
 PlistTrailer = namedtuple('PlistTrailer',
-    'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber, offsetTableOffset')
+    'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber,'
+    ' offsetTableOffset')
 PlistByteCounts = namedtuple('PlistByteCounts',
-    'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes, stringBytes, '
-    'uidBytes, arrayBytes, setBytes, dictBytes')
+    'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes,'
+    ' stringBytes, uidBytes, arrayBytes, setBytes, dictBytes')
 
 
 class PlistReader(object):
@@ -275,6 +288,8 @@
         Private method to read the root object.
         
         @return unpickled object
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         result = None
         self.reset()
@@ -287,18 +302,22 @@
             raise InvalidPlistException("File is too short.")
         trailerContents = self.contents[-32:]
         try:
-            self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents))
+            self.trailer = PlistTrailer._make(
+                unpack("!xxxxxxBBQQQ", trailerContents))
             offset_size = self.trailer.offsetSize * self.trailer.offsetCount
             offset = self.trailer.offsetTableOffset
             offset_contents = self.contents[offset:offset + offset_size]
             offset_i = 0
             while offset_i < self.trailer.offsetCount:
                 begin = self.trailer.offsetSize * offset_i
-                tmp_contents = offset_contents[begin:begin + self.trailer.offsetSize]
-                tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize)
+                tmp_contents = offset_contents[
+                    begin:begin + self.trailer.offsetSize]
+                tmp_sized = self.getSizedInteger(
+                    tmp_contents, self.trailer.offsetSize)
                 self.offsets.append(tmp_sized)
                 offset_i += 1
-            self.setCurrentOffsetToObjectNumber(self.trailer.topLevelObjectNumber)
+            self.setCurrentOffsetToObjectNumber(
+                self.trailer.topLevelObjectNumber)
             result = self.readObject()
         except TypeError as e:
             raise InvalidPlistException(e)
@@ -317,6 +336,8 @@
         Private method to read the object data.
         
         @return unpickled object
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         result = None
         tmp_byte = self.contents[self.currentOffset:self.currentOffset + 1]
@@ -343,7 +364,8 @@
                 pass  # fill byte
             else:
                 raise InvalidPlistException(
-                    "Invalid object found at offset: {0}".format(self.currentOffset - 1))
+                    "Invalid object found at offset: {0}".format(
+                        self.currentOffset - 1))
         # int
         elif format == 0b0001:
             extra = proc_extra(extra)
@@ -408,6 +430,8 @@
         
         @param length length of the object (integer)
         @return float object
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         result = 0.0
         to_read = pow(2, length)
@@ -432,7 +456,8 @@
         i = 0
         while i < count:
             fragment = self.contents[
-                self.currentOffset:self.currentOffset + self.trailer.objectRefSize]
+                self.currentOffset:
+                self.currentOffset + self.trailer.objectRefSize]
             ref = self.getSizedInteger(fragment, len(fragment))
             refs.append(ref)
             self.currentOffset += self.trailer.objectRefSize
@@ -497,7 +522,8 @@
         @return unicode encoded string
         """
         actual_length = length * 2
-        data = self.contents[self.currentOffset:self.currentOffset + actual_length]
+        data = self.contents[
+            self.currentOffset:self.currentOffset + actual_length]
         # unpack not needed?!! data = unpack(">%ds" % (actual_length), data)[0]
         self.currentOffset += actual_length
         return data.decode('utf_16_be')
@@ -509,8 +535,10 @@
         @return date object (datetime.datetime)
         """
         global apple_reference_date_offset
-        result = unpack(">d", self.contents[self.currentOffset:self.currentOffset + 8])[0]
-        result = datetime.datetime.utcfromtimestamp(result + apple_reference_date_offset)
+        result = unpack(">d",
+            self.contents[self.currentOffset:self.currentOffset + 8])[0]
+        result = datetime.datetime.utcfromtimestamp(
+            result + apple_reference_date_offset)
         self.currentOffset += 8
         return result
     
@@ -540,6 +568,9 @@
         
         @param data data to extract the integer from (bytes)
         @param bytes length of the integer (integer)
+        @return read integer (integer)
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         result = 0
         # 1, 2, and 4 byte integers are unsigned
@@ -552,7 +583,8 @@
         elif bytes == 8:
             result = unpack('>q', data)[0]
         else:
-            raise InvalidPlistException("Encountered integer longer than 8 bytes.")
+            raise InvalidPlistException(
+                "Encountered integer longer than 8 bytes.")
         return result
 
 
@@ -561,9 +593,19 @@
     Class wrapping a hashable value.
     """
     def __init__(self, value):
+        """
+        Constructor
+        
+        @param value object value
+        """
         self.value = value
 
     def __repr__(self):
+        """
+        Public method to generate a representation of the object.
+        
+        @return object representation (string)
+        """
         return "<HashableWrapper: %s>" % [self.value]
 
 
@@ -572,9 +614,19 @@
     Class wrapping a boolean value.
     """
     def __init__(self, value):
+        """
+        Constructor
+        
+        @param value object value (boolean)
+        """
         self.value = value
 
     def __repr__(self):
+        """
+        Public method to generate a representation of the object.
+        
+        @return object representation (string)
+        """
         return "<BoolWrapper: %s>" % self.value
 
 
@@ -624,6 +676,7 @@
         If the given object has been written already, return its
         position in the offset table. Otherwise, return None.
         
+        @param obj object
         @return position of the object (integer)
         """
         return self.writtenReferences.get(obj)
@@ -656,12 +709,14 @@
         """
         output = self.header
         wrapped_root = self.wrapRoot(root)
-        should_reference_root = True  # not isinstance(wrapped_root, HashableWrapper)
-        self.computeOffsets(wrapped_root, asReference=should_reference_root, isRoot=True)
+        should_reference_root = True
+        self.computeOffsets(
+            wrapped_root, asReference=should_reference_root, isRoot=True)
         self.trailer = self.trailer._replace(
             **{'objectRefSize': self.intSize(len(self.computedUniques))})
         (_, output) = self.writeObjectReference(wrapped_root, output)
-        output = self.writeObject(wrapped_root, output, setReferencePosition=True)
+        output = self.writeObject(
+            wrapped_root, output, setReferencePosition=True)
         
         # output size at this point is an upper bound on how big the
         # object reference offsets need to be.
@@ -710,15 +765,32 @@
             return root
 
     def incrementByteCount(self, field, incr=1):
+        """
+        Public method to increment the byte count.
+        
+        @param field field to evaluate
+        @param incr byte count increment (integer)
+        """
         self.byteCounts = self.byteCounts._replace(
             **{field: self.byteCounts.__getattribute__(field) + incr})
 
     def computeOffsets(self, obj, asReference=False, isRoot=False):
+        """
+        Public method to compute offsets of an object.
+        
+        @param obj plist object
+        @param asReference flag indicating offsets as references (boolean)
+        @param isRoot flag indicating a root object (boolean)
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
+        """                                             # __IGNORE_WARNING__
         def check_key(key):
             if key is None:
-                raise InvalidPlistException('Dictionary keys cannot be null in plists.')
+                raise InvalidPlistException(
+                    'Dictionary keys cannot be null in plists.')
             elif isinstance(key, Data):
-                raise InvalidPlistException('Data cannot be dictionary keys in plists.')
+                raise InvalidPlistException(
+                    'Data cannot be dictionary keys in plists.')
             elif not isinstance(key, str):
                 raise InvalidPlistException('Keys must be strings.')
         
@@ -726,6 +798,7 @@
             if size > 0b1110:
                 size += self.intSize(size)
             return size
+        
         # If this should be a reference, then we keep a record of it in the
         # uniques table.
         if asReference:
@@ -798,7 +871,8 @@
                                       bytes=self.trailer.objectRefSize)
             return (True, output)
         else:
-            output += self.binaryInt(position, bytes=self.trailer.objectRefSize)
+            output += self.binaryInt(
+                position, bytes=self.trailer.objectRefSize)
             return (False, output)
 
     def writeObject(self, obj, output, setReferencePosition=False):
@@ -870,7 +944,8 @@
                     if isNew:
                         objectsToWrite.append(objRef)
                 for objRef in objectsToWrite:
-                    output = self.writeObject(objRef, output, setReferencePosition=True)
+                    output = self.writeObject(
+                        objRef, output, setReferencePosition=True)
             elif isinstance(obj, dict):
                 output += proc_variable_length(0b1101, len(obj))
                 keys = []
@@ -888,7 +963,8 @@
                     if isNew:
                         objectsToWrite.append(value)
                 for objRef in objectsToWrite:
-                    output = self.writeObject(objRef, output, setReferencePosition=True)
+                    output = self.writeObject(
+                        objRef, output, setReferencePosition=True)
         return output
     
     def writeOffsetTable(self, output):
@@ -897,6 +973,8 @@
         
         @param output current output (bytes)
         @return new output (bytes)
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         all_positions = []
         writtenReferences = list(self.writtenReferences.items())
@@ -929,6 +1007,8 @@
         @param obj integer to be packed
         @param bytes length the integer should be packed into (integer)
         @return serialized object (bytes)
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         result = ''
         if bytes is None:
@@ -943,7 +1023,8 @@
             result += pack('>q', obj)
         else:
             raise InvalidPlistException(
-                "Core Foundation can't handle integers with size greater than 8 bytes.")
+                "Core Foundation can't handle integers with size greater"
+                " than 8 bytes.")
         return result
     
     def intSize(self, obj):
@@ -953,6 +1034,8 @@
         
         @param obj integer object
         @return number of bytes required (integer)
+        @exception InvalidPlistException raised to indicate an invalid
+            plist file
         """
         # SIGNED
         if obj < 0:  # Signed integer, always 8 bytes
@@ -970,7 +1053,8 @@
             return 8
         else:
             raise InvalidPlistException(
-                "Core Foundation can't handle integers with size greater than 8 bytes.")
+                "Core Foundation can't handle integers with size greater"
+                " than 8 bytes.")
     
     def realSize(self, obj):
         """

eric ide

mercurial