Hello community,

here is the log from the commit of package python-biplist for openSUSE:Factory 
checked in at 2017-09-04 12:31:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-biplist (Old)
 and      /work/SRC/openSUSE:Factory/.python-biplist.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-biplist"

Mon Sep  4 12:31:28 2017 rev:10 rq:519841 version:1.0.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-biplist/python-biplist.changes    
2015-04-27 13:05:35.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-biplist.new/python-biplist.changes       
2017-09-04 12:31:31.036051169 +0200
@@ -1,0 +2,21 @@
+Thu Aug 31 06:07:29 UTC 2017 - toddrme2...@gmail.com
+
+- Update to version 1.0.2
+  * Sort sets and dictionaries by key when writing.
+- Update to version 1.0.1
+  * Adding back in Python 2.6 support. This will be removed again in a future 
version.
+- Update to version 1.0.0
+  * This release changes the type of `Uid` from a subclass of `int` to a 
subclass of `object`.
+  * This change was made to address GitHub issue Ints are being turned into 
Uids and vice versa when both are present in a plist.
+  * This release also bumps the minimum supported Python versions to 2.7 and 
3.4.
+- Update to version 0.9.1
+  * Fixes GitHub issue ERROR: testLargeDates (test_valid.TestValidPlistFile)
+  * Fixes Empty Data object converted as empty string
+  * Creates 1-byte strings when possible
+
+-------------------------------------------------------------------
+Thu Aug 24 13:33:20 UTC 2017 - jmate...@suse.com
+
+- singlespec auto-conversion
+
+-------------------------------------------------------------------
@@ -38,0 +60 @@
+

Old:
----
  biplist-0.9.tar.gz

New:
----
  biplist-1.0.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-biplist.spec ++++++
--- /var/tmp/diff_new_pack.zuKIGu/_old  2017-09-04 12:31:32.275876850 +0200
+++ /var/tmp/diff_new_pack.zuKIGu/_new  2017-09-04 12:31:32.283875725 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-biplist
 #
-# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -15,26 +15,31 @@
 # Please submit bugfixes or comments via http://bugs.opensuse.org/
 #
 
+%ifarch x86_64
+%bcond_without  test
+%else
+%bcond_with     test
+%endif
 
