Repository: Electrostatics/mmcif_pdbx

Issue:
pylint code

Discussion:

Patch:
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..fb68c9c
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,2 @@
+[MESSAGES CONTROL]
+disable = no-else-return
\ No newline at end of file
diff --git a/README.md b/README.md
index b04e032..05abdf7 100644
--- a/README.md
+++ b/README.md
@@ -15,3 +15,8 @@ All of the code in this repository is based on http://mmcif.wwpdb.org/.
 Specifically, this code is directly derived from http://mmcif.wwpdb.org/docs/sw-examples/python/src/pdbx.tar.gz linked from http://mmcif.wwpdb.org/docs/sw-examples/python/html/.
 
 See http://mmcif.wwpdb.org/docs/sw-examples/python/html/ for more information about this package, including examples.
+
+## Versions
+
+Versions 0.* maintain API compatibility with the original code.
+Subsequent version break that compatibility, primarily by renaming methods in compliance with PEP8.
diff --git a/pdbx/reader/containers.py b/pdbx/reader/containers.py
index 733f3b4..40054ee 100644
--- a/pdbx/reader/containers.py
+++ b/pdbx/reader/containers.py
@@ -23,11 +23,11 @@ A base container class is defined which supports common features of
 data and definition containers. PDBx data files are organized in
 sections called data blocks which are mapped to data containers.
 PDBx dictionaries contain definition sections and data sections
-which are mapped to definition and data containes respectively.
+which are mapped to definition and data containers respectively.
 
 Data in both PDBx data files and dictionaries are organized in
 data categories. In the PDBx syntax individual items or data
-identified by labels of the form '_categoryName.attributeName'.
+identified by labels of the form '_categoryName.attribute_name'.
 The terms category and attribute in PDBx jargon are analogous
 table and column in relational data model, or class and attribute
 in an object oriented data model.
@@ -37,9 +37,10 @@ data and definition meta data.
 
 """
 import re
-import sys
+from sys import stdout
 import traceback
 
+
 __docformat__ = "restructuredtext en"
 __author__ = "John Westbrook"
 __email__ = "jwest@rcsb.rutgers.edu"
@@ -47,14 +48,11 @@ __license__ = "Creative Commons Attribution 3.0 Unported"
 __version__ = "V0.01"
 
 
-class CifName(object):
-    ''' Class of utilities for CIF-style data names -
-    '''
-    def __init__(self):
-        pass
-
+class CifName:
+    """Class of utilities for CIF-style data names."""
     @staticmethod
-    def categoryPart(name):
+    def category_part(name):
+        """Get the category part of the name."""
         tname = ""
         if name.startswith("_"):
             tname = name[1:]
@@ -68,7 +66,8 @@ class CifName(object):
             return tname[:i]
 
     @staticmethod
-    def attributePart(name):
+    def attribute_part(name):
+        """Get the attribute part of the name."""
         i = name.find(".")
         if i == -1:
             return None
@@ -76,739 +75,754 @@ class CifName(object):
             return name[i + 1:]
 
 
-class ContainerBase(object):
-    ''' Container base class for data and definition objects.
-    '''
+class ContainerBase:
+    """Container base class for data and definition objects."""
     def __init__(self, name):
         # The enclosing scope of the data container (e.g. data_/save_)
         self.__name = name
         # List of category names within this container -
-        self.__objNameList = []
+        self.__object_name_list = []
         # dictionary of DataCategory objects keyed by category name.
-        self.__objCatalog = {}
+        self.__object_catalog = {}
         self.__type = None
 
-    def getType(self):
+    def get_type(self):
+        """Get container type."""
         return self.__type
 
-    def setType(self, type):
-        self.__type = type
+    def set_type(self, type_):
+        """Set container type."""
+        self.__type = type_
 
-    def getName(self):
+    @property
+    def name(self):
+        """Get container name."""
         return self.__name
 
-    def setName(self, name):
+    def set_name(self, name):
+        """Set container name."""
         self.__name = name
 
     def exists(self, name):
-        if name in self.__objCatalog:
-            return True
-        else:
-            return False
+        """Determine if object name exists in object catalog."""
+        return (name in self.__object_catalog)
 
-    def getObj(self, name):
-        if name in self.__objCatalog:
-            return self.__objCatalog[name]
+    def get_object(self, name):
+        """Get object from object catalog."""
+        if name in self.__object_catalog:
+            return self.__object_catalog[name]
         else:
             return None
 
-    def getObjNameList(self):
-        return self.__objNameList
+    def get_object_name_list(self):
+        """Get list of object names."""
+        return self.__object_name_list
 
     def append(self, obj):
-        """ Add the input object to the current object catalog. An existing object
-            of the same name will be overwritten.
-        """
-        if obj.getName() is not None:
-            if obj.getName() not in self.__objCatalog:
-                # self.__objNameList is keeping track of object order here --
-                self.__objNameList.append(obj.getName())
-            self.__objCatalog[obj.getName()] = obj
+        """Add the input object to the current object catalog.
+        An existing object of the same name will be overwritten."""
+        if obj.name is not None:
+            if obj.name not in self.__object_catalog:
+                self.__object_name_list.append(obj.name)
+            self.__object_catalog[obj.name] = obj
 
     def replace(self, obj):
-        """ Replace an existing object with the input object
-        """
-        if ((obj.getName() is not None) and (obj.getName() in self.__objCatalog)):
-            self.__objCatalog[obj.getName()] = obj
-
-    def printIt(self, fh=sys.stdout, type="brief"):
-        fh.write("+ %s container: %30s contains %4d categories\n" %
-                 (self.getType(), self.getName(), len(self.__objNameList)))
-        for nm in self.__objNameList:
-            fh.write("--------------------------------------------\n")
-            fh.write("Data category: %s\n" % nm)
-            if type == 'brief':
-                self.__objCatalog[nm].printIt(fh)
+        """Replace an existing object with the input object."""
+        if (obj.name is not None) and (
+                obj.name in self.__object_catalog):
+            self.__object_catalog[obj.name] = obj
+
+    def print_it(self, file_=stdout, type_="brief"):
+        """Dump information about container to specified file object."""
+        file_.write(
+            "+ %s container: %30s contains %4d categories\n" % (
+                self.get_type(), self.name,
+                len(self.__object_name_list)))
+        for name in self.__object_name_list:
+            file_.write("--------------------------------------------\n")
+            file_.write("Data category: %s\n" % name)
+            if type_ == 'brief':
+                self.__object_catalog[name].print_it(file_)
             else:
-                self.__objCatalog[nm].dumpIt(fh)
+                self.__object_catalog[name].dump_it(file_)
 
-    def rename(self, curName, newName):
-        """ Change the name of an object in place -
-        """
+    def rename(self, current_name, new_name):
+        """Change the name of an object in place."""
         try:
-            i = self.__objNameList.index(curName)
-            self.__objNameList[i] = newName
-            self.__objCatalog[newName] = self.__objCatalog[curName]
-            self.__objCatalog[newName].setName(newName)
+            i = self.__object_name_list.index(current_name)
+            self.__object_name_list[i] = new_name
+            self.__object_catalog[new_name] = self.__object_catalog[
+                current_name]
+            self.__object_catalog[new_name].set_name(new_name)
             return True
-        except:
+        except KeyError:
             return False
 
-    def remove(self, curName):
-        """ Revmove object by name. Return True on success or False otherwise.
-        """
+    def remove(self, current_name):
+        """Revmove object by name.
+        Return True on success or False otherwise."""
         try:
-            if curName in self.__objCatalog:
-                del self.__objCatalog[curName]
-                i = self.__objNameList.index(curName)
-                del self.__objNameList[i]
+            if current_name in self.__object_catalog:
+                del self.__object_catalog[current_name]
+                i = self.__object_name_list.index(current_name)
+                del self.__object_name_list[i]
                 return True
             else:
                 return False
-        except:
+        except KeyError:
             pass
-
         return False
 
 
 class DefinitionContainer(ContainerBase):
+    """Container for definitions."""
+
     def __init__(self, name):
         super(DefinitionContainer, self).__init__(name)
-        self.setType('definition')
+        self.set_type('definition')
 
-    def isCategory(self):
+    def is_category(self):
+        """Determine if container contains category objects."""
         if self.exists('category'):
             return True
         return False
 
-    def isAttribute(self):
+    def is_attribute(self):
+        """Determine if container contains item objects."""
         if self.exists('item'):
             return True
         return False
 
-    def printIt(self, fh=sys.stdout, type="brief"):
+    def print_it(self, fh=stdout, type_="brief"):
+        """Print information about container to file object."""
         fh.write("Definition container: %30s contains %4d categories\n" %
-                 (self.getName(), len(self.getObjNameList())))
-        if self.isCategory():
+                 (self.name, len(self.get_object_name_list())))
+        if self.is_category():
             fh.write("Definition type: category\n")
-        elif self.isAttribute():
+        elif self.is_attribute():
             fh.write("Definition type: item\n")
         else:
             fh.write("Definition type: undefined\n")
 
-        for nm in self.getObjNameList():
+        for name in self.get_object_name_list():
             fh.write("--------------------------------------------\n")
-            fh.write("Definition category: %s\n" % nm)
-            if type == 'brief':
-                self.getObj(nm).printIt(fh)
+            fh.write("Definition category: %s\n" % name)
+            if type_ == 'brief':
+                self.get_object(name).print_it(fh)
             else:
-                self.getObj(nm).dumpId(fh)
+                self.get_object(name).dumpId(fh)
 
 
 class DataContainer(ContainerBase):
-    ''' Container class for DataCategory objects.
-    '''
+    """Container class for DataCategory objects."""
+
     def __init__(self, name):
         super(DataContainer, self).__init__(name)
-        self.setType('data')
-        self.__globalFlag = False
+        self.set_type('data')
+        self.__global_flag = False
+        self.__current_row = None
+
+    def invoke_data_block_method(self, method):
+        """Invoke a method for the given data block."""
+        self.__current_row = 1
+        # TODO - remove exec() commands!
+        exec(method.get_inline())
 
-    def invokeDataBlockMethod(self, type, method, db):
-        self.__currentRow = 1
-        exec(method.getInline())
+    def set_global(self):
+        """Set global flag to True."""
+        self.__global_flag = True
 
-    def setGlobal(self):
-        self.__globalFlag = True
+    def get_global(self):
+        """Return global flag."""
+        return self.__global_flag
 
-    def getGlobal(self):
-        return self.__globalFlag
 
+class DataCategoryBase:
+    """Base object definition for a data category."""
 
-class DataCategoryBase(object):
-    """ Base object definition for a data category -
-    """
-    def __init__(self, name, attributeNameList=None, rowList=None):
+    def __init__(self, name, attribute_name_list=None, row_list=None):
         self._name = name
-        #
-        if rowList is not None:
-            self._rowList = rowList
+        if row_list is not None:
+            self._row_list = row_list
         else:
-            self._rowList = []
+            self._row_list = []
 
-        if attributeNameList is not None:
-            self._attributeNameList = attributeNameList
+        if attribute_name_list is not None:
+            self._attribute_name_list = attribute_name_list
         else:
-            self._attributeNameList = []
-        #
-        # Derived class data -
-        #
+            self._attribute_name_list = []
         self._catalog = {}
-        self._numAttributes = 0
-        #
+        self._num_attributes = 0
         self.__setup()
 
     def __setup(self):
-        self._numAttributes = len(self._attributeNameList)
+        self._num_attributes = len(self._attribute_name_list)
         self._catalog = {}
-        for attributeName in self._attributeNameList:
-            attributeNameLC = attributeName.lower()
-            self._catalog[attributeNameLC] = attributeName
+        for attribute_name in self._attribute_name_list:
+            attribute_name_lower = attribute_name.lower()
+            self._catalog[attribute_name_lower] = attribute_name
 
-    def setRowList(self, rowList):
-        self._rowList = rowList
+    def set_row_list(self, row_list):
+        """Set row list."""
+        self._row_list = row_list
 
-    def setAttributeNameList(self, attributeNameList):
-        self._attributeNameList = attributeNameList
+    def set_attribute_name_list(self, attribute_name_list):
+        """Set attribute name list."""
+        self._attribute_name_list = attribute_name_list
         self.__setup()
 
-    def setName(self, name):
+    def set_name(self, name):
+        """Set name."""
         self._name = name
 
     def get(self):
-        return (self._name, self._attributeNameList, self._rowList)
+        """Set name, attribute name list, and row list."""
+        return (self._name, self._attribute_name_list, self._row_list)
 
 
 class DataCategory(DataCategoryBase):
-    """ Methods for creating, accessing, and formatting PDBx cif data categories.
-    """
-    def __init__(self, name, attributeNameList=None, rowList=None):
-        super(DataCategory, self).__init__(name, attributeNameList, rowList)
-        #
-        self.__lfh = sys.stdout
-
-        self.__currentRowIndex = 0
-        self.__currentAttribute = None
-        #
-        self.__avoidEmbeddedQuoting = False
-        #
-        # --------------------------------------------------------------------
-        # any whitespace
-        self.__wsRe = re.compile(r"\s")
-        self.__wsAndQuotesRe = re.compile(r"[\s'\"]")
-        # any newline or carriage control
-        self.__nlRe = re.compile(r"[\n\r]")
-        #
-        # single quote
-        self.__sqRe = re.compile(r"[']")
-        #
-        self.__sqWsRe = re.compile(r"('\s)|(\s')")
-
-        # double quote
-        self.__dqRe = re.compile(r'["]')
-        self.__dqWsRe = re.compile(r'("\s)|(\s")')
-        #
-        self.__intRe = re.compile(r'^[0-9]+$')
-        self.__floatRe = re.compile(
+    """Methods for creating, accessing, formatting PDBx cif data categories."""
+
+    def __init__(self, name, attribute_name_list=None, row_list=None):
+        super().__init__(name, attribute_name_list, row_list)
+        self.__lfh = stdout
+        self.__current_row_index = 0
+        self.__current_attribute = None
+        self.__avoid_embedded_quoting = False
+        self.__whitespace_re = re.compile(r"\s")
+        self.__whitespace_quotes_re = re.compile(r"[\s'\"]")
+        self.__newline_re = re.compile(r"[\n\r]")
+        self.__single_quote_re = re.compile(r"[']")
+        self.__whitespace_single_quote_re = re.compile(r"('\s)|(\s')")
+        self.__double_quote_re = re.compile(r'["]')
+        self.__whitespace_double_quote_re = re.compile(r'("\s)|(\s")')
+        self.__integer_re = re.compile(r'^[0-9]+$')
+        self.__float_re = re.compile(
             r'^-?(([0-9]+)[.]?|([0-9]*[.][0-9]+))([(][0-9]+[)])?([eE][+-]?[0-9]+)?$')
-        #
-        self.__dataTypeList = [
-            'DT_NULL_VALUE', 'DT_INTEGER', 'DT_FLOAT', 'DT_UNQUOTED_STRING', 'DT_ITEM_NAME',
-            'DT_DOUBLE_QUOTED_STRING', 'DT_SINGLE_QUOTED_STRING', 'DT_MULTI_LINE_STRING',
+        self.__data_type_list = [
+            'DT_NULL_VALUE', 'DT_INTEGER', 'DT_FLOAT', 'DT_UNQUOTED_STRING',
+            'DT_ITEM_NAME', 'DT_DOUBLE_QUOTED_STRING',
+            'DT_SINGLE_QUOTED_STRING', 'DT_MULTI_LINE_STRING',
         ]
-        self.__formatTypeList = [
+        self.__format_type_list = [
             'FT_NULL_VALUE', 'FT_NUMBER', 'FT_NUMBER', 'FT_UNQUOTED_STRING',
-            'FT_QUOTED_STRING', 'FT_QUOTED_STRING', 'FT_QUOTED_STRING', 'FT_MULTI_LINE_STRING'
+            'FT_QUOTED_STRING', 'FT_QUOTED_STRING', 'FT_QUOTED_STRING',
+            'FT_MULTI_LINE_STRING'
         ]
 
-    def __getitem__(self, x):
-        """ Implements list-type functionality -
-             Implements op[x] for some special cases -
-                x = integer - returns the row in category (normal list behavior)
-                x = string - returns the value of attribute 'x' in first row.
+    def __getitem__(self, item):
+        """Implements list-type functionality.
+
+        Implements op[x] for some special cases:
+            item = integer - returns the row in category (normal list behavior)
+            item = string - returns the value of attribute 'x' in first row
         """
