Hello community,

here is the log from the commit of package python-pykeepass for 
openSUSE:Factory checked in at 2020-08-05 20:28:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pykeepass (Old)
 and      /work/SRC/openSUSE:Factory/.python-pykeepass.new.3592 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-pykeepass"

Wed Aug  5 20:28:07 2020 rev:4 rq:824365 version:3.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-pykeepass/python-pykeepass.changes        
2020-05-03 22:46:21.463047654 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-pykeepass.new.3592/python-pykeepass.changes  
    2020-08-05 20:28:20.619065567 +0200
@@ -1,0 +2,15 @@
+Sat Aug  1 16:44:02 UTC 2020 - Atri Bhattacharya <[email protected]>
+
+- Update to version 3.2.1:
+  * Pin construct version to last supporting python2
+  * Hard dependency on pycryptodomex
+  * Fixed kp.groups, kp.entries not returning elements with
+    name/title None [gh#libkeepass/pykeepass#193].
+- Replace pycryptodome in BuildRequires and Requires, with
+  pycryptodomex.
+- Update version of python-construct in BuildRequires and
+  Requires.
+- Update %check section to run tests directly as recommended by
+  upstream.
+
+-------------------------------------------------------------------

Old:
----
  pykeepass-3.2.0.tar.gz

New:
----
  pykeepass-3.2.1.tar.gz

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

Other differences:
------------------
++++++ python-pykeepass.spec ++++++
--- /var/tmp/diff_new_pack.jqjNtn/_old  2020-08-05 20:28:21.599066078 +0200
+++ /var/tmp/diff_new_pack.jqjNtn/_new  2020-08-05 20:28:21.603066080 +0200
@@ -18,7 +18,7 @@
 
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 Name:           python-pykeepass
-Version:        3.2.0
+Version:        3.2.1
 Release:        0
 Summary:        Low-level library to interact with keepass databases
 License:        GPL-3.0-only
@@ -30,18 +30,19 @@
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-argon2-cffi
-Requires:       python-construct >= 2.9.31
+Requires:       python-construct >= 2.10.54
 Requires:       python-future
 Requires:       python-lxml
-Requires:       python-pycryptodome
+Requires:       python-pycryptodomex >= 3.6.2
 Requires:       python-python-dateutil
 BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module argon2-cffi}
-BuildRequires:  %{python_module construct >= 2.9.31}
+BuildRequires:  %{python_module construct >= 2.10.54}
 BuildRequires:  %{python_module future}
 BuildRequires:  %{python_module lxml}
-BuildRequires:  %{python_module pycryptodome}
+BuildRequires:  %{python_module pycryptodomex >= 3.6.2}
+BuildRequires:  %{python_module pytest}
 BuildRequires:  %{python_module python-dateutil}
 # /SECTION
 %python_subpackages
@@ -62,13 +63,14 @@
 %python_expand %fdupes %{buildroot}%{$python_sitelib}
 
 %check
-export PYTHONPATH=${PWD}
-# python2 seg faults after 65 successful tests
-python3 setup.py test
+%python_expand export PYTHONPATH=%{buildroot}%{python3_sitelib}
+export PYTHONDONTWRITEBYTECODE=1
+%python_exec tests/tests.py
 
 %files %{python_files}
 %license LICENSE
 %doc README.rst
