Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-pykeepass for 
openSUSE:Factory checked in at 2023-06-12 15:27:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pykeepass (Old)
 and      /work/SRC/openSUSE:Factory/.python-pykeepass.new.15902 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pykeepass"

Mon Jun 12 15:27:21 2023 rev:10 rq:1092474 version:4.0.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pykeepass/python-pykeepass.changes        
2023-05-31 21:54:56.541163173 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pykeepass.new.15902/python-pykeepass.changes 
    2023-06-12 15:27:56.587581503 +0200
@@ -1,0 +2,9 @@
+Sat Jun 10 16:42:35 UTC 2023 - Atri Bhattacharya <[email protected]>
+
+- Update to version 4.0.5:
+  * [gh#libkeepass/pykeepass#344] AttributeError when accessing
+    Times with None value
+  * use __hash__ when evaluating equality
+  * use mtime/uuid for HistoryEntry hashing
+
+-------------------------------------------------------------------

Old:
----
  pykeepass-4.0.4.tar.gz

New:
----
  pykeepass-4.0.5.tar.gz

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

Other differences:
------------------
++++++ python-pykeepass.spec ++++++
--- /var/tmp/diff_new_pack.l43Ozg/_old  2023-06-12 15:27:57.203585148 +0200
+++ /var/tmp/diff_new_pack.l43Ozg/_new  2023-06-12 15:27:57.207585171 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-pykeepass
-Version:        4.0.4
+Version:        4.0.5
 Release:        0
 Summary:        Low-level library to interact with keepass databases
 License:        GPL-3.0-only

++++++ pykeepass-4.0.4.tar.gz -> pykeepass-4.0.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/CHANGELOG.rst 
new/pykeepass-4.0.5/CHANGELOG.rst
--- old/pykeepass-4.0.4/CHANGELOG.rst   2023-05-12 08:00:19.000000000 +0200
+++ new/pykeepass-4.0.5/CHANGELOG.rst   2023-06-05 23:38:19.000000000 +0200
@@ -1,4 +1,10 @@
-4.0.4 -
+4.0.5 -
+------------------
+- fixed #344 - AttributeError when accessing Times with None value
+- use __hash__ when evaluating equality
+- use mtime/uuid for HistoryEntry hashing
+
+4.0.4 - 2023-05-23
 ------------------
 - fixed #314 - correctly handle binaries with no data
 - fixed #265 - check for keepass signature
@@ -10,8 +16,8 @@
 
 4.0.3 - 2022-06-21
 ------------------
-- add otp support
-- add debug_setup() function
+- added otp support
+- added debug_setup() function
 
 4.0.2 - 2022-05-21
 ------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/README.rst 
new/pykeepass-4.0.5/README.rst
--- old/pykeepass-4.0.4/README.rst      2023-05-12 08:00:19.000000000 +0200
+++ new/pykeepass-4.0.5/README.rst      2023-06-05 23:38:19.000000000 +0200
@@ -380,7 +380,7 @@
 
 **password**
 
-string containing database password.  Can also be set.  Use ``None** for no 
password.
+string containing database password.  Can also be set.  Use ``None`` for no 
password.
 
 **filename**
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/pykeepass/baseelement.py 
new/pykeepass-4.0.5/pykeepass/baseelement.py
--- old/pykeepass-4.0.4/pykeepass/baseelement.py        2023-05-12 
08:00:19.000000000 +0200
+++ new/pykeepass-4.0.5/pykeepass/baseelement.py        2023-06-05 
23:38:19.000000000 +0200
@@ -93,7 +93,7 @@
         times = self._element.find('Times')
         if times is not None:
             prop = times.find(prop)
-            if prop is not None:
+            if prop is not None and prop.text is not None:
                 return self._kp._decode_time(prop.text)
 
     def _set_times_property(self, prop, value):
@@ -166,11 +166,13 @@
     def __repr__(self):
         return self.__str__()
 
+    def __hash__(self):
+        return hash((self.uuid,))
+
     def __eq__(self, other):
-        if hasattr(other, 'uuid'):
-            return self.uuid == other.uuid
-        else:
-            return False
+        if isinstance(other, BaseElement):
+            return hash(self) == hash(other)
+        return NotImplemented
 
     def touch(self, modify=False):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/pykeepass/entry.py 
new/pykeepass-4.0.5/pykeepass/entry.py
--- old/pykeepass-4.0.4/pykeepass/entry.py      2023-05-12 08:00:19.000000000 
+0200
+++ new/pykeepass-4.0.5/pykeepass/entry.py      2023-06-05 23:38:19.000000000 
+0200
@@ -57,7 +57,9 @@
             if notes:
                 self._element.append(E.String(E.Key('Notes'), E.Value(notes)))
             if otp:
-                self._element.append(E.String(E.Key('otp'), E.Value(otp)))
+                self._element.append(
+                    E.String(E.Key('otp'), E.Value(otp, Protected="True"))
+                )
             if tags:
                 self._element.append(
                     E.Tags(';'.join(tags) if type(tags) is list else tags)
@@ -383,6 +385,7 @@
         pathstr = super().__str__()
         return 'HistoryEntry: {}'.format(pathstr)
 
-    def __eq__(self, other):
-        # all history items share the same uuid, so examine xml directly
-        return self._element == other._element
+    def __hash__(self):
+        # All history items share the same UUID with themselves and their
+        # parent, so consider the mtime also
+        return hash((self.uuid, self.mtime))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/pykeepass/kdbx_parsing/pytwofish.py 
new/pykeepass-4.0.5/pykeepass/kdbx_parsing/pytwofish.py
--- old/pykeepass-4.0.4/pykeepass/kdbx_parsing/pytwofish.py     2023-05-12 
08:00:19.000000000 +0200
+++ new/pykeepass-4.0.5/pykeepass/kdbx_parsing/pytwofish.py     2023-06-05 
23:38:19.000000000 +0200
@@ -137,11 +137,6 @@
 #
 
 import struct
-import sys
-
-WORD_BIGENDIAN = 0
-if sys.byteorder == 'big':
-    WORD_BIGENDIAN = 1
 
 def rotr32(x, n):
     return (x >> n) | ((x << (32 - n)) & 0xFFFFFFFF)
@@ -149,10 +144,6 @@
 def rotl32(x, n):
     return ((x << n) & 0xFFFFFFFF) | (x >> (32 - n))
 
-def byteswap32(x):
-    return ((x & 0xff) << 24) | (((x >> 8) & 0xff) << 16) | \
-           (((x >> 16) & 0xff) << 8) | ((x >> 24) & 0xff)
-
 class TWI:
     def __init__(self):
         self.k_len = 0 # word32
@@ -292,14 +283,9 @@
     me_key = [0,0,0,0]
     mo_key = [0,0,0,0]
     for i in range(pkey.k_len):
-        if WORD_BIGENDIAN:
-            a = byteswap32(in_key[i + 1])
-            me_key[i] = a
-            b = byteswap32(in_key[i + i + 1])
-        else:
-            a = in_key[i + i]
-            me_key[i] = a
-            b = in_key[i + i + 1]
+        a = in_key[i + i]
+        me_key[i] = a
+        b = in_key[i + i + 1]
         mo_key[i] = b
         pkey.s_key[pkey.k_len - i - 1] = mds_rem(a, b);
     for i in range(0, 40, 2):
@@ -314,16 +300,10 @@
 def encrypt(pkey, in_blk):
     blk = [0, 0, 0, 0]
 
-    if WORD_BIGENDIAN:
-        blk[0] = byteswap32(in_blk[0]) ^ pkey.l_key[0];
-        blk[1] = byteswap32(in_blk[1]) ^ pkey.l_key[1];
-        blk[2] = byteswap32(in_blk[2]) ^ pkey.l_key[2];
-        blk[3] = byteswap32(in_blk[3]) ^ pkey.l_key[3];
-    else:
-        blk[0] = in_blk[0] ^ pkey.l_key[0];
-        blk[1] = in_blk[1] ^ pkey.l_key[1];
-        blk[2] = in_blk[2] ^ pkey.l_key[2];
-        blk[3] = in_blk[3] ^ pkey.l_key[3];
+    blk[0] = in_blk[0] ^ pkey.l_key[0];
+    blk[1] = in_blk[1] ^ pkey.l_key[1];
+    blk[2] = in_blk[2] ^ pkey.l_key[2];
+    blk[3] = in_blk[3] ^ pkey.l_key[3];
 
     for i in range(8):
         t1 = ( pkey.mk_tab[0][byte(blk[1],3)] ^ pkey.mk_tab[1][byte(blk[1],0)] 
^ pkey.mk_tab[2][byte(blk[1],1)] ^ pkey.mk_tab[3][byte(blk[1],2)] );
@@ -338,32 +318,20 @@
         blk[0] = rotr32(blk[0] ^ ((t0 + t1 + pkey.l_key[4 * (i) + 10]) % 
0x100000000), 1);
         blk[1] = rotl32(blk[1], 1) ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 11]) 