-        if isinstance(x, int):
-            # return self._rowList.__getitem__(x)
-            return self._rowList[x]
+        if isinstance(item, int):
+            return self._row_list[item]
 
-        elif isinstance(x, str):
+        elif isinstance(item, str):
             try:
-                # return self._rowList[0][x]
-                ii = self.getAttributeIndex(x)
-                return self._rowList[0][ii]
+                self.get_attribute_index(item)
+                return self._row_list[0][item]
             except (IndexError, KeyError):
                 raise KeyError
-        raise TypeError(x)
-
-    def getCurrentAttribute(self):
-        return self.__currentAttribute
-
-    def getRowIndex(self):
-        return self.__currentRowIndex
-
-    def getRowList(self):
-        return self._rowList
-
-    def getRowCount(self):
-        return (len(self._rowList))
-
-    def getRow(self, index):
+        raise TypeError(item)
+
+    @property
+    def current_attribute(self):
+        """Get current attribute."""
+        return self.__current_attribute
+
+    @property
+    def current_row_index(self):
+        """Get current row index."""
+        return self.__current_row_index
+
+    @property
+    def row_list(self):
+        """Get list of rows."""
+        return self._row_list
+
+    @property
+    def row_count(self):
+        """Get number of rows."""
+        return len(self._row_list)
+
+    def get_row(self, index):
+        """Get specified row.
+        Returns empty array if row not found."""
         try:
-            return self._rowList[index]
-        except:
+            return self._row_list[index]
+        except IndexError:
             return []
 
-    def removeRow(self, index):
+    def remove_row(self, index):
+        """Remove specified row."""
         try:
-            if ((index >= 0) and (index < len(self._rowList))):
-                del self._rowList[index]
-                if self.__currentRowIndex >= len(self._rowList):
-                    self.__currentRowIndex = len(self._rowList) - 1
+            if ((index >= 0) and (index < len(self._row_list))):
+                del self._row_list[index]
+                if self.__current_row_index >= len(self._row_list):
+                    self.__current_row_index = len(self._row_list) - 1
                 return True
             else:
                 pass
-        except:
+        except IndexError:
             pass
-
         return False
 
-    def getFullRow(self, index):
-        """ Return a full row based on the length of the the attribute list.
-        """
+    def get_full_row(self, index):
+        """Return a full row based on the length of the the attribute list."""
         try:
-            if (len(self._rowList[index]) < self._numAttributes):
-                for ii in range(self._numAttributes - len(self._rowList[index])):
-                    self._rowList[index].append('?')
-            return self._rowList[index]
-        except:
-            return ['?' for ii in range(self._numAttributes)]
-
-    def getName(self):
+            if len(self._row_list[index]) < self._num_attributes:
+                self._row_list[index] += ['?'] * (
+                    self._num_attributes - len(self._row_list[index]))
+            return self._row_list[index]
+        except IndexError:
+            return ['?'] * len(self._num_attributes)
+
+    @property
+    def name(self):
+        """Get container name."""
         return self._name
 
-    def getAttributeList(self):
-        return self._attributeNameList
-
-    def getAttributeCount(self):
-        return len(self._attributeNameList)
-
-    def getAttributeListWithOrder(self):
-        oL = []
-        for ii, att in enumerate(self._attributeNameList):
-            oL.append((att, ii))
-        return oL
-
-    def getAttributeIndex(self, attributeName):
-        try:
-            return self._attributeNameList.index(attributeName)
-        except:
-            return -1
-
-    def hasAttribute(self, attributeName):
-        return attributeName in self._attributeNameList
-
-    def getIndex(self, attributeName):
-        try:
-            return self._attributeNameList.index(attributeName)
-        except:
-            return -1
-
-    def getItemNameList(self):
-        itemNameList = []
-        for att in self._attributeNameList:
-            itemNameList.append("_" + self._name + "." + att)
-        return itemNameList
+    @property
+    def attribute_list(self):
+        """Get list of attributes."""
+        return self._attribute_name_list
+
+    @property
+    def attribute_count(self):
+        """Get number of attributes."""
+        return len(self._attribute_name_list)
+
+    @property
+    def attribute_list_with_order(self):
+        """Get list of attributes in order."""
+        ordered_list = []
+        for i, att in enumerate(self._attribute_name_list):
+            ordered_list.append((att, i))
+        return ordered_list
+
+    def get_attribute_index(self, attribute_name):
+        """Get index of given attribute.
+        Raises IndexError."""
+        return self._attribute_name_list.index(attribute_name)
+
+    def has_attribute(self, attribute_name):
+        """Indicate whether container has attribute."""
+        return attribute_name in self._attribute_name_list
+
+    @property
+    def item_name_list(self):
+        """List of attribute names as fully qualified item names."""
+        item_name_list_ = []
+        for att in self._attribute_name_list:
+            item_name_list_.append("_" + self._name + "." + att)
+        return item_name_list_
 
     def append(self, row):
-        # self.__lfh.write("PdbxContainer(append) category %s row %r\n" % (self._name, row))
-        self._rowList.append(row)
-
-    def appendAttribute(self, attributeName):
-        attributeNameLC = attributeName.lower()
-        if attributeNameLC in self._catalog:
-            i = self._attributeNameList.index(self._catalog[attributeNameLC])
-            self._attributeNameList[i] = attributeName
-            self._catalog[attributeNameLC] = attributeName
-            # self.__lfh.write("Appending existing attribute %s\n" % attributeName)
+        """Add row to container."""
+        self._row_list.append(row)
+
+    def append_attribute(self, attribute_name):
+        """Add attribute to container."""
+        attribute_name_lower = attribute_name.lower()
+        if attribute_name_lower in self._catalog:
+            index = self._attribute_name_list.index(
+                self._catalog[attribute_name_lower])
+            self._attribute_name_list[index] = attribute_name
+            self._catalog[attribute_name_lower] = attribute_name
         else:
-            # self.__lfh.write("Appending existing attribute %s\n" % attributeName)
-            self._attributeNameList.append(attributeName)
-            self._catalog[attributeNameLC] = attributeName
-            #
-        self._numAttributes = len(self._attributeNameList)
-
-    def appendAttributeExtendRows(self, attributeName):
-        attributeNameLC = attributeName.lower()
-        if attributeNameLC in self._catalog:
-            i = self._attributeNameList.index(self._catalog[attributeNameLC])
-            self._attributeNameList[i] = attributeName
-            self._catalog[attributeNameLC] = attributeName
-            self.__lfh.write("Appending existing attribute %s\n" % attributeName)
+            self._attribute_name_list.append(attribute_name)
+            self._catalog[attribute_name_lower] = attribute_name
+        self._num_attributes = len(self._attribute_name_list)
+
+    def append_attribute_extend_rows(self, attribute_name):
+        """Append attribute and extend rows."""
+        attribute_name_lower = attribute_name.lower()
+        if attribute_name_lower in self._catalog:
+            index = self._attribute_name_list.index(
+                self._catalog[attribute_name_lower])
+            self._attribute_name_list[index] = attribute_name
+            self._catalog[attribute_name_lower] = attribute_name
+            self.__lfh.write(
+                "Appending existing attribute %s\n" % attribute_name)
         else:
-            self._attributeNameList.append(attributeName)
-            self._catalog[attributeNameLC] = attributeName
+            self._attribute_name_list.append(attribute_name)
+            self._catalog[attribute_name_lower] = attribute_name
             # add a placeholder to any existing rows for the new attribute.
-            if (len(self._rowList) > 0):
-                for row in self._rowList:
+            if len(self._row_list) > 0:
+                for row in self._row_list:
                     row.append("?")
-            #
-        self._numAttributes = len(self._attributeNameList)
+        self._num_attributes = len(self._attribute_name_list)
 
-    def getValue(self, attributeName=None, rowIndex=None):
-        if attributeName is None:
-            attribute = self.__currentAttribute
+    def get_value(self, attribute_name=None, row_index=None):
+        """Get value for specified attribute and row."""
+        if attribute_name is None:
+            attribute = self.__current_attribute
         else:
-            attribute = attributeName
-        if rowIndex is None:
-            rowI = self.__currentRowIndex
+            attribute = attribute_name
+        if row_index is None:
+            index = self.__current_row_index
         else:
-            rowI = rowIndex
-
-        if isinstance(attribute, str) and isinstance(rowI, int):
-            try:
-                return self._rowList[rowI][self._attributeNameList.index(attribute)]
-            except (IndexError):
-                raise IndexError
+            index = row_index
+        if isinstance(attribute, str) and isinstance(index, int):
+            return self._row_list[index][
+                self._attribute_name_list.index(attribute)]
         raise IndexError(attribute)
 
-    def setValue(self, value, attributeName=None, rowIndex=None):
-        if attributeName is None:
-            attribute = self.__currentAttribute
+    def set_value(self, value, attribute_name=None, row_index=None):
+        """Set value of attribute."""
+        if attribute_name is None:
+            attribute = self.__current_attribute
         else:
-            attribute = attributeName
-
-        if rowIndex is None:
-            rowI = self.__currentRowIndex
+            attribute = attribute_name
+        if row_index is None:
+            index = self.__current_row_index
         else:
-            rowI = rowIndex
-
-        if isinstance(attribute, str) and isinstance(rowI, int):
+            index = row_index
+        if isinstance(attribute, str) and isinstance(index, int):
             try:
-                # if row index is out of range - add the rows -
-                for ii in range(rowI + 1 - len(self._rowList)):
-                    self._rowList.append(self.__emptyRow())
-                # self._rowList[rowI][attribute] = value
-                ll = len(self._rowList[rowI])
-                ind = self._attributeNameList.index(attribute)
-
-                # extend the list if needed -
-                if (ind >= ll):
-                    self._rowList[rowI].extend([None for ii in range(2 * ind - ll)])
-                self._rowList[rowI][ind] = value
-            except (IndexError):
+                # if row index is out of range - add the rows
+                for _ in range(index + 1 - len(self._row_list)):
+                    self._row_list.append(self.__empty_row)
+                row_len = len(self._row_list[index])
+                ind = self._attribute_name_list.index(attribute)
+                # extend the list if needed
+                if ind >= row_len:
+                    self._row_list[index].extend(
+                        [None for _ in range(2 * ind - row_len)])
+                self._row_list[index][ind] = value
+            except IndexError:
                 self.__lfh.write(
                     "DataCategory(setvalue) index error category"
                     " %s attribute %s index %d value %r\n" %
-                    (self._name, attribute, rowI, value))
+                    (self._name, attribute, index, value))
                 traceback.print_exc(file=self.__lfh)