+%{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-biplist
-Version:        0.9
+Version:        1.0.2
 Release:        0
-Url:            https://bitbucket.org/wooster/biplist
 Summary:        A library for reading/writing binary plists
 License:        BSD-3-Clause
 Group:          Development/Languages/Python
-Source:         
http://pypi.python.org/packages/source/b/biplist/biplist-%{version}.tar.gz
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
+Url:            https://bitbucket.org/wooster/biplist
+Source:         
https://files.pythonhosted.org/packages/source/b/biplist/biplist-%{version}.tar.gz
+BuildRequires:  %{python_module devel}
+BuildRequires:  %{python_module setuptools}
 BuildRequires:  fdupes
-BuildRequires:  python-coverage
-BuildRequires:  python-devel >= 2.7
-BuildRequires:  python-nose
-BuildRequires:  python-setuptools
-%if 0%{?suse_version} && 0%{?suse_version} <= 1110
-%{!?python_sitelib: %global python_sitelib %(python -c "from 
distutils.sysconfig import get_python_lib; print get_python_lib()")}
-%else
-BuildArch:      noarch
+BuildRequires:  python-rpm-macros
+%if %{with test}
+BuildRequires:  %{python_module coverage}
+BuildRequires:  %{python_module nose}
 %endif
+BuildArch:      noarch
+%python_subpackages
 
 %description
 biplist is a binary plist parser/generator for Python.
@@ -47,16 +52,18 @@
 %setup -q -n biplist-%{version}
 
 %build
-python setup.py build
+%python_build
 
 %install
-python setup.py install --prefix=%{_prefix} --root=%{buildroot}
-%fdupes %buildroot/%_prefix
+%python_install
+%python_expand %fdupes %{buildroot}%{$python_sitelib}
 
+%if %{with test}
 %check
-python setup.py -q test
+%python_exec setup.py test
+%endif
 
-%files
+%files %{python_files}
 %defattr(-,root,root,-)
 %doc AUTHORS LICENSE README.md
 %{python_sitelib}/*

++++++ biplist-0.9.tar.gz -> biplist-1.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/biplist-0.9/PKG-INFO new/biplist-1.0.2/PKG-INFO
--- old/biplist-0.9/PKG-INFO    2014-10-26 20:09:20.000000000 +0100
+++ new/biplist-1.0.2/PKG-INFO  2017-05-11 01:02:57.000000000 +0200
@@ -1,19 +1,19 @@
 Metadata-Version: 1.1
 Name: biplist
-Version: 0.9
+Version: 1.0.2
 Summary: biplist is a library for reading/writing binary plists.
 Home-page: https://bitbucket.org/wooster/biplist
 Author: Andrew Wooster
 Author-email: and...@planetaryscale.com
 License: BSD
-Download-URL: 
https://bitbucket.org/wooster/biplist/downloads/biplist-0.9.tar.gz
+Download-URL: 
https://bitbucket.org/wooster/biplist/downloads/biplist-1.0.2.tar.gz
 Description: `biplist` is a binary plist parser/generator for Python.
         
         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
         plists which can be read by OS X, iOS, or other clients.
         
-        This module requires Python 2.6 or higher or Python 3.2 or higher.
+        This module requires Python 2.6 or higher or Python 3.4 or higher.
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/biplist-0.9/biplist/__init__.py 
new/biplist-1.0.2/biplist/__init__.py
--- old/biplist-0.9/biplist/__init__.py 2014-10-26 20:03:11.000000000 +0100
+++ new/biplist-1.0.2/biplist/__init__.py       2017-05-11 00:53:56.000000000 
+0200
@@ -44,13 +44,12 @@
         print "Not a plist:", e
 """
 
-import sys
 from collections import namedtuple
 import datetime
 import io
 import math
 import plistlib
-from struct import pack, unpack
+from struct import pack, unpack, unpack_from
 from struct import error as struct_error
 import sys
 import time
@@ -79,23 +78,41 @@
 # Apple uses Jan 1, 2001 as a base for all plist date/times.
 apple_reference_date = datetime.datetime.utcfromtimestamp(978307200)
 
-class Uid(int):
+class Uid(object):
     """Wrapper around integers for representing UID values. This
        is used in keyed archiving."""
+    integer = 0
+    def __init__(self, integer):
+        self.integer = integer
+    
     def __repr__(self):
-        return "Uid(%d)" % self
+        return "Uid(%d)" % self.integer
+    
+    def __eq__(self, other):
+        if isinstance(self, Uid) and isinstance(other, Uid):
+            return self.integer == other.integer
+        return False
+    
+    def __cmp__(self, other):
+        return self.integer - other.integer
+    
+    def __lt__(self, other):
+        return self.integer < other.integer
+    
+    def __hash__(self):
+        return self.integer
+    
+    def __int__(self):
+        return int(self.integer)
 
 class Data(bytes):
-    """Wrapper around str types for representing Data values."""
-    pass
+    """Wrapper around bytes to distinguish Data values."""
 
 class InvalidPlistException(Exception):
     """Raised when the plist is incorrectly formatted."""
-    pass
 
 class NotBinaryPlistException(Exception):
     """Raised when a binary plist was expected but not encountered."""
-    pass
 
 def readPlist(pathOrFile):
     """Raises NotBinaryPlistException, InvalidPlistException"""
@@ -379,7 +396,7 @@
     def readAsciiString(self, length):
         result = unpack("!%ds" % length, 
self.contents[self.currentOffset:self.currentOffset+length])[0]
         self.currentOffset += length
-        return result
+        return str(result.decode('ascii'))
     
     def readUnicode(self, length):
         actual_length = length*2
@@ -426,7 +443,9 @@
                 result = int.from_bytes(data, 'big')
             else:
                 for byte in data:
-                    result = (result << 8) | unpack('>B', byte)[0]
+                    if not isinstance(byte, int): # Python3.0-3.1.x return 
ints, 2.x return str
+                        byte = unpack_from('>B', byte)[0]
+                    result = (result << 8) | byte
         else:
             raise InvalidPlistException("Encountered integer longer than 16 
bytes.")
         return result
@@ -456,6 +475,51 @@
     def __repr__(self):
         return "<FloatWrapper: %s>" % self.value
 
+class StringWrapper(object):
+    __instances = {}
+    
+    encodedValue = None
+    encoding = None
+    
+    def __new__(cls, value):
+        '''Ensure we only have a only one instance for any string,
+         and that we encode ascii as 1-byte-per character when possible'''
+        
+        encodedValue = None
+        
+        for encoding in ('ascii', 'utf_16_be'):
+            try:
+               encodedValue = value.encode(encoding)
+            except: pass
+            if encodedValue is not None:
+                if encodedValue not in cls.__instances:
+                    cls.__instances[encodedValue] = super(StringWrapper, 
cls).__new__(cls)
+                    cls.__instances[encodedValue].encodedValue = encodedValue
+                    cls.__instances[encodedValue].encoding = encoding
+                return cls.__instances[encodedValue]
+        
+        raise ValueError('Unable to get ascii or utf_16_be encoding for %s' % 
repr(value))
+    
+    def __len__(self):
+        '''Return roughly the number of characters in this string (half the 
byte length)'''
+        if self.encoding == 'ascii':
+            return len(self.encodedValue)
+        else:
+            return len(self.encodedValue)//2
+    
+    def __lt__(self, other):
+        return self.encodedValue < other.encodedValue
+    
+    @property
+    def encodingMarker(self):
+        if self.encoding == 'ascii':
+            return 0b0101
+        else:
+            return 0b0110
+    
+    def __repr__(self):
+        return '<StringWrapper (%s): %s>' % (self.encoding, self.encodedValue)
+
 class PlistWriter(object):
     header = b'bplist00bybiplist1.0'
     file = None
@@ -507,10 +571,9 @@
         """
         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)
+        self.computeOffsets(wrapped_root, asReference=True, isRoot=True)
         self.trailer = 
self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))})
-        (_, output) = self.writeObjectReference(wrapped_root, output)
+        self.writeObjectReference(wrapped_root, output)
         output = self.writeObject(wrapped_root, output, 
setReferencePosition=True)
         
         # output size at this point is an upper bound on how big the
@@ -552,6 +615,10 @@
         elif isinstance(root, tuple):
             n = tuple([self.wrapRoot(value) for value in root])
             return HashableWrapper(n)
+        elif isinstance(root, (str, unicode)) and not isinstance(root, Data):
+            return StringWrapper(root)
+        elif isinstance(root, bytes):
+            return Data(root)
         else:
             return root
 
@@ -564,7 +631,7 @@
                 raise InvalidPlistException('Dictionary keys cannot be null in 
plists.')
             elif isinstance(key, Data):
                 raise InvalidPlistException('Data cannot be dictionary keys in 
plists.')
-            elif not isinstance(key, (bytes, unicode)):
+            elif not isinstance(key, StringWrapper):
                 raise InvalidPlistException('Keys must be strings.')
         
         def proc_size(size):
@@ -584,7 +651,7 @@
         elif isinstance(obj, BoolWrapper):
             self.incrementByteCount('boolBytes')
         elif isinstance(obj, Uid):
-            size = self.intSize(obj)
+            size = self.intSize(obj.integer)
             self.incrementByteCount('uidBytes', incr=1+size)
         elif isinstance(obj, (int, long)):
             size = self.intSize(obj)
@@ -597,7 +664,7 @@
         elif isinstance(obj, Data):
             size = proc_size(len(obj))
             self.incrementByteCount('dataBytes', incr=1+size)
-        elif isinstance(obj, (unicode, bytes)):
+        elif isinstance(obj, StringWrapper):
             size = proc_size(len(obj))
             self.incrementByteCount('stringBytes', incr=1+size)
         elif isinstance(obj, HashableWrapper):
@@ -621,7 +688,7 @@
                     self.computeOffsets(key, asReference=True)
                     self.computeOffsets(value, asReference=True)
         else:
-            raise InvalidPlistException("Unknown object type.")
+            raise InvalidPlistException("Unknown object type: %s (%s)" % 
(type(obj).__name__, repr(obj)))
 
     def writeObjectReference(self, obj, output):
         """Tries to write an object reference, adding it to the references
@@ -653,9 +720,10 @@
                 result += pack('!B', (format << 4) | length)
             return result
         
-        if isinstance(obj, (str, unicode)) and obj == unicodeEmpty:
-            # The Apple Plist decoder can't decode a zero length Unicode 
string.
-            obj = b''
+        def timedelta_total_seconds(td):
+            # Shim for Python 2.6 compatibility, which doesn't have 
total_seconds.
+            # Make one argument a float to ensure the right calculation.
+            return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 
10.0**6) / 10.0**6
        
         if setReferencePosition:
             self.referencePositions[obj] = len(output)
@@ -668,9 +736,9 @@
             else:
                 output += pack('!B', 0b00001001)
         elif isinstance(obj, Uid):
-            size = self.intSize(obj)
+            size = self.intSize(obj.integer)
             output += pack('!B', (0b1000 << 4) | size - 1)
-            output += self.binaryInt(obj)
+            output += self.binaryInt(obj.integer)
         elif isinstance(obj, (int, long)):
             byteSize = self.intSize(obj)
             root = math.log(byteSize, 2)
@@ -681,16 +749,18 @@
             output += pack('!B', (0b0010 << 4) | 3)
             output += self.binaryReal(obj)
         elif isinstance(obj, datetime.datetime):
-            timestamp = (obj - apple_reference_date).total_seconds()
+            try:
+                timestamp = (obj - apple_reference_date).total_seconds()
+            except AttributeError:
+                timestamp = timedelta_total_seconds(obj - apple_reference_date)
             output += pack('!B', 0b00110011)
             output += pack('!d', float(timestamp))
         elif isinstance(obj, Data):
             output += proc_variable_length(0b0100, len(obj))
             output += obj
-        elif isinstance(obj, unicode):
-            byteData = obj.encode('utf_16_be')
-            output += proc_variable_length(0b0110, len(byteData)//2)
-            output += byteData
+        elif isinstance(obj, StringWrapper):
+            output += proc_variable_length(obj.encodingMarker, len(obj))
+            output += obj.encodedValue
         elif isinstance(obj, bytes):
             output += proc_variable_length(0b0101, len(obj))
             output += obj
@@ -703,7 +773,7 @@
                     output += proc_variable_length(0b1010, len(obj))
             
                 objectsToWrite = []
-                for objRef in obj:
+                for objRef in sorted(obj) if isinstance(obj, set) else obj:
                     (isNew, output) = self.writeObjectReference(objRef, output)
                     if isNew:
                         objectsToWrite.append(objRef)
@@ -714,7 +784,7 @@
                 keys = []
                 values = []
                 objectsToWrite = []
-                for key, value in iteritems(obj):
+                for key, value in sorted(iteritems(obj)):
                     keys.append(key)
                     values.append(value)
                 for key in keys:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/biplist-0.9/biplist.egg-info/PKG-INFO 
new/biplist-1.0.2/biplist.egg-info/PKG-INFO
--- old/biplist-0.9/biplist.egg-info/PKG-INFO   2014-10-26 20:09:20.000000000 
+0100
+++ new/biplist-1.0.2/biplist.egg-info/PKG-INFO 2017-05-11 01:02:57.000000000 
+0200
@@ -1,19 +1,19 @@
 Metadata-Version: 1.1
 Name: biplist
-Version: 0.9
+Version: 1.0.2
 Summary: biplist is a library for reading/writing binary plists.
 Home-page: https://bitbucket.org/wooster/biplist
 Author: Andrew Wooster
 Author-email: and...@planetaryscale.com
 License: BSD
-Download-URL: 
https://bitbucket.org/wooster/biplist/downloads/biplist-0.9.tar.gz
+Download-URL: 
https://bitbucket.org/wooster/biplist/downloads/biplist-1.0.2.tar.gz
 Description: `biplist` is a binary plist parser/generator for Python.
         
         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
         plists which can be read by OS X, iOS, or other clients.
         
-        This module requires Python 2.6 or higher or Python 3.2 or higher.
+        This module requires Python 2.6 or higher or Python 3.4 or higher.
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Intended Audience :: Developers
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/biplist-0.9/setup.py new/biplist-1.0.2/setup.py
--- old/biplist-0.9/setup.py    2014-10-26 20:04:50.000000000 +0100
+++ new/biplist-1.0.2/setup.py  2017-05-11 00:55:28.000000000 +0200
@@ -12,21 +12,21 @@
 
 major, minor, micro, releaselevel, serial = sys.version_info
 
-if major <= 1 or (major == 2 and minor < 6) or (major == 3 and minor < 2):
+if major <= 1 or (major == 2 and minor < 6) or (major == 3 and minor < 4):
     # N.B.: Haven't tested with older py3k versions.
-    print('This module supports Python 2 >= 2.6 and Python 3 >= 3.2.')
+    print('This module supports Python 2 >= 2.6 and Python 3 >= 3.4.')
     sys.exit(1)
 
 author = 'Andrew Wooster'
 email = 'and...@planetaryscale.com'
-version = '0.9'
+version = '1.0.2'
 desc = 'biplist is a library for reading/writing binary plists.'
 
 setup(
     name = 'biplist',
     version = version,
     url = 'https://bitbucket.org/wooster/biplist',
-    download_url = 
'https://bitbucket.org/wooster/biplist/downloads/biplist-0.9.tar.gz',
+    download_url = 
'https://bitbucket.org/wooster/biplist/downloads/biplist-%s.tar.gz' % version,
     license = 'BSD',
     description = desc,
     long_description = 
@@ -36,7 +36,7 @@
 format for property lists on OS X. This is a library for generating binary
 plists which can be read by OS X, iOS, or other clients.
 
-This module requires Python 2.6 or higher or Python 3.2 or higher.""",
+This module requires Python 2.6 or higher or Python 3.4 or higher.""",
     author = author,
     author_email = email,
     packages = find_packages(),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/biplist-0.9/tests/test_valid.py 
new/biplist-1.0.2/tests/test_valid.py
--- old/biplist-0.9/tests/test_valid.py 2014-10-26 20:03:33.000000000 +0100
+++ new/biplist-1.0.2/tests/test_valid.py       2016-06-18 01:44:56.000000000 
+0200
@@ -19,19 +19,19 @@
     
     def validateSimpleBinaryRoot(self, root):
         self.assertTrue(type(root) == dict, "Root should be dictionary.")
-        self.assertTrue(type(root[b'dateItem']) == datetime.datetime, "date 
should be datetime")
-        us = root[b'dateItem'].microsecond
+        self.assertTrue(type(root['dateItem']) == datetime.datetime, "date 
should be datetime")
+        us = root['dateItem'].microsecond
         if us == 385448:
             # Python 3 doesn't round microseconds to the nearest value.
-            self.assertEqual(root[b'dateItem'], datetime.datetime(2010, 8, 19, 
22, 27, 30, 385448), "dates not equal" )
+            self.assertEqual(root['dateItem'], datetime.datetime(2010, 8, 19, 
22, 27, 30, 385448), "dates not equal" )
         else:
-            self.assertEqual(root[b'dateItem'], datetime.datetime(2010, 8, 19, 
22, 27, 30, 385449), "dates not equal" )
-        self.assertEqual(root[b'numberItem'], -10000000000000000, "number not 
of expected value")
-        self.assertEqual(root[b'unicodeItem'], toUnicode('abc\u212cdef\u2133'))
-        self.assertEqual(root[b'stringItem'], b'Hi there')
-        self.assertEqual(root[b'realItem'], 0.47)
-        self.assertEqual(root[b'boolItem'], True)
-        self.assertEqual(root[b'arrayItem'], [b'item0'])
+            self.assertEqual(root['dateItem'], datetime.datetime(2010, 8, 19, 
22, 27, 30, 385449), "dates not equal" )
+        self.assertEqual(root['numberItem'], -10000000000000000, "number not 
of expected value")
+        self.assertEqual(root['unicodeItem'], toUnicode('abc\u212cdef\u2133'))
+        self.assertEqual(root['stringItem'], 'Hi there')
+        self.assertEqual(root['realItem'], 0.47)
+        self.assertEqual(root['boolItem'], True)
+        self.assertEqual(root['arrayItem'], ['item0'])
         
     def testFileRead(self):
         try:
@@ -55,17 +55,17 @@
         # 0b0101 (ASCII string), so the value being asserted against
         # appears to be what is wrong.
         result = readPlist(data_path('unicode_empty.plist'))
-        self.assertEqual(result, b'')
+        self.assertEqual(result, '')
     
     def testSmallReal(self):
         result = readPlist(data_path('small_real.plist'))
-        self.assertEqual(result, {b'4 byte real':0.5})
+        self.assertEqual(result, {'4 byte real':0.5})
     
     def testLargeIntegers(self):
         result = readPlist(data_path('large_int_limits.plist'))
-        self.assertEqual(result[b'Max 8 Byte Unsigned Integer'], 
18446744073709551615)
-        self.assertEqual(result[b'Min 8 Byte Signed Integer'], 
-9223372036854775808)
-        self.assertEqual(result[b'Max 8 Byte Signed Integer'], 
9223372036854775807)
+        self.assertEqual(result['Max 8 Byte Unsigned Integer'], 
18446744073709551615)
+        self.assertEqual(result['Min 8 Byte Signed Integer'], 
-9223372036854775808)
+        self.assertEqual(result['Max 8 Byte Signed Integer'], 
9223372036854775807)
     
     def testLargeDates(self):
         result = readPlist(data_path("BFPersistentEventInfo.plist"))
@@ -86,15 +86,31 @@
         ...
         """
         result = readPlist(data_path('nskeyedarchiver_example.plist'))
-        self.assertEqual(result, {b'$version': 100000, 
-            b'$objects': 
-                [b'$null',
-                 {b'$class': Uid(3), b'somekey': Uid(2)}, 
-                 b'object value as string',
-                 {b'$classes': [b'Archived', b'NSObject'], b'$classname': 
b'Archived'}
-                 ], 
-            b'$top': {b'root': Uid(1)}, b'$archiver': b'NSKeyedArchiver'})
+        self.assertEqual(result, {
+            '$version': 100000, 
+            '$objects':
+                [
+                       '$null',
+                       {'$class':Uid(3), 'somekey':Uid(2)}, 
+                       'object value as string',
+                       {'$classes':['Archived', 'NSObject'], 
'$classname':'Archived'}
+                ],
+            '$top': {'root':Uid(1)},
+            '$archiver':'NSKeyedArchiver'
+        })
         self.assertEqual("Uid(1)", repr(Uid(1)))
     
+    def testUidComparisons(self):
+        self.assertTrue(Uid(-2) < Uid(-1))
+        self.assertTrue(Uid(-1) < Uid(0))
+        self.assertTrue(Uid(1) > Uid(0))
+        self.assertTrue(Uid(1) > Uid(-2))
+        self.assertTrue(Uid(-1) == Uid(-1))
+        self.assertTrue(Uid(0) == Uid(0))
+        self.assertTrue(Uid(1) == Uid(1))
+        
+        self.assertFalse(1 == Uid(1))
+        self.assertFalse(Uid(0) == 0)
+    
 if __name__ == '__main__':
     unittest.main()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/biplist-0.9/tests/test_write.py 
new/biplist-1.0.2/tests/test_write.py
--- old/biplist-0.9/tests/test_write.py 2014-10-26 20:03:11.000000000 +0100
+++ new/biplist-1.0.2/tests/test_write.py       2016-06-18 01:44:56.000000000 
+0200
@@ -1,39 +1,82 @@
+#!/usr/local/env python
+# -*- coding: utf-8 -*-
+
+import datetime, io, os, subprocess, sys, tempfile, unittest
+
 from biplist import *
 from biplist import PlistWriter
-import datetime
-import io
-import os
-#from cStringIO import StringIO
-import subprocess
-import tempfile
 from test_utils import *
-import unittest
 
 try:
     unicode
+    unicodeStr = lambda x: x.decode('utf-8')
+    toUnicode = lambda x: x.decode('unicode-escape')
 except NameError:
     unicode = str
+    unicodeStr = lambda x: x
+    toUnicode = lambda x: x
+try:
+    xrange
+except NameError:
+    xrange = range
 
 class TestWritePlist(unittest.TestCase):
-    def setUp(self):
-        pass
     
-    def roundTrip(self, root, xml=False, expected=None):
-        # 'expected' is more fallout from the
-        # don't-write-empty-unicode-strings issue.
-        plist = writePlistToString(root, binary=(not xml))
+    def roundTrip(self, case, xml=False, expected=None, reprTest=True):
+        # reprTest may fail randomly if True and the values being encoded 
include a dictionary with more
+        # than one key.
+        
+        # convert to plist string
+        plist = writePlistToString(case, binary=(not xml))
         self.assertTrue(len(plist) > 0)
+        
+        # confirm that lint is happy with the result
+        self.lintPlist(plist)        
+        
+        # convert back
         readResult = readPlistFromString(plist)
-        self.assertEqual(readResult, (expected if expected is not None else 
root))
-        self.lintPlist(plist)
-    
-    def lintPlist(self, plistString):
-        if os.path.exists('/usr/bin/plutil'):
-            f = tempfile.NamedTemporaryFile()
-            f.write(plistString)
-            f.flush()
-            name = f.name
-            (status, output) = run_command(['/usr/bin/plutil', '-lint', name])
+        
+        # test equality
+        if reprTest is True:
+            self.assertEqual(repr(case if expected is None else expected), 
repr(readResult))
+        else:
+            self.assertEqual((case if expected is None else expected), 
readResult)
+        
+        # write to file
+        plistFile = tempfile.NamedTemporaryFile(mode='wb+', suffix='.plist')
+        writePlist(case, plistFile, binary=(xml is False))
+        plistFile.seek(0)
+        
+        # confirm that lint is happy with the result
+        self.lintPlist(plistFile)
+        
+        # read back from file
+        fileResult = readPlist(plistFile)
+        
+        # test equality
+        if reprTest is True:
+            self.assertEqual(repr(case if expected is None else expected), 
repr(fileResult))
+        else:
+            self.assertEqual((case if expected is None else expected), 
fileResult)
+    
+    def lintPlist(self, plist):
+        if os.access('/usr/bin/plutil', os.X_OK):
+            plistFile = None
+            plistFilePath = None
+            
+            if hasattr(plist, 'name'):
+                plistFilePath = plist.name
+            else:
+                if hasattr(plist, 'read'):
+                    plistFile = tempfile.NamedTemporaryFile('w%s' % ('b' if 
'b' in plist.mode else ''))
+                    plistFile.write(plist.read())
+                else:
+                    plistFile = tempfile.NamedTemporaryFile('w%s' % ('b' if 
isinstance(plist, bytes) else ''))
+                    plistFile.write(plist)
+                plistFilePath = plistFile.name
+                plistFile.flush()
+
+            status, output = run_command(['/usr/bin/plutil', '-lint', 
plistFilePath])
             if status != 0:
                 self.fail("plutil verification failed (status %d): %s" % 
(status, output))
     
@@ -58,36 +101,32 @@
         self.roundTrip(False)
     
     def testDuplicate(self):
-        l = ["foo" for i in range(0, 100)]
+        l = ["foo" for i in xrange(0, 100)]
         self.roundTrip(l)
         
     def testListRoot(self):
         self.roundTrip([1, 2, 3])
     
     def testDictRoot(self):
-        self.roundTrip({'a':1, 'B':'d'})
+        self.roundTrip({'a':1, 'B':'d'}, reprTest=False)
     
     def mixedNumericTypesHelper(self, cases):
         result = readPlistFromString(writePlistToString(cases))
-        for i in range(0, len(cases)):
+        for i in xrange(0, len(cases)):
             self.assertTrue(cases[i] == result[i])
             self.assertEqual(type(cases[i]), type(result[i]), "Type mismatch 
on %d: %s != %s" % (i, repr(cases[i]), repr(result[i])))
     
-    def reprChecker(self, case):
-        result = readPlistFromString(writePlistToString(case))
-        self.assertEqual(repr(case), repr(result))
-    
     def testBoolsAndIntegersMixed(self):
         self.mixedNumericTypesHelper([0, 1, True, False, None])
         self.mixedNumericTypesHelper([False, True, 0, 1, None])
-        self.reprChecker({unicode('1'):[True, False, 1, 0], unicode('0'):[1, 
2, 0, {unicode('2'):[1, 0, False]}]})
-        self.reprChecker([1, 1, 1, 1, 1, True, True, True, True])
+        self.roundTrip({'1':[True, False, 1, 0], '0':[1, 2, 0, {'2':[1, 0, 
False]}]}, reprTest=False)
+        self.roundTrip([1, 1, 1, 1, 1, True, True, True, True])
     
     def testFloatsAndIntegersMixed(self):
         self.mixedNumericTypesHelper([0, 1, 1.0, 0.0, None])
         self.mixedNumericTypesHelper([0.0, 1.0, 0, 1, None])
-        self.reprChecker({unicode('1'):[1.0, 0.0, 1, 0], unicode('0'):[1, 2, 
0, {unicode('2'):[1, 0, 0.0]}]})
-        self.reprChecker([1, 1, 1, 1, 1, 1.0, 1.0, 1.0, 1.0])
+        self.roundTrip({'1':[1.0, 0.0, 1, 0], '0':[1, 2, 0, {'2':[1, 0, 
0.0]}]}, reprTest=False)
+        self.roundTrip([1, 1, 1, 1, 1, 1.0, 1.0, 1.0, 1.0])
     
     def testSetRoot(self):
         self.roundTrip(set((1, 2, 3)))
@@ -112,36 +151,102 @@
         self.lintPlist(writePlistToString(root))
         self.roundTrip(root)
     
-    def testString(self):
+    def testBytes(self):
         self.roundTrip(b'0')
         self.roundTrip(b'')
-        self.roundTrip({b'a':b''})
+        
+        self.roundTrip([b'0'])
+        self.roundTrip([b''])
+        
+        self.roundTrip({'a': b'0'})
+        self.roundTrip({'a': b''})
     
-    def testLargeDict(self):
-        d = {}
-        for i in range(0, 1000):
-            d['%d' % i] = '%d' % i
-        self.roundTrip(d)
+    def testString(self):
+        self.roundTrip('')
+        self.roundTrip('a')
+        self.roundTrip('1')
+        
+        self.roundTrip([''])
+        self.roundTrip(['a'])
+        self.roundTrip(['1'])
+        
+        self.roundTrip({'a':''})
+        self.roundTrip({'a':'a'})
+        self.roundTrip({'1':'a'})
         
+        self.roundTrip({'a':'a'})
+        self.roundTrip({'a':'1'})
+    
+    def testUnicode(self):
+        # defaulting to 1 byte strings
+        if str != unicode:
+            self.roundTrip(unicodeStr(r''), expected='')
+            self.roundTrip(unicodeStr(r'a'), expected='a')
+            
+            self.roundTrip([unicodeStr(r'a')], expected=['a'])
+            
+            self.roundTrip({'a':unicodeStr(r'a')}, expected={'a':'a'})
+            self.roundTrip({unicodeStr(r'a'):'a'}, expected={'a':'a'})
+            self.roundTrip({unicodeStr(r''):unicodeStr(r'')}, expected={'':''})
+        
+        # TODO: need a 4-byte unicode character
+        self.roundTrip(unicodeStr(r'ü'))
+        self.roundTrip([unicodeStr(r'ü')])
+        self.roundTrip({'a':unicodeStr(r'ü')})
+        self.roundTrip({unicodeStr(r'ü'):'a'})
+        
+        self.roundTrip(toUnicode('\u00b6'))
+        self.roundTrip([toUnicode('\u00b6')])
+        self.roundTrip({toUnicode('\u00b6'):toUnicode('\u00b6')})
+        
+        self.roundTrip(toUnicode('\u1D161'))
+        self.roundTrip([toUnicode('\u1D161')])
+        self.roundTrip({toUnicode('\u1D161'):toUnicode('\u1D161')})
+        
+        # Smiley face emoji
+        self.roundTrip(toUnicode('\U0001f604'))
+        self.roundTrip([toUnicode('\U0001f604'), toUnicode('\U0001f604')])
+        self.roundTrip({toUnicode('\U0001f604'):toUnicode('\U0001f604')})
+    
+    def testNone(self):
+        self.roundTrip(None)
+        self.roundTrip({'1':None})
+        self.roundTrip([None, None, None])
+    
     def testBools(self):
+        self.roundTrip(True)
+        self.roundTrip(False)
+        
         self.roundTrip([True, False])
+        
+        self.roundTrip({'a':True, 'b':False}, reprTest=False)
     
     def testUniques(self):
         root = {'hi':'there', 'halloo':'there'}
-        self.roundTrip(root)
+        self.roundTrip(root, reprTest=False)
+    
+    def testAllEmpties(self):
+        '''Primarily testint that an empty unicode and bytes are not mixed 
up'''
+        self.roundTrip([unicodeStr(''), '', b'', [], {}], expected=['', '', 
b'', [], {}])
     
+    def testLargeDict(self):
+        d = dict((str(x), str(x)) for x in xrange(0, 1000))
+        self.roundTrip(d, reprTest=False)
+        
     def testWriteToFile(self):
         for is_binary in [True, False]:
-            path = '/var/tmp/test.plist'
-            writePlist([1, 2, 3], path, binary=is_binary)
-            self.assertTrue(os.path.exists(path))
-            with open(path, 'rb') as f:
-                self.lintPlist(f.read())
-    
-    def testNone(self):
-        self.roundTrip(None)
-        self.roundTrip({'1':None})
-        self.roundTrip([None, None, None])
+            with tempfile.NamedTemporaryFile(mode='w%s' % ('b' if is_binary 
else ''), suffix='.plist') as plistFile:
+                # clear out the created file
+                os.unlink(plistFile.name)
+                self.assertFalse(os.path.exists(plistFile.name))
+                
+                # write to disk
+                writePlist([1, 2, 3], plistFile.name, binary=is_binary)
+                self.assertTrue(os.path.exists(plistFile.name))
+                
+                with open(plistFile.name, 'r%s' % ('b' if is_binary else '')) 
as f:
+                    fileContents = f.read()
+                    self.lintPlist(fileContents)
     
     def testBadKeys(self):
         try:
@@ -169,7 +274,7 @@
                  -pow(2, 15), pow(2, 15) - 1, 
                  -pow(2, 31), pow(2, 31) - 1, 
                  -pow(2, 63), pow(2, 64) - 1]
-        self.roundTrip(edges)
+        self.roundTrip(edges, reprTest=False)
         
         ioBytes = io.BytesIO()
         writer = PlistWriter(ioBytes)
@@ -185,7 +290,7 @@
                 self.assertEqual(bytelen, got, "Byte size is wrong. Expected 
%d, got %d" % (bytelen, got))
         
         bytes_lists = [list(x) for x in bytes]
-        self.roundTrip(bytes_lists)
+        self.roundTrip(bytes_lists, reprTest=False)
         
         try:
             self.roundTrip([0x10000000000000000, pow(2, 64)])
@@ -193,17 +298,23 @@
         except InvalidPlistException as e:
             pass
     
-    def testWriteData(self):
-        self.roundTrip(Data(b"woohoo"))
-        
-    def testUnicode(self):
-        unicodeRoot = unicode("Mirror's Edge\u2122 for iPad")
-        writePlist(unicodeRoot, "/tmp/odd.plist")
+    def testUnicode2(self):
+        unicodeRoot = toUnicode("Mirror's Edge\u2122 for iPad")
         self.roundTrip(unicodeRoot)
-        unicodeStrings = [unicode("Mirror's Edge\u2122 for iPad"), 
unicode('Weightbot \u2014 Track your Weight in Style')]
+        unicodeStrings = [toUnicode("Mirror's Edge\u2122 for iPad"), 
toUnicode('Weightbot \u2014 Track your Weight in Style')]
         self.roundTrip(unicodeStrings)
-        self.roundTrip({unicode(""):unicode("")}, expected={b'':b''})
-        self.roundTrip(unicode(""), expected=b'')
+        self.roundTrip({toUnicode(""):toUnicode("")}, expected={'':''})
+        self.roundTrip(toUnicode(""), expected='')
+    
+    def testWriteData(self):
+        self.roundTrip(Data(b"woohoo"))
+
+    def testEmptyData(self):
+        data = Data(b'')
+        binplist = writePlistToString(data)
+        plist = readPlistFromString(binplist)
+        self.assertEqual(plist, data)
+        self.assertEqual(type(plist), type(data))
         
     def testUidWrite(self):
         self.roundTrip({'$version': 100000, 
@@ -213,7 +324,14 @@
                  'object value as string', 
                  {'$classes': ['Archived', 'NSObject'], '$classname': 
'Archived'}
                  ], 
-            '$top': {'root': Uid(1)}, '$archiver': 'NSKeyedArchiver'})
+            '$top': {'root': Uid(1)}, '$archiver': 'NSKeyedArchiver'}, 
reprTest=False)
+    
+    def testUidRoundTrip(self):
+        # Per https://github.com/wooster/biplist/issues/9
+        self.roundTrip(Uid(1))
+        self.roundTrip([Uid(1), 1])
+        self.roundTrip([1, Uid(1)])
+        self.roundTrip([Uid(1), Uid(1)])
 
 if __name__ == '__main__':
     unittest.main()


Reply via email to