% 0x100000000);
 
-    if WORD_BIGENDIAN:
-        in_blk[0] = byteswap32(blk[2] ^ pkey.l_key[4]);
-        in_blk[1] = byteswap32(blk[3] ^ pkey.l_key[5]);
-        in_blk[2] = byteswap32(blk[0] ^ pkey.l_key[6]);
-        in_blk[3] = byteswap32(blk[1] ^ pkey.l_key[7]);
-    else:
-        in_blk[0] = blk[2] ^ pkey.l_key[4];
-        in_blk[1] = blk[3] ^ pkey.l_key[5];
-        in_blk[2] = blk[0] ^ pkey.l_key[6];
-        in_blk[3] = blk[1] ^ pkey.l_key[7];
+    in_blk[0] = blk[2] ^ pkey.l_key[4];
+    in_blk[1] = blk[3] ^ pkey.l_key[5];
+    in_blk[2] = blk[0] ^ pkey.l_key[6];
+    in_blk[3] = blk[1] ^ pkey.l_key[7];
 
     return
 
 def decrypt(pkey, in_blk):
     blk = [0, 0, 0, 0]
 
-    if WORD_BIGENDIAN:
-        blk[0] = byteswap32(in_blk[0]) ^ pkey.l_key[4];
-        blk[1] = byteswap32(in_blk[1]) ^ pkey.l_key[5];
-        blk[2] = byteswap32(in_blk[2]) ^ pkey.l_key[6];
-        blk[3] = byteswap32(in_blk[3]) ^ pkey.l_key[7];
-    else:
-        blk[0] = in_blk[0] ^ pkey.l_key[4];
-        blk[1] = in_blk[1] ^ pkey.l_key[5];
-        blk[2] = in_blk[2] ^ pkey.l_key[6];
-        blk[3] = in_blk[3] ^ pkey.l_key[7];
+    blk[0] = in_blk[0] ^ pkey.l_key[4];
+    blk[1] = in_blk[1] ^ pkey.l_key[5];
+    blk[2] = in_blk[2] ^ pkey.l_key[6];
+    blk[3] = in_blk[3] ^ pkey.l_key[7];
 
     for i in range(7, -1, -1):
         t1 = ( pkey.mk_tab[0][byte(blk[1],3)] ^ pkey.mk_tab[1][byte(blk[1],0)] 
^ pkey.mk_tab[2][byte(blk[1],1)] ^ pkey.mk_tab[3][byte(blk[1],2)] )
@@ -378,16 +346,11 @@
         blk[0] = rotl32(blk[0], 1) ^ ((t0 + t1 + pkey.l_key[4 * (i) + 8]) % 
0x100000000)
         blk[1] = rotr32(blk[1] ^ ((t0 + 2 * t1 + pkey.l_key[4 * (i) + 9]) % 
0x100000000), 1)
 