-                # raise IndexError
-            except (ValueError):
+            except ValueError:
                 self.__lfh.write(
                     "DataCategory(setvalue) value error category"
                     " %s attribute %s index %d value %r\n" %
-                    (self._name, attribute, rowI, value))
+                    (self._name, attribute, index, value))
                 traceback.print_exc(file=self.__lfh)
-                # raise ValueError
-
-    def __emptyRow(self):
-        return [None for ii in range(len(self._attributeNameList))]
-
-    def replaceValue(self, oldValue, newValue, attributeName):
-        numReplace = 0
-        if attributeName not in self._attributeNameList:
-            return numReplace
-        ind = self._attributeNameList.index(attributeName)
-        for row in self._rowList:
-            if row[ind] == oldValue:
-                row[ind] = newValue
-                numReplace += 1
-        return numReplace
-
-    def replaceSubstring(self, oldValue, newValue, attributeName):
-        ok = False
-        if attributeName not in self._attributeNameList:
-            return ok
-        ind = self._attributeNameList.index(attributeName)
-        for row in self._rowList:
+
+    @property
+    def __empty_row(self):
+        """Return an empty row."""
+        return [None for _ in range(len(self._attribute_name_list))]
+
+    def replace_value(self, old_value, new_value, attribute_name):
+        """Replace the value of the specified attribute."""
+        num_replace = 0
+        if attribute_name not in self._attribute_name_list:
+            return num_replace
+        ind = self._attribute_name_list.index(attribute_name)
+        for row in self._row_list:
+            if row[ind] == old_value:
+                row[ind] = new_value
+                num_replace += 1
+        return num_replace
+
+    def replace_substring(self, old_value, new_value, attribute_name):
+        """Replace substring of value of given attribute.
+        Return Boolean flag indicating success."""
+        replace_ok = False
+        if attribute_name not in self._attribute_name_list:
+            return replace_ok
+        ind = self._attribute_name_list.index(attribute_name)
+        for row in self._row_list:
             val = row[ind]
-            row[ind] = val.replace(oldValue, newValue)
+            row[ind] = val.replace(old_value, new_value)
             if val != row[ind]:
-                ok = True
-        return ok
-
-    def invokeAttributeMethod(self, attributeName, type, method, db):
-        self.__currentRowIndex = 0
-        self.__currentAttribute = attributeName
-        self.appendAttribute(attributeName)
-        # currentRowIndex = self.__currentRowIndex # assigned by never used
-        #
-        ind = self._attributeNameList.index(attributeName)
-        if len(self._rowList) == 0:
-            row = [None for ii in range(len(self._attributeNameList) * 2)]
+                replace_ok = True
+        return replace_ok
+
+    def invoke_attribute_method(self, attribute_name, method):
+        """Invoke method of current attribute."""
+        self.__current_row_index = 0
+        self.__current_attribute = attribute_name
+        self.append_attribute(attribute_name)
+        ind = self._attribute_name_list.index(attribute_name)
+        if len(self._row_list) == 0:
+            row = [None] * len(self._attribute_name_list)*2
             row[ind] = None
-            self._rowList.append(row)
-
-        for row in self._rowList:
-            ll = len(row)
-            if (ind >= ll):
-                row.extend([None for ii in range(2 * ind - ll)])
+            self._row_list.append(row)
+        for row in self._row_list:
+            row_len = len(row)
+            if ind >= row_len:
+                row.extend([None] * (2 * ind - row_len))
                 row[ind] = None
-            exec(method.getInline())
-            self.__currentRowIndex += 1
-            # currentRowIndex = self.__currentRowIndex # assigned by never used
-
-    def invokeCategoryMethod(self, type, method, db):
-        self.__currentRowIndex = 0
-        exec(method.getInline())
-
-    def getAttributeLengthMaximumList(self):
-        mList = [0 for i in len(self._attributeNameList)]
-        for row in self._rowList:
-            for indx, val in enumerate(row):
-                mList[indx] = max(mList[indx], len(val))
-        return mList
-
-    def renameAttribute(self, curAttributeName, newAttributeName):
-        """ Change the name of an attribute in place -
-        """
+            # TODO - just say "no" to exec()
+            exec(method.get_inline())
+            self.__current_row_index += 1
+
+    def invoke_category_method(self, method):
+        """Invoke method of current category."""
+        self.__current_row_index = 0
+        # TODO - remove exec()
+        exec(method.get_inline())
+
+    @property
+    def max_attribute_list_length(self):
+        """Get maximum attribute list length."""
+        max_list = [0] * len(self._attribute_name_list)
+        for row in self._row_list:
+            for index, value in enumerate(row):
+                max_list[index] = max(max_list[index], len(value))
+        return max_list
+
+    def rename_attribute(self, current_attribute_name, new_attribute_name):
+        """Change the name of an attribute in place."""
         try:
-            i = self._attributeNameList.index(curAttributeName)
-            self._attributeNameList[i] = newAttributeName
-            del self._catalog[curAttributeName.lower()]
-            self._catalog[newAttributeName.lower()] = newAttributeName
+            i = self._attribute_name_list.index(current_attribute_name)
+            self._attribute_name_list[i] = new_attribute_name
+            del self._catalog[current_attribute_name.lower()]
+            self._catalog[new_attribute_name.lower()] = new_attribute_name
             return True
-        except:
+        except KeyError:
             return False
 
-    def printIt(self, fh=sys.stdout):
-        fh.write("--------------------------------------------\n")
-        fh.write(" Category: %s attribute list length: %d\n" %
-                 (self._name, len(self._attributeNameList)))
-        for at in self._attributeNameList:
-            fh.write(" Category: %s attribute: %s\n" % (self._name, at))
-
-        fh.write(" Row value list length: %d\n" % len(self._rowList))
-        #
-        for row in self._rowList[:2]:
-            #
-            if len(row) == len(self._attributeNameList):
-                for ii, v in enumerate(row):
-                    fh.write(" %30s: %s ...\n" % (self._attributeNameList[ii], str(v)[:30]))
+    def print_it(self, file_=stdout):
+        """Print container information."""
+        file_.write("--------------------------------------------\n")
+        file_.write(
+            " Category: %s attribute list length: %d\n" %
+            (self._name, len(self._attribute_name_list)))
+        for attr in self._attribute_name_list:
+            file_.write(" Category: %s attribute: %s\n" % (self._name, attr))
+
+        file_.write(" Row value list length: %d\n" % len(self._row_list))
+        for row in self._row_list[:2]:
+            if len(row) == len(self._attribute_name_list):
+                for index, value in enumerate(row):
+                    file_.write(" %30s: %s ...\n" % (
+                        self._attribute_name_list[index], str(value)[:30]))
             else:
-                fh.write("+WARNING - %s data length %d attribute name length %s mismatched\n" %
-                         (self._name, len(row), len(self._attributeNameList)))
-
-    def dumpIt(self, fh=sys.stdout):
-        fh.write("--------------------------------------------\n")
-        fh.write(" Category: %s attribute list length: %d\n" %
-                 (self._name, len(self._attributeNameList)))
-        for at in self._attributeNameList:
-            fh.write(" Category: %s attribute: %s\n" % (self._name, at))
-
-        fh.write(" Value list length: %d\n" % len(self._rowList))
-        for row in self._rowList:
-            for ii, v in enumerate(row):
-                fh.write(" %30s: %s\n" % (self._attributeNameList[ii], v))
-
-    def __formatPdbx(self, inp):
-        """ Format input data following PDBx quoting rules -
-        """
+                file_.write(
+                    "+WARNING - %s data length %d attribute name length %s "
+                    "mismatched\n" % (
+                        self._name, len(row),
+                        len(self._attribute_name_list)))
+
+    def dump_it(self, file_=stdout):
+        """Dump contents of container."""
+        file_.write("--------------------------------------------\n")
+        file_.write(
+            " Category: %s attribute list length: %d\n" %
+            (self._name, len(self._attribute_name_list)))
+        for attr in self._attribute_name_list:
+            file_.write(" Category: %s attribute: %s\n" % (self._name, attr))
+
+        file_.write(" Value list length: %d\n" % len(self._row_list))
+        for row in self._row_list:
+            for index, value in enumerate(row):
+                file_.write(" %30s: %s\n" % (
+                    self._attribute_name_list[index], value))
+
+    def __format_pdbx(self, inp):
+        """Format input data following PDBx quoting rules."""
         try:
-            if (inp is None):
+            if inp is None:
                 return ("?", 'DT_NULL_VALUE')
-
             # pure numerical values are returned as unquoted strings
-            if (isinstance(inp, int) or self.__intRe.search(str(inp))):
+            if (isinstance(inp, int) or self.__integer_re.search(str(inp))):
                 return ([str(inp)], 'DT_INTEGER')
-
-            if (isinstance(inp, float) or self.__floatRe.search(str(inp))):
+            if (isinstance(inp, float) or self.__float_re.search(str(inp))):
                 return ([str(inp)], 'DT_FLOAT')
-
-            # null value handling -
-
-            if (inp == "." or inp == "?"):
+            # null value handling
+            if inp in (".", "?"):
                 return ([inp], 'DT_NULL_VALUE')
-
-            if (inp == ""):
+            if inp == "":
                 return (["."], 'DT_NULL_VALUE')
-
             # Contains white space or quotes ?
-            if not self.__wsAndQuotesRe.search(inp):
+            if not self.__whitespace_quotes_re.search(inp):
                 if inp.startswith("_"):
-                    return (self.__doubleQuotedList(inp), 'DT_ITEM_NAME')
+                    return (self.__double_quoted_list(inp), 'DT_ITEM_NAME')
                 else:
                     return ([str(inp)], 'DT_UNQUOTED_STRING')
             else:
-                if self.__nlRe.search(inp):
-                    return (self.__semiColonQuotedList(inp), 'DT_MULTI_LINE_STRING')
+                if self.__newline_re.search(inp):
+                    return (
+                        self.__semicolon_quoted_list(inp),
+                        'DT_MULTI_LINE_STRING')
                 else:
-                    if (self.__avoidEmbeddedQuoting):
+                    if self.__avoid_embedded_quoting:
                         # change priority to choose double quoting where possible.
-                        if not self.__dqRe.search(inp) and not self.__sqWsRe.search(inp):
-                            return (self.__doubleQuotedList(inp), 'DT_DOUBLE_QUOTED_STRING')
-                        elif not self.__sqRe.search(inp) and not self.__dqWsRe.search(inp):
-                            return (self.__singleQuotedList(inp), 'DT_SINGLE_QUOTED_STRING')
+                        if (
+                                not self.__double_quote_re.search(inp) and not
+                                self.__whitespace_single_quote_re.search(inp)):
+                            return (
+                                self.__double_quoted_list(inp),
+                                'DT_DOUBLE_QUOTED_STRING')
+                        elif (
+                                not self.__single_quote_re.search(inp) and not
+                                self.__whitespace_double_quote_re.search(inp)):
+                            return (
+                                self.__single_quoted_list(inp),
+                                'DT_SINGLE_QUOTED_STRING')
                         else:
-                            return (self.__semiColonQuotedList(inp), 'DT_MULTI_LINE_STRING')
+                            return (
+                                self.__semicolon_quoted_list(inp),
+                                'DT_MULTI_LINE_STRING')
                     else:
                         # change priority to choose double quoting where possible.
-                        if not self.__dqRe.search(inp):
-                            return (self.__doubleQuotedList(inp), 'DT_DOUBLE_QUOTED_STRING')
-                        elif not self.__sqRe.search(inp):
-                            return (self.__singleQuotedList(inp), 'DT_SINGLE_QUOTED_STRING')
+                        if not self.__double_quote_re.search(inp):
+                            return (
+                                self.__double_quoted_list(inp),
+                                'DT_DOUBLE_QUOTED_STRING')
+                        elif not self.__single_quote_re.search(inp):
+                            return (
+                                self.__single_quoted_list(inp),
+                                'DT_SINGLE_QUOTED_STRING')
                         else:
-                            return (self.__semiColonQuotedList(inp), 'DT_MULTI_LINE_STRING')
-        except:
+                            return (
+                                self.__semicolon_quoted_list(inp),
+                                'DT_MULTI_LINE_STRING')
+        except ValueError:
             traceback.print_exc(file=self.__lfh)
 
-    def __dataTypePdbx(self, inp):
-        """ Detect the PDBx data type -
-        """
-        if (inp is None):
-            return ('DT_NULL_VALUE')
-
+    def __data_type_pdbx(self, inp):
+        """Detect the PDBx data type."""
+        if inp is None:
+            return 'DT_NULL_VALUE'
         # pure numerical values are returned as unquoted strings
-        if isinstance(inp, int) or self.__intRe.search(str(inp)):
-            return ('DT_INTEGER')
-
-        if isinstance(inp, float) or self.__floatRe.search(str(inp)):
-            return ('DT_FLOAT')
-
-        # null value handling -
-
-        if (inp == "." or inp == "?"):
-            return ('DT_NULL_VALUE')
-
-        if (inp == ""):
-            return ('DT_NULL_VALUE')
-
+        if isinstance(inp, int) or self.__integer_re.search(str(inp)):
+            return 'DT_INTEGER'
+        if isinstance(inp, float) or self.__float_re.search(str(inp)):
+            return 'DT_FLOAT'
+        # null value handling
+        if inp in (".", "?"):
+            return 'DT_NULL_VALUE'
+        if inp == "":
+            return 'DT_NULL_VALUE'
         # Contains white space or quotes ?