-%{python_sitelib}/*
+%{python_sitelib}/pykeepass/
+%{python_sitelib}/pykeepass-%{version}-py%{python_version}.egg-info/
 
 %changelog

++++++ pykeepass-3.2.0.tar.gz -> pykeepass-3.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/.gitignore 
new/pykeepass-3.2.1/.gitignore
--- old/pykeepass-3.2.0/.gitignore      2020-01-19 05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/.gitignore      2020-07-20 00:47:29.000000000 +0200
@@ -5,3 +5,4 @@
 build/
 *.xml
 Pipfile.lock
+*.kdbx
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/.travis.yml 
new/pykeepass-3.2.1/.travis.yml
--- old/pykeepass-3.2.0/.travis.yml     2020-01-19 05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/.travis.yml     2020-07-20 00:47:29.000000000 +0200
@@ -3,13 +3,12 @@
 jobs:
   include:
     - python: '2.7'
-    - python: '3.4'
     - python: '3.5'
     - python: '3.6'
     - python: '3.7'
-      dist: xenial
+    - python: '3.8'
 
 # command to install dependencies
-install: "pip install -r requirements.txt"
+install: pip install -r requirements.txt
 # command to run tests
-script: "python -m unittest tests.tests"
+script: python -m unittest tests.tests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/CHANGELOG.rst 
new/pykeepass-3.2.1/CHANGELOG.rst
--- old/pykeepass-3.2.0/CHANGELOG.rst   2020-01-19 05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/CHANGELOG.rst   2020-07-20 00:47:29.000000000 +0200
@@ -1,4 +1,10 @@
-3.2.0 - 2019-01-18
+3.2.1 - 2020-07-19
+------------------
+- pin construct version to last supporting python2
+- hard dependency on pycryptodomex
+- fixed #193 - kp.groups, kp.entries not returning elements with name/title 
None
+
+3.2.0 - 2020-01-18
 ------------------
 - added PyKeePass.xml()
 - added create_database()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/README.rst 
new/pykeepass-3.2.1/README.rst
--- old/pykeepass-3.2.0/README.rst      2020-01-19 05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/README.rst      2020-07-20 00:47:29.000000000 +0200
@@ -226,7 +226,7 @@
 
 **delete_binary** (id)
 
-where ``id`` is an int.  Removes binary data from the database and deletes any 
attachments that reference it.  Since attachments reference binaries by their 
positional index, attachments that reference binaries id > ``id`` will 
automatically be decremented.
+where ``id`` is an int.  Removes binary data from the database and deletes any 
attachments that reference it.  Since attachments reference binaries by their 
positional index, attachments that reference binaries with id > ``id`` will 
automatically be decremented.
 
 **find_attachments** (id=None, filename=None, element=None, recursive=True, 
regex=False, flags=None, history=False, first=False)
 
@@ -237,7 +237,7 @@
 
 **binaries**
 
-list of bytes containing binary data.  List index corresponds to attachment id.
+list of bytestrings containing binary data.  List index corresponds to 
attachment id.
 
 **attachments**
 
@@ -245,7 +245,7 @@
 
 **Entry.add_attachment** (id, filename)
 
-where ``id`` is an int and ``filename`` is a string.  Creates a reference 
using the given filename to a database binary.  The existence of an binary with 
the given id is not checked.  Returns ``Attachment``.
+where ``id`` is an int and ``filename`` is a string.  Creates a reference 
using the given filename to a database binary.  The existence of a binary with 
the given id is not checked.  Returns ``Attachment``.
 
 **Entry.delete_attachment** (attachment)
 
@@ -314,10 +314,12 @@
 
 Miscellaneous
 -------------
-**read** (filename, password=None, keyfile=None, transformed_key=None)
+**read** (filename=None, password=None, keyfile=None, transformed_key=None)
 
 where ``filename``, ``password``, and ``keyfile`` are strings.  ``filename`` 
is the path to the database, ``password`` is the master password string, and 
``keyfile`` is the path to the database keyfile.  At least one of ``password`` 
and ``keyfile`` is required.  Alternatively, the derived key can be supplied 
directly through ``transformed_key``.
 
+Can raise ``CredentialsError``, ``HeaderChecksumError``, or 
``PayloadChecksumError``.
+
 **save** (filename=None)
 
 where ``filename`` is the path of the file to save to.  If ``filename`` is not 
given, the path given in ``read`` will be used.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/pykeepass/exceptions.py 
new/pykeepass-3.2.1/pykeepass/exceptions.py
--- old/pykeepass-3.2.0/pykeepass/exceptions.py 2020-01-19 05:13:52.000000000 
+0100
+++ new/pykeepass-3.2.1/pykeepass/exceptions.py 2020-07-20 00:47:29.000000000 
+0200
@@ -1,15 +1,19 @@
 # ----- binary parsing exceptions -----
 
+class CredentialsError(Exception):
+    pass
+
+class PayloadChecksumError(Exception):
+    pass
+
+class HeaderChecksumError(Exception):
+    pass
+
 # ----- pykeepass exceptions -----
+
 class BinaryError(Exception):
     pass
 
-# raised on invalid credentials, header corruption, payload corruption
-# this may be split into multiple exceptions when python-construct supports it
-class CredentialsIntegrityError(Exception):
-    def __init__(self, msg='Credentials are wrong or integrity check failed'):
-        super(Exception, self).__init__(msg)
-
 # ----- Entry exceptions -----
 
 # ----- Group exceptions -----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/pykeepass/kdbx_parsing/common.py 
new/pykeepass-3.2.1/pykeepass/kdbx_parsing/common.py
--- old/pykeepass-3.2.0/pykeepass/kdbx_parsing/common.py        2020-01-19 
05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/pykeepass/kdbx_parsing/common.py        2020-07-20 
00:47:29.000000000 +0200
@@ -1,6 +1,6 @@
-from Crypto.Cipher import AES, ChaCha20, Salsa20
+from Cryptodome.Cipher import AES, ChaCha20, Salsa20
 from .twofish import Twofish
-from Crypto.Util import Padding as CryptoPadding
+from Cryptodome.Util import Padding as CryptoPadding
 import hashlib
 from construct import (
     Adapter, BitStruct, BitsSwapped, Container, Flag, Padding, ListContainer, 
Mapping, GreedyBytes, Int32ul, Switch
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/pykeepass/kdbx_parsing/kdbx3.py 
new/pykeepass-3.2.1/pykeepass/kdbx_parsing/kdbx3.py
--- old/pykeepass-3.2.0/pykeepass/kdbx_parsing/kdbx3.py 2020-01-19 
05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/pykeepass/kdbx_parsing/kdbx3.py 2020-07-20 
00:47:29.000000000 +0200
@@ -1,4 +1,3 @@
-#!/bin/env python3
 # Evan Widloski - 2018-04-11
 # keepass decrypt experimentation
 
@@ -129,7 +128,7 @@
 UnpackedPayload = Reparsed(
     Struct(
         # validate payload decryption
-        Checksum(
+        "cred_check" / Checksum(
             Bytes(32),
             lambda this: 
this._._.header.value.dynamic_header.stream_start_bytes.data,
             this,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/pykeepass/kdbx_parsing/kdbx4.py 
new/pykeepass-3.2.1/pykeepass/kdbx_parsing/kdbx4.py
--- old/pykeepass-3.2.0/pykeepass/kdbx_parsing/kdbx4.py 2020-01-19 
05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/pykeepass/kdbx_parsing/kdbx4.py 2020-07-20 
00:47:29.000000000 +0200
@@ -1,4 +1,3 @@
-#!/bin/env python3
 # Evan Widloski - 2018-04-11
 # keepass decrypt experimentation
 
@@ -259,7 +258,7 @@
         this._.header.data,
         # exception=HeaderChecksumError,
     ),
-    "hmac" / Checksum(
+    "cred_check" / Checksum(
         Bytes(32),
         compute_header_hmac_hash,
         this,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/pykeepass/kdbx_parsing/twofish.py 
new/pykeepass-3.2.1/pykeepass/kdbx_parsing/twofish.py
--- old/pykeepass-3.2.0/pykeepass/kdbx_parsing/twofish.py       2020-01-19 
05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/pykeepass/kdbx_parsing/twofish.py       2020-07-20 
00:47:29.000000000 +0200
@@ -25,8 +25,8 @@
 __all__ = ['Twofish']
 
 from . import pytwofish
-from Crypto.Util.strxor import strxor
-from Crypto.Util.Padding import pad
+from Cryptodome.Util.strxor import strxor
+from Cryptodome.Util.Padding import pad
 
 MODE_ECB = 1
 MODE_CBC = 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/pykeepass/pykeepass.py 
new/pykeepass-3.2.1/pykeepass/pykeepass.py
--- old/pykeepass-3.2.0/pykeepass/pykeepass.py  2020-01-19 05:13:52.000000000 
+0100
+++ new/pykeepass-3.2.1/pykeepass/pykeepass.py  2020-07-20 00:47:29.000000000 
+0200
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
 # coding: utf-8
 
 # FIXME python2
@@ -25,7 +24,6 @@
 from pykeepass.kdbx_parsing.kdbx4 import kdf_uuids
 from pykeepass.xpath import attachment_xp, entry_xp, group_xp, path_xp
 
-logging.basicConfig()
 logger = logging.getLogger(__name__)
 
 
@@ -37,12 +35,35 @@
 # FIXME python2
 @python_2_unicode_compatible
 class PyKeePass(object):
+    """Open a KeePass database
+
+    Args:
+        filename (:obj:`str`, optional): path to database.  If None, the
+            path given when the database was opened is used.
+        password (:obj:`str`, optional): database password.  If None,
+            database is assumed to have no password
+        keyfile (:obj:`str`, optional): path to keyfile.  If None,
+            database is assumed to have no keyfile
+        transformed_key (:obj:`bytes`, optional): precomputed transformed
+            key.
+
+    Raises:
+        CredentialsError: raised when password/keyfile or transformed key
+            are wrong
+        HeaderChecksumError: raised when checksum in database header is
+            is wrong.  e.g. database tampering or file corruption
+        PayloadChecksumError: raised when payload blocks checksum is wrong,
+            e.g. corruption during database saving
+
+    Todo:
+        - raise, no filename provided, database not open
+    """
 
     def __init__(self, filename, password=None, keyfile=None,
                  transformed_key=None):
-        self.filename = filename
 
         self.read(
+            filename=filename,
             password=password,
             keyfile=keyfile,
             transformed_key=transformed_key
@@ -57,9 +78,17 @@
 
     def read(self, filename=None, password=None, keyfile=None,
              transformed_key=None):
+        """
+        See class docstring.
+
+        Todo:
+            - raise, no filename provided, database not open
+        """
         self.password = password
         self.keyfile = keyfile
-        if not filename:
+        if filename:
+            self.filename = filename
+        else:
             filename = self.filename
 
         try:
@@ -69,10 +98,31 @@
                 keyfile=keyfile,
                 transformed_key=transformed_key
             )
-        except ChecksumError:
-            raise CredentialsIntegrityError
+        except ChecksumError as e:
+            if e.path in (
+                    '(parsing) -> body -> cred_check', # KDBX4
+                    '(parsing) -> cred_check' # KDBX3
+                    ):
+                raise CredentialsError
+            elif e.path == '(parsing) -> body -> sha256':
+                raise HeaderChecksumError
+            elif e.path in (
+                    '(parsing) -> body -> payload -> hmac_hash', # KDBX4
+                    '(parsing) -> xml -> block_hash' # KDBX3
+                    ):
+                raise PayloadChecksumError
+            else:
+                raise
 
     def save(self, filename=None, transformed_key=None):
+        """Save current database object to disk.
+
+        Args:
+            filename (:obj:`str`, optional): path to database.  If None, the
+                path given when the database was opened is used.
+            transformed_key (:obj:`bytes`, optional): precomputed transformed
+                key.
+        """
         if not filename:
             filename = self.filename
 
@@ -86,6 +136,8 @@
 
     @property
     def version(self):
+        """tuple: Length 2 tuple of ints containing major and minor versions.
+        Generally (3, 1) or (4, 0)."""
         return (
             self.kdbx.header.value.major_version,
             self.kdbx.header.value.minor_version
@@ -93,10 +145,14 @@
 
     @property
     def encryption_algorithm(self):
+        """str: encryption algorithm used by database during decryption.
+        Can be one of 'aes256', 'chacha20', or 'twofish'."""
         return self.kdbx.header.value.dynamic_header.cipher_id.data
 
     @property
     def kdf_algorithm(self):
+        """str: key derivation algorithm used by database during decryption.
+        Can be one of 'aeskdf', 'argon2', or 'aeskdf'"""
         if self.version == (3, 1):
             return 'aeskdf'
         elif self.version == (4, 0):
@@ -108,25 +164,38 @@
 
     @property
     def transformed_key(self):
+        """bytes: transformed key used in database decryption.  May be cached
+        and passed to `open` for faster database opening"""
         return self.kdbx.body.transformed_key
 
     @property
     def tree(self):
+        """lxml.etree._ElementTree: database XML payload"""
         return self.kdbx.body.payload.xml
 
     @property
     def root_group(self):
+        """Group: root Group of database"""
         return self.find_groups(path='', first=True)
 
     @property
     def groups(self):
-        return self.find_groups_by_name('.*', regex=True)
+        """:obj:`list` of :obj:`Group`: list of all Group objects in database
+        """
+        return self._xpath('//Group', cast=True)
 
     @property
     def entries(self):
-        return self.find_entries_by_title('.*', regex=True)
+        """:obj:`list` of :obj:`Entry`: list of all Entry objects in database,
+        excluding history"""
+        return self._xpath('//Entry', cast=True)
 
     def xml(self):
+        """Get XML part of database as string
+
+        Returns:
+            str: XML payload section of database.
+        """
         return etree.tostring(
             self.tree,
             pretty_print=True,
@@ -134,12 +203,13 @@
             encoding='unicode'
         )
 
-    def dump_xml(self, outfile):
-        '''
-        Dump the content of the database to a file
-        NOTE The file is unencrypted!
-        '''
-        with open(outfile, 'wb') as f:
+    def dump_xml(self, filename):
+        """ Dump the contents of the database to file as XML
+
+        Args:
+            filename (str): path to output file
+        """
+        with open(filename, 'wb') as f:
             f.write(
                 etree.tostring(
                     self.tree,
@@ -151,6 +221,26 @@
 
     def _xpath(self, xpath_str, tree=None, first=False, history=False,
                cast=False, **kwargs):
+        """Look up elements in the XML payload and return corresponding object.
+
+        Internal function which searches the payload lxml ElementTree for
+        elements via XPath.  Matched entry, group, and attachment elements are
+        automatically cast to their corresponding objects, otherwise an error
+        is raised.
+
+        Args:
+            xpath_str (str): XPath query for finding element(s)
+            tree (:obj:`_ElementTree`, :obj:`Element`, optional): use this
+                element as root node when searching
+            first (bool): If True, function returns first result or None.  If
+                False, function returns list of matches or empty list.  Default
+                is False.
+            history (bool): If True, history entries are included in results.
+                Default is False.
+            cast (bool): If True, matches are instead instantiated as
+                pykeepass Group, Entry, or Attachment objects.  An exception
+                is raised if a match cannot be cast.  Default is False.
+        """
 
         if tree is None:
             tree = self.tree
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/requirements.txt 
new/pykeepass-3.2.1/requirements.txt
--- old/pykeepass-3.2.0/requirements.txt        2020-01-19 05:13:52.000000000 
+0100
+++ new/pykeepass-3.2.1/requirements.txt        2020-07-20 00:47:29.000000000 
+0200
@@ -1,6 +1,6 @@
 lxml==4.3.5
-pycryptodome==3.9.4
-construct==2.9.51
+pycryptodomex==3.9.4
+construct==2.10.54
 argon2-cffi==19.2.0
 python-dateutil==2.8.0
 future==0.17.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/setup.py new/pykeepass-3.2.1/setup.py
--- old/pykeepass-3.2.0/setup.py        2020-01-19 05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/setup.py        2020-07-20 00:47:29.000000000 +0200
@@ -3,7 +3,7 @@
 
 setup(
     name="pykeepass",
-    version="3.2.0",
+    version="3.2.1",
     license="GPL3",
     description="Python library to interact with keepass databases "
                 "(supports KDBX3 and KDBX4)",
@@ -14,9 +14,10 @@
     packages=find_packages(),
     install_requires=[
         "python-dateutil",
-        "construct",
+        # FIXME python2 - last version to support python2
+        "construct==2.10.54",
         "argon2_cffi",
-        "pycryptodome",
+        "pycryptodomex>=3.6.2",
         "lxml",
         # FIXME python2
         "future"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/pykeepass-3.2.0/tests/tests.py 
new/pykeepass-3.2.1/tests/tests.py
--- old/pykeepass-3.2.0/tests/tests.py  2020-01-19 05:13:52.000000000 +0100
+++ new/pykeepass-3.2.1/tests/tests.py  2020-07-20 00:47:29.000000000 +0200
@@ -18,7 +18,7 @@
 from pykeepass.exceptions import BinaryError
 from pykeepass.group import Group
 from pykeepass.kdbx_parsing import KDBX
-from pykeepass.exceptions import BinaryError, CredentialsIntegrityError
+from pykeepass.exceptions import BinaryError, CredentialsError
 
 """
 Missing Tests:
@@ -45,11 +45,21 @@
 
     # get some things ready before testing
     def setUp(self):
+        shutil.copy(
+            os.path.join(base_dir, self.database),
+            os.path.join(base_dir, 'change_creds.kdbx')
+        )
         self.kp = PyKeePass(
             os.path.join(base_dir, self.database),
             password=self.password,
             keyfile=os.path.join(base_dir, self.keyfile)
         )
+        # for tests which modify the database, use this
+        self.kp_tmp = PyKeePass(
+            os.path.join(base_dir, 'change_creds.kdbx'),
+            password=self.password,
+            keyfile=os.path.join(base_dir, self.keyfile)
+        )
 
 
 class KDBX4Tests(KDBX3Tests):
@@ -687,30 +697,15 @@
 
 
 class PyKeePassTests3(KDBX3Tests):
-    def setUp(self):
-        shutil.copy(
-            os.path.join(base_dir, self.database),
-            os.path.join(base_dir, 'change_creds.kdbx')
-        )
-        self.kp = PyKeePass(
-            os.path.join(base_dir, self.database),
-            password=self.password,
-            keyfile=os.path.join(base_dir, self.keyfile)
-        )
-        self.kp_tmp = PyKeePass(
-            os.path.join(base_dir, 'change_creds.kdbx'),
-            password=self.password,
-            keyfile=os.path.join(base_dir, self.keyfile)
-        )
 
     def test_set_credentials(self):
         self.kp_tmp.password = 'f00bar'
         self.kp_tmp.keyfile = os.path.join(base_dir, 'change.key')
         self.kp_tmp.save()
         self.kp_tmp = PyKeePass(
-            os.path.join(base_dir, 'change_creds.kdbx'),
-            password='f00bar',
-            keyfile=os.path.join(base_dir, 'change.key')
+            self.kp_tmp.filename,
+            'f00bar',
+            self.kp_tmp.keyfile
         )
 
         results = self.kp.find_entries_by_username('foobar_user', first=True)
@@ -727,20 +722,35 @@
 
 
 class BugRegressionTests3(KDBX3Tests):
-    def test_129(self):
+    def test_issue129(self):
         # issue 129 - protected multiline string fields lose newline
         e = self.kp.find_entries(title='foobar_entry', first=True)
         self.assertEqual(e.get_custom_property('multiline'), 'hello\nworld')
 
     def test_pull102(self):
         # PR 102 - entries are protected after save
-        # reset self.kp
+        # reset self.kp_tmp
         self.setUp()
-        e = self.kp.find_entries(title='foobar_entry', first=True)
+        e = self.kp_tmp.find_entries(title='foobar_entry', first=True)
         self.assertEqual(e.password, 'foobar')
-        self.kp.save()
+        self.kp_tmp.save()
         self.assertEqual(e.password, 'foobar')
 
+    def test_issue193(self):
+        # issue 193 - kp.entries doesn't return entries with title=None
+        e = self.kp.add_entry(self.kp.root_group, 'test', 'user', 'pass')
+        prop = e._xpath('String/Key[text()="Title"]/..', first=True)
+        e._element.remove(prop)
+        self.assertTrue(e.title is None)
+        self.assertTrue(e in self.kp.entries)
+        # also test for kp.groups
+        g = self.kp.add_group(self.kp.root_group, 'test_g')
+        prop = g._xpath('Name', first=True)
+        g._element.remove(prop)
+        self.assertTrue(g.name is None)
+        self.assertTrue(g in self.kp.groups)
+
+
 
 class EntryFindTests4(KDBX4Tests, EntryFindTests3):
     pass
@@ -882,7 +892,7 @@
             )
 
     def test_open_error(self):
-        with self.assertRaises(CredentialsIntegrityError):
+        with self.assertRaises(CredentialsError):
             database = 'test4.kdbx'
             invalid_password = 'foobar'
             keyfile = os.path.join(base_dir, 'test4.key')
@@ -891,7 +901,7 @@
                 password=invalid_password,
                 keyfile=keyfile
             )
-        with self.assertRaises(CredentialsIntegrityError):
+        with self.assertRaises(CredentialsError):
             database = 'test4.kdbx'
             password = 'password'
             invalid_keyfile = os.path.join(base_dir, 'test3.key')


Reply via email to