-    if WORD_BIGENDIAN:
-        in_blk[0] = byteswap32(blk[2] ^ pkey.l_key[0]);
-        in_blk[1] = byteswap32(blk[3] ^ pkey.l_key[1]);
-        in_blk[2] = byteswap32(blk[0] ^ pkey.l_key[2]);
-        in_blk[3] = byteswap32(blk[1] ^ pkey.l_key[3]);
-    else:
-        in_blk[0] = blk[2] ^ pkey.l_key[0];
-        in_blk[1] = blk[3] ^ pkey.l_key[1];
-        in_blk[2] = blk[0] ^ pkey.l_key[2];
-        in_blk[3] = blk[1] ^ pkey.l_key[3];
+    in_blk[0] = blk[2] ^ pkey.l_key[0];
+    in_blk[1] = blk[3] ^ pkey.l_key[1];
+    in_blk[2] = blk[0] ^ pkey.l_key[2];
+    in_blk[3] = blk[1] ^ pkey.l_key[3];
+
     return
 
 __testkey = 
b'\xD4\x3B\xB7\x55\x6E\xA3\x2E\x46\xF2\xA2\x82\xB7\xD4\x5B\x4E\x0D\x57\xFF\x73\x9D\x4D\xC9\x2C\x1B\xD7\xFC\x01\x70\x0C\xC8\x21\x6F'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/pykeepass/version.py 
new/pykeepass-4.0.5/pykeepass/version.py
--- old/pykeepass-4.0.4/pykeepass/version.py    2023-05-12 08:00:19.000000000 
+0200
+++ new/pykeepass-4.0.5/pykeepass/version.py    2023-06-05 23:38:19.000000000 
+0200
@@ -1,3 +1,3 @@
-__version__ = "4.0.4"
+__version__ = "4.0.5"
 
 __all__= ["__version__"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/setup.py new/pykeepass-4.0.5/setup.py
--- old/pykeepass-4.0.4/setup.py        2023-05-12 08:00:19.000000000 +0200
+++ new/pykeepass-4.0.5/setup.py        2023-06-05 23:38:19.000000000 +0200
@@ -14,6 +14,7 @@
     description="Python library to interact with keepass databases "
     "(supports KDBX3 and KDBX4)",
     long_description=README,
+    long_description_content_type='text/x-rst',
     author="Philipp Schmitt",
     author_email="[email protected]",
     url="https://github.com/libkeepass/pykeepass";,
Binary files old/pykeepass-4.0.4/tests/test3.kdbx and 
new/pykeepass-4.0.5/tests/test3.kdbx differ
Binary files old/pykeepass-4.0.4/tests/test4.kdbx and 
new/pykeepass-4.0.5/tests/test4.kdbx differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-4.0.4/tests/tests.py 
new/pykeepass-4.0.5/tests/tests.py
--- old/pykeepass-4.0.4/tests/tests.py  2023-05-12 08:00:19.000000000 +0200
+++ new/pykeepass-4.0.5/tests/tests.py  2023-06-05 23:38:19.000000000 +0200
@@ -197,6 +197,8 @@
         hist = entry.history
         self.assertIsInstance(hist, list)
         self.assertEqual(len(hist), 4)
+        self.assertEqual(len(set(hist)), 4)
+        self.assertNotEqual(hist[0], hist[1])
 
     def test_history_path(self):
         for title in ["root_entry", "subentry"]:
@@ -469,6 +471,7 @@
 
     def test_references(self):
         original_entry = self.kp.find_entries(title='foobar_entry', first=True)
+        original_entry_duplicate = self.kp.find_entries(title='foobar_entry', 
first=True)
         clone1 = self.kp.find_entries(title='foobar_entry - Clone', first=True)
         clone2 = self.kp.find_entries(title='foobar_entry - Clone of clone', 
first=True)
         prefixed = self.kp.find_entries(title='foobar_entry - Clone with 
prefix and suffix', first=True)
@@ -478,6 +481,10 @@
         self.assertEqual(original_entry.ref('username'), clone1.username)
         self.assertEqual(prefixed.deref('username'), 
'domain\\{}2'.format(original_entry.username))
         self.assertEqual(prefixed.deref('password'), 
'A{}BC'.format(original_entry.password))
+        self.assertEqual(original_entry, original_entry_duplicate)
+        self.assertEqual(hash(original_entry), hash(original_entry_duplicate))
+        self.assertNotEqual(original_entry, clone1)
+        self.assertNotEqual(clone1, clone2)
 
     def test_set_and_get_fields(self):
         time = datetime.now().replace(microsecond=0)
@@ -953,6 +960,11 @@
 
         self.assertEqual(results, results2)
 
+    def test_issue344(self):
+        # accessing expiry_time throws exception when None
+
+        e = self.kp.find_entries(title='none_date', first=True)
+        self.assertEqual(e.expiry_time, None)
 
 class EntryFindTests4(KDBX4Tests, EntryFindTests3):
     pass

Reply via email to