-        if not self.__wsAndQuotesRe.search(inp):
+        if not self.__whitespace_quotes_re.search(inp):
             if inp.startswith("_"):
-                return ('DT_ITEM_NAME')
+                return 'DT_ITEM_NAME'
             else:
-                return ('DT_UNQUOTED_STRING')
+                return 'DT_UNQUOTED_STRING'
         else:
-            if self.__nlRe.search(inp):
-                return ('DT_MULTI_LINE_STRING')
+            if self.__newline_re.search(inp):
+                return 'DT_MULTI_LINE_STRING'
             else:
-                if (self.__avoidEmbeddedQuoting):
-                    if not self.__sqRe.search(inp) and not self.__dqWsRe.search(inp):
-                        return ('DT_DOUBLE_QUOTED_STRING')
-                    elif not self.__dqRe.search(inp) and not self.__sqWsRe.search(inp):
-                        return ('DT_SINGLE_QUOTED_STRING')
+                if self.__avoid_embedded_quoting:
+                    if (
+                            not self.__single_quote_re.search(inp) and not
+                            self.__whitespace_double_quote_re.search(inp)):
+                        return 'DT_DOUBLE_QUOTED_STRING'
+                    elif (
+                            not self.__double_quote_re.search(inp) and not
+                            self.__whitespace_single_quote_re.search(inp)):
+                        return 'DT_SINGLE_QUOTED_STRING'
                     else:
-                        return ('DT_MULTI_LINE_STRING')
+                        return 'DT_MULTI_LINE_STRING'
                 else:
-                    if not self.__sqRe.search(inp):
-                        return ('DT_DOUBLE_QUOTED_STRING')
-                    elif not self.__dqRe.search(inp):
-                        return ('DT_SINGLE_QUOTED_STRING')
+                    if not self.__single_quote_re.search(inp):
+                        return 'DT_DOUBLE_QUOTED_STRING'
+                    elif not self.__double_quote_re.search(inp):
+                        return 'DT_SINGLE_QUOTED_STRING'
                     else:
-                        return ('DT_MULTI_LINE_STRING')
-
-    def __singleQuotedList(self, inp):
-        l = []
-        l.append("'")
-        l.append(inp)
-        l.append("'")
-        return(l)
-
-    def __doubleQuotedList(self, inp):
-        l = []
-        l.append('"')
-        l.append(inp)
-        l.append('"')
-        return(l)
-
-    def __semiColonQuotedList(self, inp):
-        l = []
-        l.append("\n")
+                        return 'DT_MULTI_LINE_STRING'
+
+    @staticmethod
+    def __single_quoted_list(inp):
+        """Generate a single-quoted list from the input."""
+        return ["'"] + [inp] + ["'"]
+
+    @staticmethod
+    def __double_quoted_list(inp):
+        """Generate a double-quoted list from the input."""
+        return ['"'] + [inp] + ['"']
+
+    @staticmethod
+    def __semicolon_quoted_list(inp):
+        """Generate a semicolon-delimited quoted list from the input."""
         if inp[-1] == '\n':
-            l.append(";")
-            l.append(inp)
-            l.append(";")
-            l.append("\n")
+            return ["\n", ";"] + [inp] + [";", "\n"]
         else:
-            l.append(";")
-            l.append(inp)
-            l.append("\n")
-            l.append(";")
-            l.append("\n")
-        return(l)
-
-    def getValueFormatted(self, attributeName=None, rowIndex=None):
-        if attributeName is None:
-            attribute = self.__currentAttribute
-        else:
-            attribute = attributeName
+            return ["\n", ";"] + [inp] + ["\n", ";", "\n"]
 
-        if rowIndex is None:
-            rowI = self.__currentRowIndex
+    def get_value_formatted(self, attribute_name=None, row_index=None):
+        """Get formatted version of value."""
+        if attribute_name is None:
+            attribute = self.__current_attribute
         else:
-            rowI = rowIndex
-
-        if isinstance(attribute, str) and isinstance(rowI, int):
+            attribute = attribute_name
+        if row_index is None:
+            index = self.__current_row_index
+        else:
+            index = row_index
+        if isinstance(attribute, str) and isinstance(index, int):
             try:
-                list, type = self.__formatPdbx(
-                    self._rowList[rowI][self._attributeNameList.index(attribute)])
-                return "".join(list)
-            except (IndexError):
+                list_, _ = self.__format_pdbx(
+                    self._row_list[index][
+                        self._attribute_name_list.index(attribute)])
+                return "".join(list_)
+            except IndexError:
                 self.__lfh.write(
-                    "attributeName %s rowI %r rowdata %r\n" %
-                    (attributeName, rowI, self._rowList[rowI]))
+                    "attribute_name %s index %r rowdata %r\n" %
+                    (attribute_name, index, self._row_list[index]))
                 raise IndexError
         raise TypeError(attribute)
 
-    def getValueFormattedByIndex(self, attributeIndex, rowIndex):
-        try:
-            list, type = self.__formatPdbx(self._rowList[rowIndex][attributeIndex])
-            return "".join(list)
-        except (IndexError):
-            raise IndexError
-
-    def getAttributeValueMaxLengthList(self, steps=1):
-        mList = [0 for i in range(len(self._attributeNameList))]
-        for row in self._rowList[::steps]:
-            for indx in range(len(self._attributeNameList)):
-                val = row[indx]
-                mList[indx] = max(mList[indx], len(str(val)))
-        return mList
-
-    def getFormatTypeList(self, steps=1):
+    def get_value_formatted_by_index(self, attribute_index, row_index):
+        """Get value formatted by index."""
+        list_, _ = self.__format_pdbx(
+            self._row_list[row_index][attribute_index])
+        return "".join(list_)
+
+    def get_max_attribute_list_length(self, steps=1):
+        """Get maximum length of attribute value list."""
+        max_list = [0] * len(self._attribute_name_list)
+        for _ in self._row_list[::steps]:
+            for index, value in enumerate(self._attribute_name_list):
+                max_list[index] = max(max_list[index], len(str(value)))
+        return max_list
+
+    def get_format_type_list(self, steps=1):
+        """Get a formatted type list."""
         try:
-            curDataTypeList = ['DT_NULL_VALUE' for i in range(len(self._attributeNameList))]
-            for row in self._rowList[::steps]:
-                for indx in range(len(self._attributeNameList)):
-                    val = row[indx]
-                    # print "index ", indx, " val ", val
-                    dType = self.__dataTypePdbx(val)
-                    dIndx = self.__dataTypeList.index(dType)
-                    # print "d type", dType, " d type index ", dIndx
-                    cType = curDataTypeList[indx]
-                    cIndx = self.__dataTypeList.index(cType)
-                    cIndx = max(cIndx, dIndx)
-                    curDataTypeList[indx] = self.__dataTypeList[cIndx]
-
+            current_data_type_list = ['DT_NULL_VALUE'] * len(
+                self._attribute_name_list)
+            for row in self._row_list[::steps]:
+                for index, value in enumerate(self._attribute_name_list):
+                    data_type = self.__data_type_pdbx(value)
+                    data_index = self.__data_type_list.index(data_type)
+                    current_type = current_data_type_list[index]
+                    current_index = self.__data_type_list.index(
+                        current_type)
+                    current_index = max(current_index, data_index)
+                    current_data_type_list[index] = self.__data_type_list[
+                        current_index]
             # Map the format types to the data types
-            curFormatTypeList = []
-            for dt in curDataTypeList:
-                ii = self.__dataTypeList.index(dt)
-                curFormatTypeList.append(self.__formatTypeList[ii])
-        except:
+            current_format_type_list = []
+            for data_type in current_data_type_list:
+                index = self.__data_type_list.index(data_type)
+                current_format_type_list.append(self.__format_type_list[index])
+        except IndexError:
             self.__lfh.write(
-                "PdbxDataCategory(getFormatTypeList) ++Index error at index %d in row %r\n" %
-                (indx, row))
-
-        return curFormatTypeList, curDataTypeList
-
-    def getFormatTypeListX(self):
-        curDataTypeList = ['DT_NULL_VALUE' for i in range(len(self._attributeNameList))]
-        for row in self._rowList:
-            for indx in range(len(self._attributeNameList)):
-                val = row[indx]
-                # print "index ", indx, " val ", val
-                dType = self.__dataTypePdbx(val)
-                dIndx = self.__dataTypeList.index(dType)
-                # print "d type", dType, " d type index ", dIndx
-
-                cType = curDataTypeList[indx]
-                cIndx = self.__dataTypeList.index(cType)
-                cIndx = max(cIndx, dIndx)
-                curDataTypeList[indx] = self.__dataTypeList[cIndx]
-
+                "PdbxDataCategory(get_format_type_list) ++Index error at "
+                "index %d in row %r\n" %
+                (index, row))
+        return current_format_type_list, current_data_type_list
+
+    @property
+    def get_format_type_list_x(self):
+        """Alternate version of format type list."""
+        current_data_type_list = ['DT_NULL_VALUE'] * len(
+            self._attribute_name_list)
+        for _ in self._row_list:
+            for index, value in enumerate(self._attribute_name_list):
+                data_type = self.__data_type_pdbx(value)
+                data_index = self.__data_type_list.index(data_type)
+                current_type = current_data_type_list[index]
+                current_index = self.__data_type_list.index(current_type)
+                current_index = max(current_index, data_index)
+                current_data_type_list[index] = self.__data_type_list[
+                    current_index]
         # Map the format types to the data types
-        curFormatTypeList = []
-        for dt in curDataTypeList:
-            ii = self.__dataTypeList.index(dt)
-            curFormatTypeList.append(self.__formatTypeList[ii])
-        return curFormatTypeList, curDataTypeList
+        current_format_type_list = []
+        for data_type in current_data_type_list:
+            index = self.__data_type_list.index(data_type)
+            current_format_type_list.append(self.__format_type_list[index])
+        return current_format_type_list, current_data_type_list
diff --git a/pdbx/reader/reader.py b/pdbx/reader/reader.py
index 4aad948..d47fbce 100644
--- a/pdbx/reader/reader.py
+++ b/pdbx/reader/reader.py
@@ -9,52 +9,45 @@
 # 2012-09-02 - (jdw) Revise tokenizer to better handle embedded quoting.
 #
 ##
-"""
-PDBx/mmCIF dictionary and data file parser.
+"""PDBx/mmCIF dictionary and data file parser.
 
 Acknowledgements:
 
- The tokenizer used in this module is modeled after the clever parser design
- used in the PyMMLIB package.
-
- PyMMLib Development Group
- Authors: Ethan Merritt: merritt@u.washington.ed & Jay Painter: jay.painter@gmail.com
- See: http://pymmlib.sourceforge.net/
+The tokenizer used in this module is modeled after the clever parser design
+used in the PyMMLIB package.
 
+PyMMLib Development Group
+Authors: Ethan Merritt: merritt@u.washington.ed & Jay Painter: jay.painter@gmail.com
+See: http://pymmlib.sourceforge.net/
 """
 import re
 from .containers import DataCategory, DefinitionContainer, DataContainer
 
 
 class PdbxError(Exception):
-    """ Class for catch general errors
-    """
-    pass
+    """Class for catching general errors."""
+
 
+class PdbxSyntaxError(Exception):
+    """Class for catching syntax errors."""
 
-class SyntaxError(Exception):
-    """ Class for catching syntax errors
-    """
-    def __init__(self, lineNumber, text):
-        Exception.__init__(self)
-        self.lineNumber = lineNumber
+    def __init__(self, line_number, text):
+        super().__init__(self)
+        self.line_number = line_number
         self.text = text
 
     def __str__(self):
-        return "%%ERROR - [at line: %d] %s" % (self.lineNumber, self.text)
+        return "%%ERROR - [at line: %d] %s" % (self.line_number, self.text)
 
 
-class PdbxReader(object):
-    """ PDBx reader for data files and dictionaries.
+class PdbxReader:
+    """PDBx reader for data files and dictionaries."""
 
-    """
-    def __init__(self, ifh):
-        """ ifh - input file handle returned by open()
-        """
-        #
-        self.__curLineNumber = 0
-        self.__ifh = ifh
-        self.__stateDict = {
+    def __init__(self, input_file):
+        """Initialize with input file handle; e.g. as returned by open()."""
+        self.__current_line_number = 0
+        self.__input_file = input_file
+        self.__state_dict = {
             "data": "ST_DATA_CONTAINER",
             "loop": "ST_TABLE",
             "global": "ST_GLOBAL_CONTAINER",
@@ -62,409 +55,356 @@ class PdbxReader(object):
             "stop": "ST_STOP"
         }
 
-    def read(self, containerList):
-        """
-        Appends to the input list of definition and data containers.
-
-        """
-        self.__curLineNumber = 0
+    def read(self, container_list):
+        """Appends to the input list of definition and data containers."""
+        self.__current_line_number = 0
         try:
-            self.__parser(self.__tokenizer(self.__ifh), containerList)
+            self.__parser(self.__tokenizer(self.__input_file), container_list)
         except StopIteration:
             pass
         else:
             raise PdbxError()
 
-    def __syntaxError(self, errText):
-        raise SyntaxError(self.__curLineNumber, errText)
+    def __syntax_error(self, error_text):
+        """Raise a PdbxSyntaxError."""
+        raise PdbxSyntaxError(self.__current_line_number, error_text)
 
-    def __getContainerName(self, inWord):
-        """ Returns the name of the data_ or save_ container
-        """
-        return str(inWord[5:]).strip()
+    @staticmethod
+    def __get_container_name(in_word):
+        """Returns the name of the data_ or save_ container."""
+        return str(in_word[5:]).strip()
 
-    def __getState(self, inWord):
+    def __get_state(self, in_word):
         """Identifies reserved syntax elements and assigns an associated state.
 
-           Returns: (reserved word, state)
-           where -
-              reserved word - is one of CIF syntax elements:
-                               data_, loop_, global_, save_, stop_
-              state - the parser state required to process this next section.
+        Returns: (reserved word, state) where:
+            reserved word - is one of CIF syntax elements:
+                            data_, loop_, global_, save_, stop_
+            state - the parser state required to process this next section.
         """
-        i = inWord.find("_")
+        i = in_word.find("_")
         if i == -1:
             return None, "ST_UNKNOWN"
-
         try:
-            rWord = inWord[:i].lower()
-            return rWord, self.__stateDict[rWord]
-        except:
+            reserved_word = in_word[:i].lower()
+            return reserved_word, self.__state_dict[reserved_word]
+        except KeyError:
             return None, "ST_UNKNOWN"
 
-    def __parser(self, tokenizer, containerList):
+    def __parser(self, tokenizer, container_list):
         """ Parser for PDBx data files and dictionaries.
 
-            Input - tokenizer() reentrant method recognizing
-                    data item names (_category.attribute)
-                    quoted strings (single, double and multi-line semi-colon delimited),
-                    and unquoted strings.
-
-                    containerList - list-type container for data and definition objects parsed
-                                     from from the input file.
-
-            Return:
-                    containerList - is appended with data and definition objects -
+            Args:
+                tokenizer: reentrant method recognizing data item names
+                    (_category.attribute), quoted strings (single, double and
+                    multi-line semi-colon delimited), and unquoted strings.
+                container_list: list-type container for data and definition
+                    objects parsed from from the input file.
+            Returns:
+                container_list - is appended with data and definition objects
         """
         # Working container - data or definition
-        curContainer = None
-        #
+        current_container = None
         # Working category container
-        categoryIndex = {}
-        curCategory = None
-        #
-        curRow = None
+        category_index = {}
+        current_category = None
+        current_row = None
         state = None
 
         # Find the first reserved word and begin capturing data.
-        #
         while True:
-            curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
-            if curWord is None:
+            current_category_name, current_attribute_name, \
+                current_quoted_string, current_word = next(tokenizer)
+            if current_word is None:
                 continue
-            reservedWord, state = self.__getState(curWord)
-            if reservedWord is not None:
+            reserved_word, state = self.__get_state(current_word)
+            if reserved_word is not None:
                 break
 
         while True:
-            #
-            # Set the current state -
-            #
-            # At this point in the processing cycle we are expecting a token containing
-            # either a '_category.attribute' or a reserved word.
-            #
-            if curCatName is not None:
+            # Set the current state: at this point in the processing cycle we
+            # are expecting a token containing # either a '_category.attribute'
+            # or a reserved word.
+            if current_category_name is not None:
                 state = "ST_KEY_VALUE_PAIR"
-            elif curWord is not None:
-                reservedWord, state = self.__getState(curWord)
+            elif current_word is not None:
+                reserved_word, state = self.__get_state(current_word)
             else:
-                self.__syntaxError("Miscellaneous syntax error")
+                self.__syntax_error("Miscellaneous syntax error")
                 return
 
-            #
             # Process _category.attribute value assignments
-            #
             if state == "ST_KEY_VALUE_PAIR":
                 try:
-                    curCategory = categoryIndex[curCatName]
+                    current_category = category_index[current_category_name]
                 except KeyError:
-                    # A new category is encountered - create a container and add a row
-                    curCategory = categoryIndex[curCatName] = DataCategory(curCatName)
-
+                    # A new category is encountered - create a container and
+                    # add a row
+                    category_index[current_category_name] = DataCategory(
+                        current_category_name)
+                    current_category = category_index[current_category_name]
                     try:
-                        curContainer.append(curCategory)
+                        current_container.append(current_category)
                     except AttributeError:
-                        self.__syntaxError("Category cannot be added to data_ block")
+                        self.__syntax_error(
+                            "Category cannot be added to data_ block")
                         return
-
-                    curRow = []
-                    curCategory.append(curRow)
+                    current_row = []
+                    current_category.append(current_row)
                 else:
                     # Recover the existing row from the category
                     try:
-                        curRow = curCategory[0]
+                        current_row = current_category[0]
                     except IndexError:
-                        self.__syntaxError("Internal index error accessing category data")
+                        self.__syntax_error(
+                            "Internal index error accessing category data")
                         return
-
                 # Check for duplicate attributes and add attribute to table.
-                if curAttName in curCategory.getAttributeList():
-                    self.__syntaxError("Duplicate attribute encountered in category")
+                if current_attribute_name in current_category.attribute_list:
+                    self.__syntax_error(
+                        "Duplicate attribute encountered in category")
                     return
                 else:
-                    curCategory.appendAttribute(curAttName)
-
+                    current_category.append_attribute(current_attribute_name)
                 # Get the data for this attribute from the next token
-                tCat, tAtt, curQuotedString, curWord = next(tokenizer)
-
-                if tCat is not None or (curQuotedString is None and curWord is None):
-                    self.__syntaxError("Missing data for item _%s.%s" % (curCatName, curAttName))
-
-                if curWord is not None:
-                    #
-                    # Validation check token for misplaced reserved words -
-                    #
-                    reservedWord, state = self.__getState(curWord)
-                    if reservedWord is not None:
-                        self.__syntaxError("Unexpected reserved word: %s" % (reservedWord))
-
-                    curRow.append(curWord)
-
-                elif curQuotedString is not None:
-                    curRow.append(curQuotedString)
-
+                tok_category, _, current_quoted_string, current_word = next(
+                    tokenizer)
+                if (tok_category is not None or (
+                        current_quoted_string is None
+                        and current_word is None)):
+                    self.__syntax_error(
+                        "Missing data for item _%s.%s" % (
+                            current_category_name, current_attribute_name))
+                if current_word is not None:
+                    # Validation check token for misplaced reserved words
+                    reserved_word, state = self.__get_state(current_word)
+                    if reserved_word is not None:
+                        self.__syntax_error(
+                            "Unexpected reserved word: %s" % (reserved_word))
+                    current_row.append(current_word)
+                elif current_quoted_string is not None:
+                    current_row.append(current_quoted_string)
                 else:
-                    self.__syntaxError("Missing value in item-value pair")
-
+                    self.__syntax_error("Missing value in item-value pair")
                 try:
-                    curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
+                    current_category_name, current_attribute_name, \
+                        current_quoted_string, current_word = next(tokenizer)
                 except RuntimeError as err:
                     raise StopIteration(err)
                 continue
 
-            #
-            # Process a loop_ declaration and associated data -
-            #
+            # Process a loop_ declaration and associated data
             elif state == "ST_TABLE":
-
-                # The category name in the next curCatName, curAttName pair
-                # defines the name of the category container.
-                curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
-
-                if curCatName is None or curAttName is None:
-                    self.__syntaxError("Unexpected token in loop_ declaration")
+                # The category name in the next current_category_name,
+                # current_attribute_name pair defines the name of the category
+                # container.
+                current_category_name, current_attribute_name, \
+                    current_quoted_string, current_word = next(tokenizer)
+                if (current_category_name is None) or (
+                        current_attribute_name is None):
+                    self.__syntax_error(
+                        "Unexpected token in loop_ declaration")
                     return
-
                 # Check for a previous category declaration.
-                if curCatName in categoryIndex:
-                    self.__syntaxError("Duplicate category declaration in loop_")
+                if current_category_name in category_index:
+                    self.__syntax_error(
+                        "Duplicate category declaration in loop_")
                     return
-
-                curCategory = DataCategory(curCatName)
-
+                current_category = DataCategory(current_category_name)
                 try:
-                    curContainer.append(curCategory)
+                    current_container.append(current_category)
                 except AttributeError:
-                    self.__syntaxError("loop_ declaration outside of data_ block or save_ frame")
+                    self.__syntax_error(
+                        "loop_ declaration outside of data_ block or save_ "
+                        "frame")
                     return
-
-                curCategory.appendAttribute(curAttName)
-
+                current_category.append_attribute(current_attribute_name)
                 # Read the rest of the loop_ declaration
                 while True:
-                    curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
-
-                    if curCatName is None:
+                    current_category_name, current_attribute_name, \
+                        current_quoted_string, current_word = next(tokenizer)
+                    if current_category_name is None:
                         break
-
-                    if curCatName != curCategory.getName():
-                        self.__syntaxError("Changed category name in loop_ declaration")
+                    if current_category_name != current_category.name:
+                        self.__syntax_error(
+                            "Changed category name in loop_ declaration")
                         return
-
-                    curCategory.appendAttribute(curAttName)
-
-                # If the next token is a 'word', check it for any reserved words -
-                if curWord is not None:
-                    reservedWord, state = self.__getState(curWord)
-                    if reservedWord is not None:
-                        if reservedWord == "stop":
+                    current_category.append_attribute(current_attribute_name)
+                # If the next token is a 'word', check it for any reserved
+                # words
+                if current_word is not None:
+                    reserved_word, state = self.__get_state(current_word)
+                    if reserved_word is not None:
+                        if reserved_word == "stop":
                             return
                         else:
-                            self.__syntaxError(
-                                "Unexpected reserved word after loop declaration: %s" %
-                                (reservedWord))
-
-                # Read the table of data for this loop_ -
+                            self.__syntax_error(
+                                "Unexpected reserved word after loop "
+                                "declaration: %s" % (reserved_word))
+                # Read the table of data for this loop_
                 while True:
-                    curRow = []
-                    curCategory.append(curRow)
-
-                    for tAtt in curCategory.getAttributeList():
-                        if curWord is not None:
-                            curRow.append(curWord)
-                        elif curQuotedString is not None:
-                            curRow.append(curQuotedString)
-
+                    current_row = []
+                    current_category.append(current_row)
+                    for _ in current_category.attribute_list:
+                        if current_word is not None:
+                            current_row.append(current_word)
+                        elif current_quoted_string is not None:
+                            current_row.append(current_quoted_string)
                         try:
-                            curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
+                            current_category_name, current_attribute_name, \
+                                current_quoted_string, current_word = next(
+                                    tokenizer)
                         except RuntimeError as err:
                             raise StopIteration(err)
-
-                    # loop_ data processing ends if -
-
-                    # A new _category.attribute is encountered
-                    if curCatName is not None:
+                    # loop_ data processing ends if a new _category.attribute
+                    # is encountered
+                    if current_category_name is not None:
                         break
-
                     # A reserved word is encountered
-                    if curWord is not None:
-                        reservedWord, state = self.__getState(curWord)
-                        if reservedWord is not None:
+                    if current_word is not None:
+                        reserved_word, state = self.__get_state(current_word)
+                        if reserved_word is not None:
                             break
-
                 continue
-
             elif state == "ST_DEFINITION":
                 # Ignore trailing unnamed saveframe delimiters e.g. 'save_'
-                sName = self.__getContainerName(curWord)
-                if (len(sName) > 0):
-                    curContainer = DefinitionContainer(sName)
-                    containerList.append(curContainer)
-                    categoryIndex = {}
-                    curCategory = None
-
-                curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
-
+                state_name = self.__get_container_name(current_word)
+                if len(state_name) > 0:
+                    current_container = DefinitionContainer(state_name)
+                    container_list.append(current_container)
+                    category_index = {}
+                    current_category = None
+                current_category_name, current_attribute_name, \
+                    current_quoted_string, current_word = next(tokenizer)
             elif state == "ST_DATA_CONTAINER":
-                #
-                dName = self.__getContainerName(curWord)
-                if len(dName) == 0:
-                    dName = "unidentified"
-                curContainer = DataContainer(dName)
-                containerList.append(curContainer)
-                categoryIndex = {}
-                curCategory = None
-                curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
-
+                data_name = self.__get_container_name(current_word)
+                if len(data_name) == 0:
+                    data_name = "unidentified"
+                current_container = DataContainer(data_name)
+                container_list.append(current_container)
+                category_index = {}
+                current_category = None
+                current_category_name, current_attribute_name, \
+                    current_quoted_string, current_word = next(tokenizer)
             elif state == "ST_STOP":
                 return
             elif state == "ST_GLOBAL":
-                curContainer = DataContainer("blank-global")
-                curContainer.setGlobal()
-                containerList.append(curContainer)
-                categoryIndex = {}
-                curCategory = None
-                curCatName, curAttName, curQuotedString, curWord = next(tokenizer)
-
+                current_container = DataContainer("blank-global")
+                current_container.set_global()
+                container_list.append(current_container)
+                category_index = {}
+                current_category = None
+                current_category_name, current_attribute_name, \
+                    current_quoted_string, current_word = next(tokenizer)
             elif state == "ST_UNKNOWN":
-                self.__syntaxError("Unrecogized syntax element: " + str(curWord))
+                self.__syntax_error(
+                    "Unrecognized syntax element: " + str(current_word))
                 return
 
-    def __tokenizer(self, ifh):
-        """ Tokenizer method for the mmCIF syntax file -
-
-            Each return/yield from this method returns information about
-            the next token in the form of a tuple with the following structure.
-
-            (category name, attribute name, quoted strings, words w/o quotes or white space)
-
-            Differentiated the reqular expression to the better handle embedded quotes.
+    def __tokenizer(self, input_file):
+        """Tokenizer method for the mmCIF syntax file.
 
+        Each return/yield from this method returns information about the next
+        token in the form of a tuple with the following structure:
+            (category name, attribute name, quoted strings, words w/o quotes
+            or white space)
+        Differentiated the regular expression to the better handle embedded
+        quotes.
         """
-        #
-        # Regex definition for mmCIF syntax - semi-colon delimited strings are handled
-        # outside of this regex.
-        mmcifRe = re.compile(
+        # Regex definition for mmCIF syntax - semi-colon delimited strings are
+        # handled outside of this regex.
+        mmcif_re = re.compile(
             r"(?:"
-
-            "(?:_(.+?)[.](\S+))" "|"  # _category.attribute
-
-            "(?:['](.*?)(?:[']\s|[']$))" "|"  # single quoted strings
-            "(?:[\"](.*?)(?:[\"]\s|[\"]$))" "|"  # double quoted strings
-
-            "(?:\s*#.*$)" "|"  # comments (dumped)
-
-            "(\S+)"  # unquoted words
-
-            ")"
+            r"(?:_(.+?)[.](\S+))" "|"  # _category.attribute
+            r"(?:['](.*?)(?:[']\s|[']$))" "|"  # single quoted strings
+            r"(?:[\"](.*?)(?:[\"]\s|[\"]$))" "|"  # double quoted strings
+            r"(?:\s*#.*$)" "|"  # comments (dumped)
+            r"(\S+)" # unquoted words
+            r")"
         )
-
-        fileIter = iter(ifh)
-
-        # Tokenizer loop begins here ---
+        file_iterator = iter(input_file)
+        # Tokenizer loop begins here
         while True:
-            line = next(fileIter)
-            self.__curLineNumber += 1
-
+            line = next(file_iterator)
+            self.__current_line_number += 1
             # Dump comments
             if line.startswith("#"):
                 continue
-
-            # Gobble up the entire semi-colon/multi-line delimited string and
+            # Gobble up the entire semi-colon/multi-line-delimited string and
             # and stuff this into the string slot in the return tuple
-            #
             if line.startswith(";"):
-                mlString = [line[1:]]
+                multiline_string = [line[1:]]
                 while True:
-                    line = next(fileIter)
-                    self.__curLineNumber += 1
+                    line = next(file_iterator)
+                    self.__current_line_number += 1
                     if line.startswith(";"):
                         break
-                    mlString.append(line)
-
+                    multiline_string.append(line)
                 # remove trailing new-line that is part of the \n; delimiter
-                mlString[-1] = mlString[-1].rstrip()
-                #
-                yield (None, None, "".join(mlString), None)
-                #
+                multiline_string[-1] = multiline_string[-1].rstrip()
+                yield (None, None, "".join(multiline_string), None)
                 # Need to process the remainder of the current line -
                 line = line[1:]
-                # continue
 
             # Apply regex to the current line consolidate the single/double
             # quoted within the quoted string category
-            for it in mmcifRe.finditer(line):
-                tgroups = it.groups()
-                if tgroups != (None, None, None, None, None):
-                    if tgroups[2] is not None:
-                        qs = tgroups[2]
-                    elif tgroups[3] is not None:
-                        qs = tgroups[3]
+            for match in mmcif_re.finditer(line):
+                match_groups = match.groups()
+                if match_groups != (None, None, None, None, None):
+                    if match_groups[2] is not None:
+                        quoted_string = match_groups[2]
+                    elif match_groups[3] is not None:
+                        quoted_string = match_groups[3]
                     else:
-                        qs = None
-                    groups = (tgroups[0], tgroups[1], qs, tgroups[4])
+                        quoted_string = None
+                    groups = (
+                        match_groups[0], match_groups[1], quoted_string,
+                        match_groups[4])
                     yield groups
 
-    def __tokenizerOrg(self, ifh):
-        """ Tokenizer method for the mmCIF syntax file -
-
-            Each return/yield from this method returns information about
-            the next token in the form of a tuple with the following structure.
-
-            (category name, attribute name, quoted strings, words w/o quotes or white space)
+    def __tokenizer_org(self, input_file):
+        """Tokenizer method for the mmCIF syntax file.
 
+        Each return/yield from this method returns information about the next
+        token in the form of a tuple with the following structure:
+            (category name, attribute name, quoted strings, words w/o quotes
+            or white space)
         """
-        #
-        # Regex definition for mmCIF syntax - semi-colon delimited strings are handled
-        # outside of this regex.
-        mmcifRe = re.compile(
+        # Regex definition for mmCIF syntax - semi-colon delimited strings are
+        # handled outside of this regex.
+        mmcif_re = re.compile(
             r"(?:"
-
-            "(?:_(.+?)[.](\S+))" "|"  # _category.attribute
-
-            "(?:['\"](.*?)(?:['\"]\s|['\"]$))" "|"  # quoted strings
-
-            "(?:\s*#.*$)" "|"  # comments (dumped)
-
-            "(\S+)"  # unquoted words
-
-            ")"
+            r"(?:_(.+?)[.](\S+))" "|" # _category.attribute
+            r"(?:['\"](.*?)(?:['\"]\s|['\"]$))" "|" # quoted strings
+            r"(?:\s*#.*$)" "|"  # comments (dumped)
+            r"(\S+)"  # unquoted words
+            r")"
         )
-
-        fileIter = iter(ifh)
-
-        # Tokenizer loop begins here ---
+        file_iterator = iter(input_file)
+        # Tokenizer loop begins here
         while True:
-            line = next(fileIter)
-            self.__curLineNumber += 1
-
+            line = next(file_iterator)
+            self.__current_line_number += 1
             # Dump comments
             if line.startswith("#"):
                 continue
-
             # Gobble up the entire semi-colon/multi-line delimited string and
             # and stuff this into the string slot in the return tuple
-            #
             if line.startswith(";"):
-                mlString = [line[1:]]
+                multiline_string = [line[1:]]
                 while True:
-                    line = next(fileIter)
-                    self.__curLineNumber += 1
+                    line = next(file_iterator)
+                    self.__current_line_number += 1
                     if line.startswith(";"):
                         break
-                    mlString.append(line)
-
+                    multiline_string.append(line)
                 # remove trailing new-line that is part of the \n; delimiter
-                mlString[-1] = mlString[-1].rstrip()
-                #
-                yield (None, None, "".join(mlString), None)
-                #
-                # Need to process the remainder of the current line -
+                multiline_string[-1] = multiline_string[-1].rstrip()
+                yield (None, None, "".join(multiline_string), None)
+                # Need to process the remainder of the current line
                 line = line[1:]
-                # continue
-
             # Apply regex to the current line
-            for it in mmcifRe.finditer(line):
-                groups = it.groups()
+            for match in mmcif_re.finditer(line):
+                groups = match.groups()
                 if groups != (None, None, None, None):
                     yield groups
diff --git a/pdbx/writer/writer.py b/pdbx/writer/writer.py
index f4db730..5549905 100644
--- a/pdbx/writer/writer.py
+++ b/pdbx/writer/writer.py
@@ -7,187 +7,163 @@
 # 23-Oct-2012 jdw update path details and reorganize.
 #
 ###
-"""
-Classes for writing data and dictionary containers in PDBx/mmCIF format.
-
-"""
-import sys
-from pdbx.reader.containers import DefinitionContainer, DataContainer
+"""Classes for writing data and dictionary containers in PDBx/mmCIF format."""
+from sys import stdout
+from ..reader.containers import DefinitionContainer, DataContainer
 
 __docformat__ = "restructuredtext en"
 __author__ = "John Westbrook"
 __email__ = "jwest@rcsb.rutgers.edu"
-__license__ = "Creative Commons Attribution 3.0 Unported"
-__version__ = "V0.01"
+
+
+MAXIMUM_LINE_LENGTH = 2048
+SPACING = 2
+INDENT_DEFINITION = 3
+DO_DEFINITION_INDENT = False
 
 
 class PdbxError(Exception):
-    """ Class for catch general errors
+    """Class for catch general errors.
+    TODO - consolidate error classes.
     """
-    pass
 
 
-class PdbxWriter(object):
-    """Write PDBx data files or dictionaries using the input container
-       or container list.
+class PdbxWriter:
+    """Write PDBx data files or dictionaries.
+    Use the input container or container list.
     """
-    def __init__(self, ofh=sys.stdout):
-        self.__ofh = ofh
-        self.__containerList = []
-        self.__MAXIMUM_LINE_LENGTH = 2048
-        self.__SPACING = 2
-        self.__INDENT_DEFINITION = 3
-        self.__indentSpace = " " * self.__INDENT_DEFINITION
-        self.__doDefinitionIndent = False
+    def __init__(self, output_file=stdout):
+        self.__output_file = output_file
+        self.__container_list = []
+        self.__maximum_line_length = MAXIMUM_LINE_LENGTH
+        self.__spacing = SPACING
+        self.__indent_definition = INDENT_DEFINITION
+        self.__indent_space = " " * self.__indent_definition
+        self._do_definition_indent = DO_DEFINITION_INDENT
         # Maximum number of rows checked for value length and format
-        self.__rowPartition = None
+        self.__row_partition = None
 
-    def setRowPartition(self, numRows):
-        ''' Maximum number of rows checked for value length and format
-        '''
-        self.__rowPartition = numRows
+    def set_row_partition(self, num_rows):
+        """Maximum number of rows checked for value length and format."""
+        self.__row_partition = num_rows
 
-    def write(self, containerList):
-        self.__containerList = containerList
-        for container in self.__containerList:
-            self.writeContainer(container)
+    def write(self, container_list):
+        """Write out a list of containers."""
+        self.__container_list = container_list
+        for container in self.__container_list:
+            self.write_container(container)
 
-    def writeContainer(self, container):
-        indS = " " * self.__INDENT_DEFINITION
+    def write_container(self, container):
+        """Write out information for an individual container."""
+        indent_string = " " * self.__indent_definition
         if isinstance(container, DefinitionContainer):
-            self.__write("save_%s\n" % container.getName())
-            self.__doDefinitionIndent = True
-            self.__write(indS + "#\n")
+            self.__write("save_%s\n" % container.name)
+            self._do_definition_indent = True
+            self.__write(indent_string + "#\n")
         elif isinstance(container, DataContainer):
-            if (container.getGlobal()):
+            if container.get_global():
                 self.__write("global_\n")
-                self.__doDefinitionIndent = False
+                self._do_definition_indent = False
                 self.__write("\n")
             else:
-                self.__write("data_%s\n" % container.getName())
-                self.__doDefinitionIndent = False
+                self.__write("data_%s\n" % container.name)
+                self._do_definition_indent = False
                 self.__write("#\n")
-
-        for nm in container.getObjNameList():
-            obj = container.getObj(nm)
-            objL = obj.getRowList()
-
+        for name in container.get_object_name_list():
+            obj = container.get_object(name)
+            object_list = obj.row_list
             # Skip empty objects
-            if len(objL) == 0:
+            if len(object_list) == 0:
                 continue
-
             # Item - value formattting
-            elif len(objL) == 1:
-                self.__writeItemValueFormat(obj)
-
-            # Table formatting -
-            elif len(objL) > 1 and len(obj.getAttributeList()) > 0:
-                self.__writeTableFormat(obj)
+            if len(object_list) == 1:
+                self.__write_item_value_format(obj)
+            # Table formatting
+            elif len(object_list) > 1 and len(obj.attribute_list) > 0:
+                self.__write_table_format(obj)
             else:
-                raise PdbxError()
-
-            if self.__doDefinitionIndent:
-                self.__write(indS + "#")
+                raise PdbxError(
+                    "len(object_list) = %d and len(obj.attribute_list) = %d" %
+                    (len(object_list), len(obj.attribute_list)))
+            if self._do_definition_indent:
+                self.__write(indent_string + "#")
             else:
                 self.__write("#")
-
         # Add a trailing saveframe reserved word
         if isinstance(container, DefinitionContainer):
             self.__write("\nsave_\n")
         self.__write("#\n")
 
-    def __write(self, st):
-        self.__ofh.write(st)
-
-    def __writeItemValueFormat(self, myCategory):
+    def __write(self, string_):
+        """Write a string."""
+        self.__output_file.write(string_)
 
+    def __write_item_value_format(self, category):
+        """Write items and values for the given category."""
         # Compute the maximum item name length within this category -
-        attributeNameLengthMax = 0
-        for attributeName in myCategory.getAttributeList():
-            attributeNameLengthMax = max(attributeNameLengthMax, len(attributeName))
-        itemNameLengthMax = self.__SPACING + len(myCategory.getName()) + attributeNameLengthMax + 2
-        #
-        lineList = []
-        lineList.append("#\n")
-        for attributeName, iPos in myCategory.getAttributeListWithOrder():
-            if self.__doDefinitionIndent:
+        attribute_name_max_length = 0
+        for attribute_name in category.attribute_list:
+            attribute_name_max_length = max(
+                attribute_name_max_length, len(attribute_name))
+        item_name_max_length = (
+            self.__spacing + len(category.name)
+            + attribute_name_max_length + 2)
+        line_list = []
+        line_list.append("#\n")
+        for attribute_name, _ in category.attribute_list_with_order:
+            if self._do_definition_indent:
                 # - add indent --
-                lineList.append(self.__indentSpace)
-
-            itemName = "_%s.%s" % (myCategory.getName(), attributeName)
-            lineList.append(itemName.ljust(itemNameLengthMax))
-
-            lineList.append(myCategory.getValueFormatted(attributeName, 0))
-            lineList.append("\n")
-
-        self.__write("".join(lineList))
-
-    def __writeTableFormat(self, myCategory):
-
+                line_list.append(self.__indent_space)
+            item_name = "_%s.%s" % (category.name, attribute_name)
+            line_list.append(item_name.ljust(item_name_max_length))
+            line_list.append(category.get_value_formatted(attribute_name, 0))
+            line_list.append("\n")
+        self.__write("".join(line_list))
+
+    def __write_table_format(self, category):
+        """Write table format data."""
         # Write the declaration of the loop_
-        #
-        lineList = []
-        lineList.append('#\n')
-        if self.__doDefinitionIndent:
-            lineList.append(self.__indentSpace)
-        lineList.append("loop_")
-        for attributeName in myCategory.getAttributeList():
-            lineList.append('\n')
-            if self.__doDefinitionIndent:
-                lineList.append(self.__indentSpace)
-            itemName = "_%s.%s" % (myCategory.getName(), attributeName)
-            lineList.append(itemName)
-        self.__write("".join(lineList))
-
-        #
-        # Write the data in tabular format -
-        #
-        # print myCategory.getName()
-        # print myCategory.getAttributeList()
-
+        line_list = []
+        line_list.append('#\n')
+        if self._do_definition_indent:
+            line_list.append(self.__indent_space)
+        line_list.append("loop_")
+        for attribute_name in category.attribute_list:
+            line_list.append('\n')
+            if self._do_definition_indent:
+                line_list.append(self.__indent_space)
+            item_name = "_%s.%s" % (category.name, attribute_name)
+            line_list.append(item_name)
+        self.__write("".join(line_list))
+        # Write the data in tabular format
         # For speed make the following evaluation on a portion of the table
-        if self.__rowPartition is not None:
-            numSteps = max(1, myCategory.getRowCount() / self.__rowPartition)
+        if self.__row_partition is not None:
+            num_steps = max(1, category.row_count / self.__row_partition)
         else:
-            numSteps = 1
-
-        formatTypeList, dataTypeList = myCategory.getFormatTypeList(steps=numSteps)
-        maxLengthList = myCategory.getAttributeValueMaxLengthList(steps=numSteps)
-        spacing = " " * self.__SPACING
-        #
-
-        # print formatTypeList
-        # print dataTypeList
-        # print maxLengthList
-        #
-        for iRow in range(myCategory.getRowCount()):
-            lineList = []
-            lineList.append('\n')
-            if self.__doDefinitionIndent:
-                lineList.append(self.__indentSpace + " ")
-
-            for iAt in range(myCategory.getAttributeCount()):
-                formatType = formatTypeList[iAt]
-                maxLength = maxLengthList[iAt]
-
-                if (formatType == 'FT_UNQUOTED_STRING' or formatType == 'FT_NULL_VALUE'):
-                    val = myCategory.getValueFormattedByIndex(iAt, iRow)
-                    lineList.append(val.ljust(maxLength))
-
-                elif formatType == 'FT_NUMBER':
-                    val = myCategory.getValueFormattedByIndex(iAt, iRow)
-                    lineList.append(val.rjust(maxLength))
-
-                elif formatType == 'FT_QUOTED_STRING':
-                    val = myCategory.getValueFormattedByIndex(iAt, iRow)
-
-                    lineList.append(val.ljust(maxLength + 2))
-
-                elif formatType == "FT_MULTI_LINE_STRING":
-                    val = myCategory.getValueFormattedByIndex(iAt, iRow)
-                    lineList.append(val)
-
-                lineList.append(spacing)
-
-            self.__write("".join(lineList))
+            num_steps = 1
+        format_type_list, _ = category.get_format_type_list(steps=num_steps)
+        max_length_list = category.get_max_attribute_list_length(steps=num_steps)
+        spacing = " " * self.__spacing
+        for irow in range(category.row_count):
+            line_list = []
+            line_list.append('\n')
+            if self._do_definition_indent:
+                line_list.append(self.__indent_space + " ")
+            for iattr in range(category.attribute_count):
+                format_type = format_type_list[iattr]
+                max_length = max_length_list[iattr]
+                if format_type in ('FT_UNQUOTED_STRING', 'FT_NULL_VALUE'):
+                    val = category.get_value_formatted_by_index(iattr, irow)
+                    line_list.append(val.ljust(max_length))
+                elif format_type == 'FT_NUMBER':
+                    val = category.get_value_formatted_by_index(iattr, irow)
+                    line_list.append(val.rjust(max_length))
+                elif format_type == 'FT_QUOTED_STRING':
+                    val = category.get_value_formatted_by_index(iattr, irow)
+                    line_list.append(val.ljust(max_length + 2))
+                elif format_type == "FT_MULTI_LINE_STRING":
+                    val = category.get_value_formatted_by_index(iattr, irow)
+                    line_list.append(val)
+                line_list.append(spacing)
+            self.__write("".join(line_list))
         self.__write("\n")
diff --git a/setup.py b/setup.py
index b89552e..bfc13c6 100644
--- a/setup.py
+++ b/setup.py
@@ -8,7 +8,7 @@ with open("README.md", "r") as fh:
 
 setuptools.setup(
     name="mmcif_pdbx",
-    version="0.0.1",
+    version="1.0.0",
     author="John Westbrook",
     author_email="jwest@rcsb.rutgers.edu",
     maintainer="Nathan Baker",


Test patch:
diff --git a/tests/reader_test.py b/tests/reader_test.py
index fa0ca50..303d229 100644
--- a/tests/reader_test.py
+++ b/tests/reader_test.py
@@ -5,122 +5,38 @@
 # Version: 0.001
 #
 # Update:
-#  27-Sep-2012  jdw add test case for reading PDBx structure factor file 
+#  27-Sep-2012  jdw add test case for reading PDBx structure factor file
 #
 ##
-"""
-Test cases for reading PDBx/mmCIF data files reader class -
-
-"""
-import sys, unittest, traceback
-import sys, time, os, os.path, shutil
-
+"""Test cases for reading PDBx/mmCIF data files reader class."""
+import logging
+from pathlib import Path
+import pytest
 from pdbx.reader.reader import PdbxReader
-from pdbx.reader.containers import *
-
-class readerTests(unittest.TestCase):
-    def setUp(self):
-        self.lfh=sys.stderr
-        self.verbose=False
-        self.pathPdbxDataFile     ="tests/data/1kip.cif"
-        self.pathBigPdbxDataFile  ="tests/data/1ffk.cif"
-        self.pathSFDataFile       ="tests/data/1kip-sf.cif"
-
-    def tearDown(self):
-        pass
 
-    def testReadSmallDataFile(self): 
-        """Test case -  read data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            myDataList=[]
-            ifh = open(self.pathPdbxDataFile, "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myDataList)
-            ifh.close()            
-        except:
-            traceback.print_exc(file=sys.stderr)
-            self.fail()
 
-    def testReadBigDataFile(self): 
-        """Test case -  read data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            myDataList=[]
-            ifh = open(self.pathBigPdbxDataFile, "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myDataList)
-            ifh.close()            
-        except:
-            traceback.print_exc(file=sys.stderr)
-            self.fail()
+DATA_DIR = Path("tests/data")
+LOGGER = logging.getLogger()
 
-    def testReadSFDataFile(self): 
-        """Test case -  read PDB structure factor data  file and compute statistics on f/sig(f).
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            myContainerList=[]
-            ifh = open(self.pathSFDataFile, "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myContainerList)
-            c0=myContainerList[0]
-            #
-            catObj=c0.getObj("refln")
-            if catObj is None:
-                return false
-        
-            nRows=catObj.getRowCount()
-            #
-            # Get column name index.
-            #
-            itDict={}
-            itNameList=catObj.getItemNameList()
-            for idxIt,itName in enumerate(itNameList):
-                itDict[str(itName).lower()]=idxIt
-                #
-            idf=itDict['_refln.f_meas_au']
-            idsigf=itDict['_refln.f_meas_sigma_au']
-            minR=100
-            maxR=-1
-            sumR=0
-            icount=0
-            for row in  catObj.getRowList():
-                try:
-                    f=float(row[idf])
-                    sigf=float(row[idsigf])
-                    ratio=sigf/f
-                    #self.lfh.write(" %f %f %f\n" % (f,sigf,ratio))
-                    maxR=max(maxR,ratio)
-                    minR=min(minR,ratio)
-                    sumR+=ratio
-                    icount+=1
-                except:
-                    continue
-            
-            ifh.close()
-            self.lfh.write("f/sig(f) min %f max %f avg %f count %d\n" % (minR, maxR, sumR/icount,icount))
-        except:
-            traceback.print_exc(file=sys.stderr)
-            self.fail()
 
+@pytest.mark.parametrize("input_cif", ["1kip.cif", "1ffk.cif"], ids=str)
+def test_data_file(input_cif):
+    """Test data file input."""
+    input_path = DATA_DIR / Path(input_cif)
+    with open(input_path, "rt") as input_file:
+        reader = PdbxReader(input_file)
+        data_list = []
+        reader.read(data_list)
 
-def simpleSuite():
-    suiteSelect = unittest.TestSuite()
-    suiteSelect.addTest(readerTests("testReadBigDataFile"))
-    suiteSelect.addTest(readerTests("testReadSmallDataFile"))    
-    suiteSelect.addTest(readerTests("testReadSFDataFile"))
-    return suiteSelect
 
-if __name__ == '__main__':
-    mySuite=simpleSuite()    
-    unittest.TextTestRunner(verbosity=2).run(mySuite)
-    #
+@pytest.mark.parametrize("input_cif", ["1kip-sf.cif"], ids=str)
+def test_structure_factor_file(input_cif):
+    """Test structure factor input."""
+    input_path = DATA_DIR / Path(input_cif)
+    with open(input_path, "rt") as input_file:
+        reader = PdbxReader(input_file)
+        container_list = []
+        reader.read(container_list)
+    container = container_list[0]
+    refln_object = container.get_object("refln")
+    assert refln_object is not None
diff --git a/tests/readwrite_test.py b/tests/readwrite_test.py
index 3443242..bfc02ab 100644
--- a/tests/readwrite_test.py
+++ b/tests/readwrite_test.py
@@ -8,229 +8,103 @@
 #         24-Oct-2012 jdw update path details and reorganize.
 #
 ##
-"""  Various tests caess for PDBx/mmCIF data file and dictionary reader and writer. 
-"""
-
-__docformat__ = "restructuredtext en"
-__author__    = "John Westbrook"
-__email__     = "jwest@rcsb.rutgers.edu"
-__license__   = "Creative Commons Attribution 3.0 Unported"
-__version__   = "V0.01"
-
-import sys, unittest, traceback
-import sys, time, os, os.path, shutil
-
+"""Test reading, writing, and updating files."""
+import logging
+from pathlib import Path
 from pdbx.reader.reader import PdbxReader
 from pdbx.writer.writer import PdbxWriter
-from pdbx.reader.containers import *
-
-
-class PdbxReadWriteTests(unittest.TestCase):
-    def setUp(self):
-        self.lfh=sys.stdout
-        self.verbose=False
-        self.pathPdbxDataFile     = "tests/data/1kip.cif"
-        self.pathOutputFile       = "testOutputDataFile.cif"
-
-    def tearDown(self):
-        pass
-
-
-    def testSimpleInitialization(self):
-        """Test case -  Simple initialization of a data category and data block
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            fn="test-simple.cif"
-            attributeNameList=['aOne','aTwo','aThree','aFour','aFive','aSix','aSeven','aEight','aNine','aTen']
-            rowList=[[1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10],
-                     [1,2,3,4,5,6,7,8,9,10] 
-                     ]
-            nameCat='myCategory'
-            #
-            #
-            curContainer=DataContainer("myblock")
-            aCat=DataCategory(nameCat,attributeNameList,rowList)
-            aCat.printIt()
-            curContainer.append(aCat)
-            curContainer.printIt()
-            #
-            myContainerList=[]
-            myContainerList.append(curContainer)
-            ofh = open(fn, "w")        
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myContainerList)
-            ofh.close()
-
-            myContainerList=[]            
-            ifh = open(fn, "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myContainerList)
-            ifh.close()
-            for container in myContainerList:
-                for objName in container.getObjNameList():
-                    name,aList,rList=container.getObj(objName).get()
-                    self.lfh.write("Recovered data category  %s\n" % name)
-                    self.lfh.write("Attribute list           %r\n" % repr(aList))
-                    self.lfh.write("Row list                 %r\n" % repr(rList))                                        
-        except:
-            traceback.print_exc(file=self.lfh)
-            self.fail()
-            
-        
-    def testWriteDataFile(self): 
-        """Test case -  write data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            myDataList=[]
-            ofh = open("test-output.cif", "w")
-            curContainer=DataContainer("myblock")
-            aCat=DataCategory("pdbx_seqtool_mapping_ref")
-            aCat.appendAttribute("ordinal")
-            aCat.appendAttribute("entity_id")
-            aCat.appendAttribute("auth_mon_id")
-            aCat.appendAttribute("auth_mon_num")
-            aCat.appendAttribute("pdb_chain_id")
-            aCat.appendAttribute("ref_mon_id")
-            aCat.appendAttribute("ref_mon_num")                        
-            aCat.append([1,2,3,4,5,6,7])
-            aCat.append([1,2,3,4,5,6,7])
-            aCat.append([1,2,3,4,5,6,7])
-            aCat.append([1,2,3,4,5,6,7])
-            aCat.append([7,6,5,4,3,2,1])
-            aCat.printIt()            
-            curContainer.append(aCat)
-            curContainer.printIt()
-            #
-            myDataList.append(curContainer)
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myDataList)
-            ofh.close()
-        except:
-            traceback.print_exc(file=self.lfh)
-            self.fail()
-
-    def testUpdateDataFile(self): 
-        """Test case -  update data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            # Create a initial data file --
-            #
-            myDataList=[]
-
-            curContainer=DataContainer("myblock")
-            aCat=DataCategory("pdbx_seqtool_mapping_ref")
-            aCat.appendAttribute("ordinal")
-            aCat.appendAttribute("entity_id")
-            aCat.appendAttribute("auth_mon_id")
-            aCat.appendAttribute("auth_mon_num")
-            aCat.appendAttribute("pdb_chain_id")
-            aCat.appendAttribute("ref_mon_id")
-            aCat.appendAttribute("ref_mon_num")                        
-            aCat.append([9,2,3,4,5,6,7])
-            aCat.append([10,2,3,4,5,6,7])
-            aCat.append([11,2,3,4,5,6,7])
-            aCat.append([12,2,3,4,5,6,7])
-            
-            #self.lfh.write("Assigned data category state-----------------\n")            
-            #aCat.dumpIt(fh=self.lfh)
-
-            curContainer.append(aCat)
-            myDataList.append(curContainer)
-            ofh = open("test-output-1.cif", "w")            
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myDataList)
-            ofh.close()
-            #
-            #
-            # Read and update the data -
-            # 
-            myDataList=[]
-            ifh = open("test-output-1.cif", "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myDataList)
-            ifh.close()
-            #
-            myBlock=myDataList[0]
-            myBlock.printIt()
-            myCat=myBlock.getObj('pdbx_seqtool_mapping_ref')
-            myCat.printIt()
-            for iRow in range(0,myCat.getRowCount()):
-                myCat.setValue('some value', 'ref_mon_id',iRow)
-                myCat.setValue(100, 'ref_mon_num',iRow)
-            ofh = open("test-output-2.cif", "w")            
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myDataList)
-            ofh.close()
-            #
-            
-        except:
-            traceback.print_exc(file=self.lfh)
-            self.fail()
-
-    def testReadDataFile(self): 
-        """Test case -  read data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            myDataList=[]
-            ifh = open(self.pathPdbxDataFile, "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myDataList)
-            ifh.close()            
-        except:
-            traceback.print_exc(file=self.lfh)
-            self.fail()
-
-    def testReadWriteDataFile(self): 
-        """Test case -  data file read write test
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            myDataList=[]            
-            ifh = open(self.pathPdbxDataFile, "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myDataList)
-            ifh.close()            
-            
-            ofh = open(self.pathOutputFile, "w")
-            pWr=PdbxWriter(ofh)
-            pWr.write(myDataList)        
-            ofh.close()
-        except:
-            traceback.print_exc(file=self.lfh)
-            self.fail()
-
-
-def simpleSuite():
-    suiteSelect = unittest.TestSuite()
-    suiteSelect.addTest(PdbxReadWriteTests("testSimpleInitialization"))
-    suiteSelect.addTest(PdbxReadWriteTests("testUpdateDataFile"))        
-    suiteSelect.addTest(PdbxReadWriteTests("testReadWriteDataFile"))    
-    return suiteSelect
-
-
-if __name__ == '__main__':
-    #
-    mySuite=simpleSuite()      
-    unittest.TextTestRunner(verbosity=2).run(mySuite)
-    #
-
+from pdbx.reader.containers import DataCategory, DataContainer
+
+
+_LOGGER = logging.getLogger()
+DATA_DIR = Path("tests/data")
+
+
+def test_init_write_read(tmp_path):
+    """Test initialization, writing, and reading."""
+    attribute_name_list = [
+        'aOne', 'aTwo', 'aThree', 'aFour', 'aFive', 'aSix', 'aSeven',
+        'aEight', 'aNine', 'aTen']
+    row_list = [
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+        [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]
+    category_name = 'category'
+    container = DataContainer("myblock")
+    category = DataCategory(category_name, attribute_name_list, row_list)
+    category.print_it()
+    container.append(category)
+    container.print_it()
+    container_list = [container]
+    cif_path = Path(tmp_path) / Path("test-simple.cif")
+    with open(cif_path, "wt") as output_file:
+        writer = PdbxWriter(output_file)
+        writer.write(container_list)
+    container_list = []
+    with open(cif_path, "rt") as input_file:
+        reader = PdbxReader(input_file)
+        reader.read(container_list)
+    for container in container_list:
+        for object_name in container.get_object_name_list():
+            name, attr_list, row_list = container.get_object(
+                object_name).get()
+            _LOGGER.info("Recovered data category  %s\n", name)
+            _LOGGER.info("Attribute list           %r\n", repr(attr_list))
+            _LOGGER.info("Row list                 %r\n", repr(row_list))
+
+
+def test_update_data_file(tmp_path):
+    """Test updating of a data file."""
+    container = DataContainer("myblock")
+    category = DataCategory("pdbx_seqtool_mapping_ref")
+    category.append_attribute("ordinal")
+    category.append_attribute("entity_id")
+    category.append_attribute("auth_mon_id")
+    category.append_attribute("auth_mon_num")
+    category.append_attribute("pdb_chain_id")
+    category.append_attribute("ref_mon_id")
+    category.append_attribute("ref_mon_num")
+    category.append([9, 2, 3, 4, 5, 6, 7])
+    category.append([10, 2, 3, 4, 5, 6, 7])
+    category.append([11, 2, 3, 4, 5, 6, 7])
+    category.append([12, 2, 3, 4, 5, 6, 7])
+    container.append(category)
+    data_list = [container]
+    cif_path = Path(tmp_path) / Path("test-output-1.cif")
+    with open(cif_path, "wt") as output_file:
+        writer = PdbxWriter(output_file)
+        writer.write(data_list)
+    data_list = []
+    with open(cif_path, "rt") as input_file:
+        reader = PdbxReader(input_file)
+        reader.read(data_list)
+    block = data_list[0]
+    block.print_it()
+    category = block.get_object('pdbx_seqtool_mapping_ref')
+    category.print_it()
+    for irow in range(category.row_count):
+        category.set_value('some value', 'ref_mon_id', irow)
+        category.set_value(100, 'ref_mon_num', irow)
+    cif_path = Path(tmp_path) / Path("test-output-2.cif")
+    with open(cif_path, "wt") as output_file:
+        writer = PdbxWriter(output_file)
+        writer.write(data_list)
+
+def test_read_write_data_file(tmp_path):
+    """Data file read/write test."""
+    input_path = Path(DATA_DIR) / Path("1kip.cif")
+    with open(input_path, "rt") as input_file:
+        reader = PdbxReader(input_file)
+        data_list = []
+        reader.read(data_list)
+    output_path = Path(tmp_path) / Path("testOutputDataFile.cif")
+    with open(output_path, "wt") as output_path:
+        writer = PdbxWriter(output_path)
+        writer.write(data_list)
diff --git a/tests/writer_test.py b/tests/writer_test.py
index 1919257..d9c0c8a 100644
--- a/tests/writer_test.py
+++ b/tests/writer_test.py
@@ -8,148 +8,34 @@
 #  5-Apr-2011 jdw   Using the double quote format preference
 # 24-Oct-2012 jdw   Update path and examples.
 ##
-"""
-Test implementing PDBx/mmCIF write and formatting operations.
-
-"""
-__docformat__ = "restructuredtext en"
-__author__    = "John Westbrook"
-__email__     = "jwest@rcsb.rutgers.edu"
-__license__   = "Creative Commons Attribution 3.0 Unported"
-__version__   = "V0.01"
-
-
-
-import sys, unittest, traceback
-import sys, time, os, os.path, shutil
-
-from pdbx.reader.reader  import PdbxReader
-from pdbx.writer.writer  import PdbxWriter
-from pdbx.reader.containers import *
-
-class writerTests(unittest.TestCase):
-    def setUp(self):
-        self.lfh=sys.stderr
-        self.verbose=False
-        self.pathPdbxDataFile     ="tests/data/1kip.cif"
-        self.pathOutputFile       ="testOutputDataFile.cif"
-
-    def tearDown(self):
-        pass
-
-    def testWriteDataFile(self): 
-        """Test case -  write data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            #
-            myDataList=[]
-            ofh = open("test-output.cif", "w")
-            curContainer=DataContainer("myblock")
-            aCat=DataCategory("pdbx_seqtool_mapping_ref")
-            aCat.appendAttribute("ordinal")
-            aCat.appendAttribute("entity_id")
-            aCat.appendAttribute("auth_mon_id")
-            aCat.appendAttribute("auth_mon_num")
-            aCat.appendAttribute("pdb_chain_id")
-            aCat.appendAttribute("ref_mon_id")
-            aCat.appendAttribute("ref_mon_num")                        
-            aCat.append((1,2,3,4,5,6,7))
-            aCat.append((1,2,3,4,5,6,7))
-            aCat.append((1,2,3,4,5,6,7))
-            aCat.append((1,2,3,4,5,6,7))
-            curContainer.append(aCat)
-            myDataList.append(curContainer)
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myDataList)
-            ofh.close()
-        except:
-            traceback.print_exc(file=sys.stderr)
-            self.fail()
-
-    def testUpdateDataFile(self): 
-        """Test case -  write data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        try:
-            # Create a initial data file --
-            #
-            myDataList=[]
-            ofh = open("test-output-1.cif", "w")
-            curContainer=DataContainer("myblock")
-            aCat=DataCategory("pdbx_seqtool_mapping_ref")
-            aCat.appendAttribute("ordinal")
-            aCat.appendAttribute("entity_id")
-            aCat.appendAttribute("auth_mon_id")
-            aCat.appendAttribute("auth_mon_num")
-            aCat.appendAttribute("pdb_chain_id")
-            aCat.appendAttribute("ref_mon_id")
-            aCat.appendAttribute("ref_mon_num")                        
-            aCat.append((1,2,3,4,5,6,7))
-            aCat.append((1,2,3,4,5,6,7))
-            aCat.append((1,2,3,4,5,6,7))
-            aCat.append((1,2,3,4,5,6,7))
-            curContainer.append(aCat)
-            myDataList.append(curContainer)
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myDataList)
-            ofh.close()
-            #
-            # Read and update the data -
-            # 
-            myDataList=[]
-            ifh = open("test-output-1.cif", "r")
-            pRd=PdbxReader(ifh)
-            pRd.read(myDataList)
-            ifh.close()
-            #
-            myBlock=myDataList[0]
-            myBlock.printIt()
-            myCat=myBlock.getObj('pdbx_seqtool_mapping_ref')
-            myCat.printIt()
-            for iRow in range(0,myCat.getRowCount()):
-                myCat.setValue('some value', 'ref_mon_id',iRow)
-                myCat.setValue(100, 'ref_mon_num',iRow)
-            ofh = open("test-output-2.cif", "w")            
-            pdbxW=PdbxWriter(ofh)
-            pdbxW.write(myDataList)
-            ofh.close()            
-            
-        except:
-            traceback.print_exc(file=sys.stderr)
-            self.fail()
-
-    def testReadDataFile(self): 
-        """Test case -  read data file 
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        myDataList=[]
-        ifh = open(self.pathPdbxDataFile, "r")
-        pRd=PdbxReader(ifh)
-        pRd.read(myDataList)
-        ifh.close()            
-
-    def testReadWriteDataFile(self): 
-        """Test case -  data file read write test
-        """
-        self.lfh.write("\nStarting %s %s\n" % (self.__class__.__name__,
-                                               sys._getframe().f_code.co_name))
-        myDataList=[]
-        ifh = open(self.pathPdbxDataFile, "r")            
-        pRd=PdbxReader(ifh)
-        pRd.read(myDataList)
-        ifh.close()            
-        
-        ofh = open(self.pathOutputFile, "w")
-        pWr=PdbxWriter(ofh)
-        pWr.write(myDataList)        
-        ofh.close()
-
-def suite():
-    return unittest.makeSuite(writerTests,'test')
-
-if __name__ == '__main__':
-    unittest.main()
+"""Test PDBx/mmCIF write and formatting operations."""
+import logging
+from pathlib import Path
+from pdbx.reader.containers import DataContainer, DataCategory
+from pdbx.writer.writer import PdbxWriter
+
+
+_LOGGER = logging.getLogger()
+
+
+def test_write_data_file(tmp_path):
+    """Test case -  write data file."""
+    output_path = Path(tmp_path) / Path("test-output.cif")
+    category = DataCategory("pdbx_seqtool_mapping_ref")
+    category.append_attribute("ordinal")
+    category.append_attribute("entity_id")
+    category.append_attribute("auth_mon_id")
+    category.append_attribute("auth_mon_num")
+    category.append_attribute("pdb_chain_id")
+    category.append_attribute("ref_mon_id")
+    category.append_attribute("ref_mon_num")
+    category.append((1, 2, 3, 4, 5, 6, 7))
+    category.append((1, 2, 3, 4, 5, 6, 7))
+    category.append((1, 2, 3, 4, 5, 6, 7))
+    category.append((1, 2, 3, 4, 5, 6, 7))
+    container = DataContainer("myblock")
+    container.append(category)
+    data_list = [container]
+    with open(output_path, "wt") as output_file:
+        writer = PdbxWriter(output_file)
+        writer.write(data_list)