Repository: RenneLab/hybkit (language: python)

Issue:
Update v3.4



Discussion / hints:

Fix patch:
diff --git a/.cspell.json b/.cspell.json
new file mode 100644
index 0000000..6c7eccf
--- /dev/null
+++ b/.cspell.json
@@ -0,0 +1,21 @@
+{
+   "version": "0.2",
+   "ignorePaths": [],
+   "dictionaryDefinitions": [],
+   "dictionaries": [],
+   "words": [
+      "badparam",
+      "equalto",
+      "hybfile",
+      "hybfolditer",
+      "hybformat",
+      "NOENERGY",
+      "NOFOLD"
+   ],
+   "ignoreWords": [
+      "notype",
+      "NOTYPE",
+      "Ctfile"
+   ],
+   "import": []
+}
diff --git a/.ruff.toml b/.ruff.toml
new file mode 100644
index 0000000..ff95e62
--- /dev/null
+++ b/.ruff.toml
@@ -0,0 +1,16 @@
+# Load the ruff formatter settings.
+extend = "/home/ds/scripts/ruff.toml"
+
+# select = ["ALL"]
+
+ignore = [
+    "PT012", # Limit Pytest Raises: One Simple Statement
+    "SIM117", # Disallow compound with statements
+    # "ANN202", # Require type annotations on private methods
+    "ANN101", # Missing type annotation for self in method
+    "ANN102", # Missing type annotation for cls in classmethod
+]
+
+select = [
+    "ANN", # Add type annotations
+]
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index e89ff2b..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-language: python
-python:
-  - "3.8"
-  - "3.9"
-  - "3.9-dev"  # 3.9 development branch
-  - "3.10"
-  - "3.10-dev"  # 3.9 development branch
-# install:
-  # command to run tests
-#os:
-#- linux
-#- osx
-
-#jdk:
-#- openjdk8
-
-#branches:
-#  only:
-#  - master
-
-#jobs:
-#  allow_failures:
-#  - python:
-#     "3.10-dev" # Fails due to lack of matplotlib availability
-
-install:
-  - pip install -r requirements.txt
-  - pip install coveralls
-  - pip install pyyaml
-  - python setup.py install
-  - export PATH=$PATH:$PWD/scripts
-
-script:
-  - cd auto_tests
-  - ./auto_test.sh
-  - cd ../
-  - coverage run --source=. --omit="conf*",setup.py -m pytest
-  - coveralls
diff --git a/README.rst b/README.rst
index c53bc46..48bc2fa 100644
--- a/README.rst
+++ b/README.rst
@@ -91,7 +91,9 @@ Hybkit API:
     Record attributes can be analyzed, set, and evaluated using included class methods.
 
     For example, a workflow to print the identifiers of only sequences within a ".hyb" file
-    that contain a miRNA can be performed as such::
+    that contain a miRNA can be performed as such:
+
+    .. code-block:: Python
 
         #!/usr/bin/env python3
         import hybkit
@@ -145,6 +147,7 @@ Installation:
         * Python3.8+
         * `matplotlib <https://matplotlib.org/>`_ >= 3.7.1 (|Hunter2007|_)
         * `BioPython <https://biopython.org/>`_ >= 1.79 (|Cock2009|_)
+        * `typing_extensions <https://pypi.org/project/typing-extensions/>` >= 4.8.0
 
     Via PyPI / Python PIP:
         |PipVersion|
@@ -212,12 +215,30 @@ Installation:
 
     Further documentation on hybkit usage can be found in |docs_link|_.
 
-    Copyright:
-        | hybkit is a free, sharable, open-source project.
-        | All source code and executable scripts contained within this package are considered
-          part of the "hybkit" project and are distributed without any warranty or implied warranty
-          under the GNU General Public License v3.0 or any later version, described in the "LICENSE"
-          file.
+Setup Testing:
+    Hybkit provides a suite of unit tests to maintain stability of the API and script
+    functionalities. To run the API test suite, install pytest and run the tests from the
+    root directory of the hybkit package:
+
+    .. code-block:: bash
+
+        $ pip install pytest
+        $ pytest
+
+    Command-line scripts can be tested by running the auto_test.sh script in
+    the auto_tests directory:
+
+    .. code-block:: bash
+
+        $ ./auto_tests/auto_test.sh
+
+
+Copyright:
+    | hybkit is a free, sharable, open-source project.
+    | All source code and executable scripts contained within this package are considered
+        part of the "hybkit" project and are distributed without any warranty or implied warranty
+        under the GNU General Public License v3.0 or any later version, described in the "LICENSE"
+        file.
 
 .. |Helwak2013| replace:: *Helwak et al. (Cell 2013)*
 .. _Helwak2013: https://doi.org/10.1016/j.cell.2013.03.043
@@ -251,4 +272,5 @@ Installation:
 .. |hybkit API| replace:: *hybkit API*
 .. |docs_link| replace:: hybkit's ReadTheDocs
 .. _docs_link: https://hybkit.readthedocs.io#
-.. |example_01_image| image:: example_01_type_mirna_analysis/example_output/combined_analysis_types_mirna_hybrids.png
+.. |example_01_image| image:: https://raw.githubusercontent.com/RenneLab/hybkit/master/example_01_type_mirna_analysis/example_output/combined_analysis_types_mirna_hybrids.png
+
diff --git a/docs/index.rst b/docs/index.rst
index abe67a4..7d16755 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -91,7 +91,9 @@ Hybkit API:
     Record attributes can be analyzed, set, and evaluated using included class methods.
 
     For example, a workflow to print the identifiers of only sequences within a ".hyb" file
-    that contain a miRNA can be performed as such::
+    that contain a miRNA can be performed as such:
+
+    .. code-block:: Python
 
         #!/usr/bin/env python3
         import hybkit
@@ -145,6 +147,7 @@ Installation:
         * Python3.8+
         * `matplotlib <https://matplotlib.org/>`_ >= 3.7.1 (|Hunter2007|_)
         * `BioPython <https://biopython.org/>`_ >= 1.79 (|Cock2009|_)
+        * `typing_extensions <https://pypi.org/project/typing-extensions/>` >= 4.8.0
 
     Via PyPI / Python PIP:
         |PipVersion|
@@ -212,12 +215,30 @@ Installation:
 
     Further documentation on hybkit usage can be found in |docs_link|_.
 
-    Copyright:
-        | hybkit is a free, sharable, open-source project.
-        | All source code and executable scripts contained within this package are considered
-          part of the "hybkit" project and are distributed without any warranty or implied warranty
-          under the GNU General Public License v3.0 or any later version, described in the "LICENSE"
-          file.
+Setup Testing:
+    Hybkit provides a suite of unit tests to maintain stability of the API and script
+    functionalities. To run the API test suite, install pytest and run the tests from the
+    root directory of the hybkit package:
+
+    .. code-block:: bash
+
+        $ pip install pytest
+        $ pytest
+
+    Command-line scripts can be tested by running the auto_test.sh script in
+    the auto_tests directory:
+
+    .. code-block:: bash
+
+        $ ./auto_tests/auto_test.sh
+
+
+Copyright:
+    | hybkit is a free, sharable, open-source project.
+    | All source code and executable scripts contained within this package are considered
+        part of the "hybkit" project and are distributed without any warranty or implied warranty
+        under the GNU General Public License v3.0 or any later version, described in the "LICENSE"
+        file.
 
 .. |Helwak2013| replace:: *Helwak et al. (Cell 2013)*
 .. _Helwak2013: https://doi.org/10.1016/j.cell.2013.03.043
diff --git a/docs/index_suffix.rst b/docs/index_suffix.rst
index f64612d..8eb5310 100644
--- a/docs/index_suffix.rst
+++ b/docs/index_suffix.rst
@@ -4,7 +4,7 @@
 .. |hybkit API| replace:: :ref:`hybkit API`
 .. |docs_link| replace:: this documentation
 .. _docs_link: #
-.. |example_01_image| image:: ../example_01_type_mirna_analysis/example_output/combined_analysis_types_mirna_hybrids.png
+.. |example_01_image| image:: https://raw.githubusercontent.com/RenneLab/hybkit/master/example_01_type_mirna_analysis/example_output/combined_analysis_types_mirna_hybrids.png
 
 .. toctree::
    :maxdepth: 2
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 73049c1..deba6a5 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -4,4 +4,4 @@ importlib_resources
 sphinx-argparse
 sphinx>=4.3.0
 sphinx_rtd_theme>=0.5.1
-
+typing_extensions>=4.8.0
diff --git a/docs/source/about.rst b/docs/source/about.rst
index 5a826b7..be9606f 100644
--- a/docs/source/about.rst
+++ b/docs/source/about.rst
@@ -23,6 +23,21 @@ Changelog
 ---------
 
 
+    * 0.3.4 (ongoing) Changes include:
+
+      * Misc Bugfixes and Refinements
+      * Switch code linting to Ruff
+      * Add hybkit.errors module and HybkitError classes
+      * Moved printing of warnings to python logging module
+      * Add option for direct passage of file-like object for construction of
+        HybFile and ViennaFile
+      * Add HybRecord.to_csv_header(), HybRecord.to_fields(),
+        and HybRecord.to_fields_header() methods
+      * Refine description of HybFile.open() constructor method
+      * Add typing_extensions dependency
+      * Add Python3.8-compatible type hints to API
+      * Documentation Updates
+
     * 0.3.3 (2023-09) Changes include:
 
       * Misc Bugfixes and Refinements
diff --git a/docs/source/hybkit.errors.rst b/docs/source/hybkit.errors.rst
new file mode 100644
index 0000000..fe02e73
--- /dev/null
+++ b/docs/source/hybkit.errors.rst
@@ -0,0 +1,6 @@
+hybkit.errors
+======================
+
+.. automodule:: hybkit.errors
+   :members:
+
diff --git a/docs/source/hybkit_api.rst b/docs/source/hybkit_api.rst
index eee25d8..e5f0374 100644
--- a/docs/source/hybkit_api.rst
+++ b/docs/source/hybkit_api.rst
@@ -16,6 +16,7 @@ hybkit API
     :mod:`~hybkit.analysis`       Classes for predefined analyses of hyb records
     :mod:`~hybkit.plot`           Plotting methods for analysis results
     :mod:`~hybkit.util`           Support methods for executable scripts
+    :mod:`~hybkit.errors`         Error classes for the hybkit package
     ============================= =====================================================
 
 .. toctree::
@@ -27,3 +28,4 @@ hybkit API
    hybkit.plot
    hybkit.settings
    hybkit.util
+   hybkit.errors
diff --git a/example_01_type_mirna_analysis/README.rst b/example_01_type_mirna_analysis/README.rst
index a8f3a55..dc6efa2 100644
--- a/example_01_type_mirna_analysis/README.rst
+++ b/example_01_type_mirna_analysis/README.rst
@@ -9,6 +9,12 @@ Example Type-miRNA Analysis
 This directory contains a example analysis of Hyb-format data, published in the quick Crosslinking and Sequencing of Hybrids (qCLASH) experiment described in:
 Gay, Lauren A., et al. "Modified cross-linking, ligation, and sequencing of hybrids (qCLASH) identifies Kaposi's Sarcoma-associated herpesvirus microRNA targets in endothelial cells." Journal of virology 92.8 (2018): e02138-17.
 
+The analysis is carried out in multiple example implementations which produce identical output:
+    * via the `Command-Line
+      <https://github.com/RenneLab/hybkit/blob/master/example_01_type_mirna_analysis/analysis_shell.sh/>`_
+    * via the `Python3 API
+      <https://github.com/RenneLab/hybkit/blob/master/example_01_type_mirna_analysis/analysis_python.py/>`_
+
 This analysis first performs quality control on the data. It then summarizes and analyzes the hybrid, segment, and miRNA characteristics of each input file.
 Analyses for each individual file, and a combined summary analysis are all produced.
 
diff --git a/example_01_type_mirna_analysis/analysis_python.py b/example_01_type_mirna_analysis/analysis_python.py
index ed79ade..6563874 100755
--- a/example_01_type_mirna_analysis/analysis_python.py
+++ b/example_01_type_mirna_analysis/analysis_python.py
@@ -12,6 +12,7 @@
 
 import os
 import sys
+
 import hybkit
 
 # Set mirna types as custom to include KSHV-miRNAs
@@ -82,7 +83,7 @@
                     break
 
             # If record has an excluded type, continue to next record without analyzing.
-            if not use_record:
+            if not use_record:  # noqa: SIM114
                 continue
 
             # If record is a duplicate, skip it.
diff --git a/example_02_target_analysis/README.rst b/example_02_target_analysis/README.rst
index ce0ba71..252804b 100644
--- a/example_02_target_analysis/README.rst
+++ b/example_02_target_analysis/README.rst
@@ -13,6 +13,12 @@ Gay, Lauren A., et al. "Modified cross-linking, ligation, and sequencing of hybr
 herpesvirus microRNA targets in endothelial cells."
 Journal of virology 92.8 (2018): e02138-17.
 
+The analysis is carried out in multiple example implementations which produce identical output:
+    * via the `Command-Line
+      <https://github.com/RenneLab/hybkit/blob/master/example_02_target_analysis/analysis_shell.sh/>`_
+    * via the `Python3 API
+      <https://github.com/RenneLab/hybkit/blob/master/example_02_target_analysis/analysis_python.py/>`_
+
 This analysis specifically filters and analyzes the kshv-miR-K12-5 miRNA arising from
 Kaposi's Sarcoma-Associated Herpesvirus (KSHV), which has the assigned type "KSHV-miRNA".
 Both individual and summary output files are produced.
diff --git a/example_02_target_analysis/analysis_python.py b/example_02_target_analysis/analysis_python.py
index 22476fe..8abf682 100755
--- a/example_02_target_analysis/analysis_python.py
+++ b/example_02_target_analysis/analysis_python.py
@@ -13,13 +13,13 @@
 """
 
 import os
-import sys
+
 import hybkit
 
 # Set mirna types as custom to include KSHV-miRNAs
-hybkit.util.set_setting('mirna_types', ['miRNA', 'KSHV-miRNA'])
+hybkit.util.set_setting('mirna_types', set_value=['miRNA', 'KSHV-miRNA'])
 # Tell hybkit that identifiers are in Hyb-Program standard format.
-hybkit.util.set_setting('hybformat_id', True)
+hybkit.util.set_setting('hybformat_id', set_value=True)
 
 # Set script directories and input file names.
 analysis_dir = os.path.abspath(os.path.dirname(__file__))
diff --git a/example_03_grouped_target_analysis/README.rst b/example_03_grouped_target_analysis/README.rst
index 4f7eae6..02b7d6f 100644
--- a/example_03_grouped_target_analysis/README.rst
+++ b/example_03_grouped_target_analysis/README.rst
@@ -14,6 +14,12 @@ Gay, Lauren A., et al. "Modified cross-linking, ligation, and sequencing of hybr
 herpesvirus microRNA targets in endothelial cells."
 Journal of virology 92.8 (2018): e02138-17.
 
+The analysis is carried out in multiple example implementations which produce identical output:
+    * via the `Command-Line
+      <https://github.com/RenneLab/hybkit/blob/master/example_03_grouped_target_analysis/analysis_shell.sh/>`_
+    * via the `Python3 API
+      <https://github.com/RenneLab/hybkit/blob/master/example_03_grouped_target_analysis/analysis_python.py/>`_
+
 This analysis specifically investigates and characterizes miRNA arising from
 six experimental replicates from two conditions with cells infected with
 Kaposi's Sarcoma Herpesvirus, which are given the type name "KSHV_miRNA".
diff --git a/example_03_grouped_target_analysis/analysis_python.py b/example_03_grouped_target_analysis/analysis_python.py
index 8d295d1..815e048 100755
--- a/example_03_grouped_target_analysis/analysis_python.py
+++ b/example_03_grouped_target_analysis/analysis_python.py
@@ -12,13 +12,13 @@
 """
 
 import os
-import sys
+
 import hybkit
 
 # Set mirna types as custom to include KSHV-miRNAs
-hybkit.util.set_setting('mirna_types', ['miRNA', 'KSHV-miRNA'])
+hybkit.util.set_setting('mirna_types', set_value=['miRNA', 'KSHV-miRNA'])
 # Tell hybkit that identifiers are in Hyb-Program standard format.
-hybkit.util.set_setting('hybformat_id', True)
+hybkit.util.set_setting('hybformat_id', set_value=True)
 
 # Set script directories and input file names.
 analysis_dir = os.path.abspath(os.path.dirname(__file__))
@@ -86,7 +86,7 @@
                 hyb_record.eval_mirna()
 
                 # If assigned 'miRNA' does not contain string 'kshv', skip.
-                if (not hyb_record.has_prop('has_mirna')
+                if (not hyb_record.has_prop('has_mirna')  # noqa: SIM114
                         or not hyb_record.has_prop('mirna_contains', 'kshv')):
 
                     continue
diff --git a/example_04_fold_analysis/README.rst b/example_04_fold_analysis/README.rst
index 127a94e..57d783b 100644
--- a/example_04_fold_analysis/README.rst
+++ b/example_04_fold_analysis/README.rst
@@ -13,6 +13,12 @@ Gay, Lauren A., et al. "Modified cross-linking, ligation, and sequencing of hybr
 herpesvirus microRNA targets in endothelial cells."
 Journal of virology 92.8 (2018): e02138-17.
 
+The analysis is carried out in multiple example implementations which produce identical output:
+    * via the `Command-Line
+      <https://github.com/RenneLab/hybkit/blob/master/example_04_fold_analysis/analysis_shell.sh/>`_
+    * via the `Python3 API
+      <https://github.com/RenneLab/hybkit/blob/master/example_04_fold_analysis/analysis_python.py/>`_
+
 This analysis investigates the predicted folding of miRNA
 from an experimental replicate infected with
 Kaposi's Sarcoma-Associated Herpesvirus (KSHV), which are given the type name "KSHV-miRNA".
diff --git a/example_04_fold_analysis/analysis_python.py b/example_04_fold_analysis/analysis_python.py
index 74c39c6..44b5360 100755
--- a/example_04_fold_analysis/analysis_python.py
+++ b/example_04_fold_analysis/analysis_python.py
@@ -12,13 +12,13 @@
 """
 
 import os
-import sys
+
 import hybkit
 
 # Set mirna types as custom to include KSHV-miRNAs
-hybkit.util.set_setting('mirna_types', ['miRNA', 'KSHV-miRNA'])
+hybkit.util.set_setting('mirna_types', set_value=['miRNA', 'KSHV-miRNA'])
 # Tell hybkit that identifiers are in Hyb-Program standard format.
-hybkit.util.set_setting('hybformat_id', True)
+hybkit.util.set_setting('hybformat_id', set_value=True)
 # Set FoldRecord seq_type as "dynamic" to work with Hyb-format *_hybrid_ua.vienna files
 #   which have a modified sequence.
 hybkit.util.set_setting('seq_type', 'dynamic')
@@ -40,7 +40,7 @@
     os.mkdir(out_dir)
 
 print('Using Input Files:')
-print('    ' + '\n    '.join([input_hyb_name, input_vienna_name]) + '\n')
+print(f'    {input_hyb_name}\n    {input_vienna_name}\n')
 
 # Set hybrid segment types to remove as part of quality control (QC)
 remove_types = ['rRNA', 'mitoch-rRNA']
@@ -60,7 +60,7 @@
     # hybkit.HybFile.open(out_hyb_name, 'w') as out_hyb:
 
     hyb_fold_iter = hybkit.HybFoldIter(input_hyb, input_vienna, combine=True)
-    for i, hyb_record in enumerate(hyb_fold_iter):
+    for _, hyb_record in enumerate(hyb_fold_iter):
         # Find Segment types and miRNA information
         hyb_record.eval_types()
         hyb_record.eval_mirna()
diff --git a/hybkit/__about__.py b/hybkit/__about__.py
index ca8e73a..9079ea2 100644
--- a/hybkit/__about__.py
+++ b/hybkit/__about__.py
@@ -9,13 +9,19 @@
 #   Change Version
 #   Change Date
 
+
+import logging
 import os
 import sys
 from importlib import resources
 
 # Check Python version and throw error if not 3.8+
-if not (sys.version_info.major >= 3 and sys.version_info.minor >= 8):
-    raise RuntimeError('Python 3.8+ is required for hybkit.')
+if sys.version_info < (3, 8):  #noqa: UP036
+    message = 'Python 3.8+ is required for hybkit.'
+    message += ' Current version is ' + str(sys.version_info.major) + '.'
+    message += str(sys.version_info.minor) + '.' + str(sys.version_info.micro)
+    message += '.' + str(sys.version_info.releaselevel)
+    raise RuntimeError(message)
 
 # Set hybkit module directory
 with resources.path('hybkit', '__init__.py') as path_obj:
@@ -23,7 +29,7 @@
 
 # Hybkit information
 project_name = 'hybkit'
-version = "v0.3.3"
+version = 'v0.3.3'
 python_requires = '>=3.8'
 description = 'Toolkit for analysis of chimeric (hybrid) RNA sequence data.'
 project_url = 'https://github.com/RenneLab/hybkit'
@@ -46,17 +52,9 @@
 elif os.path.isdir(os.path.join(local_prefix_data_dir, 'ref_data')):
     hybkit_data_dir = local_prefix_data_dir
 else:
-    print('WARNING: hybkit_data_dir variable cannot be set, ignore during setup.py.')
+    message = 'hybkit_data_dir variable cannot be set, ignore during setup.py.'
+    logging.warning(message)
     hybkit_data_dir = ''
-    # print('prefix_data_dir', prefix_data_dir)
-    # if os.path.isdir(prefix_data_dir):
-    #     print(os.listdir(prefix_data_dir))
-    # print('local_data_dir', local_data_dir)
-    # if os.path.isdir(local_data_dir):
-    #     print(os.listdir(local_data_dir))
-    # print('local_prefix_data_dir', local_prefix_data_dir)
-    # if os.path.isdir(local_prefix_data_dir):
-    #     print(os.listdir(local_prefix_data_dir))
 
 ref_data_dir = os.path.join(hybkit_data_dir, 'reference_data')
 docs_dir = os.path.join(hybkit_data_dir, 'docs')
@@ -90,16 +88,16 @@
 keywords = 'genetics, genomics, microRNAs, Ribonomics, Hyb, Hybrids, CLASH, qCLASH, '
 keywords += 'CLEAR-CLIP, Chimeric e-CLIP'
 
-__author__ = "Daniel Stribling"
-__contact__ = "ds@ufl.edu"
-__credits__ = ["Daniel Stribling", "Rolf Renne"]
+__author__ = 'Daniel Stribling'
+__contact__ = 'ds@ufl.edu'
+__credits__ = ['Daniel Stribling', 'Rolf Renne']
 __copyright__ = '2023, ' + __author__
-__date__ = "2023/09/05"
+__date__ = '2023/09/05'
 __deprecated__ = False
-__email__ = "ds@ufl.edu"
-__license__ = "GPLv3+"
-__maintainer__ = "Renne Group, University of Florida"
-__status__ = "Development"
+__email__ = 'ds@ufl.edu'
+__license__ = 'GPLv3+'
+__maintainer__ = 'Renne Group, University of Florida'
+__status__ = 'Development'
 __version__ = version
 # Hyb file specification version.
 spec_version = __version__
diff --git a/hybkit/__init__.py b/hybkit/__init__.py
index 6116d2b..b43d3ed 100644
--- a/hybkit/__init__.py
+++ b/hybkit/__init__.py
@@ -48,33 +48,86 @@
 
 """
 
+import copy
+import logging
 import os
 import sys
-import copy
 from collections import Counter
+from typing import Any, Dict, Iterable, List, Literal, Optional, Tuple, Type, Union
+
+from typing_extensions import Self
+
+Bio, Seq, SeqRecord = None, None, None
 try:
     import Bio
+
     # from Bio import SeqIO
     from Bio.Seq import Seq
     from Bio.SeqRecord import SeqRecord
-    bio_module_error = None
-except ModuleNotFoundError as bio_module_error:
-    Bio, Seq, SeqRecord = None, None, None
+except ModuleNotFoundError:
+    pass
 
+# ----- Linting Directives:
+# ruff: noqa: D214 E402 F401 SLF001 TRY301 B028
 
 # Perform *Initial* hybkit submodule imports, remainder at code end.
-sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
 import hybkit
+import hybkit.__about__
 import hybkit.settings
 import hybkit.type_finder
-import hybkit.__about__
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+from hybkit.errors import HybkitArgError, HybkitConstructorError, HybkitIterError, HybkitMiscError
+
+# ----- Begin Custom Types -----
+SegProps = Dict[str, Union[float, int, str]]
+StrOrNum = Union[float, int, str]
+FlagsDict = Dict[str, Any]
+Value = Union[float, int, str, bool, None]
+FlexFoldRecord = Union['FoldRecord', Tuple['FoldRecord', Any]]
+MirnaDetailsArg = Literal[
+    'all', 'mirna_ref', 'target_ref', 'mirna_seg_type',
+    'target_seg_type', 'mirna_seq', 'target_seq',
+    'mirna_fold', 'target_fold'
+    ]
+ToFastaRecordArg = Literal['hybrid', 'seg1', 'seg2', 'mirna', 'target']
+FoldSeqArg = Literal['static', 'dynamic']
+ErrorModeArg = Literal['raise', 'warn_return', 'return']
+IterErrorModeArg = Literal['raise', 'warn_return', 'warn_skip', 'skip', 'return']
+
+FieldsHeaderReturn = Literal[
+    'id', 'seq', 'energy', 'seg1_ref_name', 'seg1_read_start', 'seg1_read_end',
+    'seg1_ref_start', 'seg1_ref_end', 'seg1_score', 'seg2_ref_name',
+    'seg2_read_start', 'seg2_read_end', 'seg2_ref_start', 'seg2_ref_end',
+    'seg2_score', 'flags'
+    ]
+CsvHeaderReturn = Literal[
+    'id,seq,energy,seg1_ref_name,seg1_read_start,seg1_read_end,'
+    'seg1_ref_start,seg1_ref_end,seg1_score,seg2_ref_name,'
+    'seg2_read_start,seg2_read_end,seg2_ref_start,seg2_ref_end,'
+    'seg2_score,flags'
+    ]
+FoldReturn = Union[
+    Tuple[None, str],
+    Tuple[Literal['NOFOLD'], str],
+    Tuple[Literal['NOENERGY'], str],
+    Self
+    ]
 
 
 # ----- Begin HybRecord Class -----
-class HybRecord(object):
+class HybRecord:
     r"""
     Class for storing and analyzing chimeric (hybrid) RNA-seq reads in hyb format.
 
@@ -202,15 +255,15 @@ class HybRecord(object):
     # Start HybRecord Attributes
     # HybRecord : Class-Level Constants
     #: Record columns 1-3 defining parameters of the overall hybrid, defined by the Hyb format
-    HYBRID_COLUMNS = [
+    HYBRID_COLUMNS = (
         'id',      #: str, Hybrid read identifier, ex: "1257_12"
         'seq',     #: str (alphabetic), Hybrid seq, ex: "ATCGGCTAATCGGTCA..."
         'energy',  #: float or str(of float), hybrid folding energy, ex: "-11.33"
-    ]
+    )
 
     #: Record columns 4-9 and 10-15, respectively, defining annotated parameters of
     #: seg1 and seg2 respectively, defined by the Hyb format
-    SEGMENT_COLUMNS = [
+    SEGMENT_COLUMNS = (
         'ref_name',    # str, Reference ID, ex: "MIMAT0000076_MirBase_miR-21_microRNA"
         'read_start',  # int, Start position of mapping within hybrid, ex: "0"
         'read_end',    # int, End position of mapping within hybrid, ex: "21"
@@ -219,19 +272,19 @@ class HybRecord(object):
         'score',       # str(int or float) Alignment Score, can be BLAST e-score
                        #   or mapping alignment score, depending on analysis
                        #   implementation type.
-    ]
+    )
 
     # Arbitrary details included in column 16 of the hyb format in the form:
     #   “feature1=value1;feature2=value2;..."
     #   Flags utilized in the Hyb software package, see specification.
-    _HYB_FLAGS = [
+    _HYB_FLAGS = (
         'count_total',
         'count_last_clustering',
         'two_way_merged',
         'seq_IDs_in_cluster',
-    ]
+    )
     # Additional flag specifications utilized by hybkit, see specification.
-    _HYBKIT_FLAGS = [
+    _HYBKIT_FLAGS = (
         'read_count',
         'orient',
         'det',
@@ -243,7 +296,7 @@ class HybRecord(object):
         'target_reg',
         'ext',
         'dataset',
-    ]
+    )
 
     #: Flags defined by the hybkit package. Flags 1-4 are utilized by the Hyb software package.
     #: For information on flags, see the :any:`Flags` portion of the
@@ -270,18 +323,18 @@ class HybRecord(object):
     #:   :ref:`seg2_type <HybRecord-Attributes>` flags have been set
     #: * ``eval_mirna``     : :ref:`miRNA_seg <HybRecord-Attributes>` flag has been set
     #:
-    SET_PROPS = [
+    SET_PROPS = (
         'energy', 'full_seg_props', 'fold_record',
         'eval_types', 'eval_mirna', 'eval_target',
-    ]
+    )
 
     #: General record properties for the :meth:`prop` method.
     #:
     #: * ``has_indels`` : either seg1 or seg2 alignments has insertions/deletions,
     #:   shown by differing read/reference length for the same alignment
-    GEN_PROPS = [
-        'has_indels'
-    ]
+    GEN_PROPS = (
+        'has_indels',
+    )
 
     #: String-comparison properties for the :meth:`prop` method.
     #:
@@ -304,7 +357,7 @@ class HybRecord(object):
     #:   * ``suffix``   : Comparison string matches end of field
     #:   * ``contains`` : Comparison string is contained within field
     #:
-    STR_PROPS = [
+    STR_PROPS = (
         'id_is', 'id_prefix', 'id_suffix', 'id_contains',
         'seq_is', 'seq_prefix', 'seq_suffix', 'seq_contains',
         'seg1_is', 'seg1_prefix', 'seg1_suffix', 'seg1_contains',
@@ -313,7 +366,7 @@ class HybRecord(object):
         'seg1_type_is', 'seg1_type_prefix', 'seg1_type_suffix', 'seg1_type_contains',
         'seg2_type_is', 'seg2_type_prefix', 'seg2_type_suffix', 'seg2_type_contains',
         'any_seg_type_is', 'any_seg_type_prefix', 'any_seg_type_suffix', 'any_seg_type_contains',
-    ]
+    )
     #: miRNA-evaluation-related properties for the :meth:`prop` method.
     #: Requires :ref:`miRNA_seg <mirna_seg>` flag to be set by :meth:`eval_mirna` method.
     #:
@@ -325,10 +378,10 @@ class HybRecord(object):
     #: * ``5p_mirna``        : Seg1 (5p) has been identified as a miRNA
     #: * ``3p_mirna``        : Seg2 (3p) has been identified as a miRNA
     #:
-    MIRNA_PROPS = [
+    MIRNA_PROPS = (
         'has_mirna', 'no_mirna', 'mirna_dimer', 'mirna_not_dimer',
         '5p_mirna', '3p_mirna',
-    ]
+    )
 
     #: miRNA-evaluation & string-comparison properties for the :meth:`prop` method.
     #: Requires :ref:`miRNA_seg <mirna_seg>` flag to be set by :meth:`eval_mirna` method.
@@ -351,14 +404,14 @@ class HybRecord(object):
     #:   * ``suffix``   : Comparison string matches end of field
     #:   * ``contains`` : Comparison string is contained within field
     #:
-    MIRNA_STR_PROPS = [
+    MIRNA_STR_PROPS = (
         'mirna_is', 'mirna_prefix', 'mirna_suffix', 'mirna_contains',
         'target_is', 'target_prefix', 'target_suffix', 'target_contains',
         'mirna_seg_type_is', 'mirna_seg_type_prefix',
         'mirna_seg_type_suffix', 'mirna_seg_type_contains',
         'target_seg_type_is', 'target_seg_type_prefix',
         'target_seg_type_suffix', 'target_seg_type_contains',
-    ]
+    )
 
     #: All allowed properties for the :meth:`prop()` method.
     #: See :attr:`GEN_PROPS`, :attr:`STR_PROPS`, :attr:`MIRNA_PROPS`, and
@@ -367,22 +420,23 @@ class HybRecord(object):
 
     # Start HybRecord Public Methods
     # HybRecord : Public Methods : Initialization
-    def __init__(self,
-                 id,
-                 seq,
-                 energy=None,
-                 seg1_props=None,
-                 seg2_props=None,
-                 flags=None,
-                 read_count=None,
-                 allow_undefined_flags=None,
-                 ):
+    def __init__(
+            self,
+            id: str,
+            seq: str,
+            energy: Optional[StrOrNum] = None,
+            seg1_props: Optional[SegProps] = None,
+            seg2_props: Optional[SegProps] = None,
+            flags: Optional[FlagsDict] = None,
+            read_count: Optional[int] = None,
+            allow_undefined_flags: Optional[bool] = None,
+            ) -> None:
         """Describe __init__ method description in class docstring."""
         if id is None or seq is None:
             message = 'HybRecord initialization requires "id" '
             message += 'and "seq" parameters to be defined.\n'
-            message += 'ID: "%s", Seq: "%s"' % (id, seq)
-            _print_and_error(message)
+            message += f'ID: "{id}", Seq: "{seq}"'
+            raise HybkitConstructorError(message)
         self.id = self._ensure_attr_types(id, 'id')
         self.seq = self._ensure_attr_types(seq, 'seq')
         self.energy = self._ensure_attr_types(energy, 'energy')
@@ -418,7 +472,7 @@ def __init__(self,
             if 'read_count' in flags and int(flags['read_count']) != int(read_count):
                 message = '"read_count" parameter defined both in function call and flags,\n'
                 message += 'but values do not match. Please define only once.'
-                _print_and_error(message)
+                raise HybkitConstructorError(message)
             self.set_flag('read_count', str(read_count))
 
         # if fold_record is not None:
@@ -427,7 +481,12 @@ def __init__(self,
         self._post_init_tasks()    # Method stub for subclassing
 
     # HybRecord : Public Methods : flags
-    def set_flag(self, flag_key, flag_val, allow_undefined_flags=None):
+    def set_flag(
+            self,
+            flag_key: str,
+            flag_val: Value,
+            allow_undefined_flags: Optional[bool] = None
+            ) -> None:
         """
         Set the value of record ``flag_key`` to ``flag_val``.
 
@@ -445,8 +504,7 @@ def set_flag(self, flag_key, flag_val, allow_undefined_flags=None):
         if allow_undefined_flags is None:
             allow_undefined_flags = self.allow_undefined_flags
 
-        if self._flagset is None:
-            self._flagset = set(self.ALL_FLAGS + list(self.settings['custom_flags']))
+        self._ensure_flagset()
 
         if (not allow_undefined_flags
                 and flag_key not in self._flagset):
@@ -454,23 +512,28 @@ def set_flag(self, flag_key, flag_val, allow_undefined_flags=None):
             message += 'run with: "allow_undefined_flags=True", '
             message += 'add flag to "custom_flags" setting, '
             message += 'or set "HybRecord.settings.allow_undefined_flags = True".'
-            _print_and_error(message)
+            raise HybkitMiscError(message)
 
-        self.flags[flag_key] = flag_val
+        self.flags[flag_key] = str(flag_val)
 
     # HybRecord : Public Methods : Flag_Info : seg_type
-    def get_seg1_type(self, require=False):
+    def get_seg1_type(
+            self,
+            require: bool = False
+            ) -> Optional[str]:
         """
         Return the :ref:`seg1_type <seg1_type>` flag if defined, or return None.
 
         Args:
-            require (:obj:`bool`, optional): If ``True``, raise an error
-                if seg1_type is not defined.
+            require: If ``True``, raise an error if seg1_type is not defined.
         """
         return self._get_flag('seg1_type', require)
 
     # HybRecord : Public Methods : Flag_Info : seg_type
-    def get_seg2_type(self, require=False):
+    def get_seg2_type(
+            self,
+            require: bool = False
+            ) -> Optional[str]:
         """
         Return the :ref:`seg2_type <seg2_type>` flag if defined, or return None.
 
@@ -481,7 +544,10 @@ def get_seg2_type(self, require=False):
         return self._get_flag('seg2_type', require)
 
     # HybRecord : Public Methods : Flag_Info : seg_type
-    def get_seg_types(self, require=False):
+    def get_seg_types(
+            self,
+            require: bool = False
+            ) -> Tuple[Union[str, None], Union[str, None]]:
         """
         Return "seg1_type" (or None), "seg2_type" (or None) flags.
 
@@ -492,10 +558,17 @@ def get_seg_types(self, require=False):
             require (:obj:`bool`, optional): If ``True``,
                 raise an error if either flag is not defined.
         """
-        return (self._get_flag('seg1_type', require), self._get_flag('seg2_type', require))
+        ret_tuple = (
+            self._get_flag('seg1_type', require),
+            self._get_flag('seg2_type', require)
+            )
+        return ret_tuple
 
     # HybRecord : Public Methods : Flag_Info : get_read_count
-    def get_read_count(self, require=False):
+    def get_read_count(
+            self,
+            require: bool = False
+            ) -> Optional[int]:
         """
         Return the :ref:`read_count <read_count>` flag if defined, otherwise return None.
 
@@ -510,7 +583,10 @@ def get_read_count(self, require=False):
         return int(ret_val)
 
     # HybRecord : Public Methods : Flag_Info : record_count
-    def get_record_count(self, require=False):
+    def get_record_count(
+            self,
+            require: bool = False
+            ) -> int:
         """
         Return :ref:`count_total <count_total>` flag if defined, or return 1 (this record).
 
@@ -525,7 +601,11 @@ def get_record_count(self, require=False):
         return int(ret_val)
 
     # HybRecord : Public Methods : get_mirna_props
-    def get_mirna_props(self, allow_mirna_dimers=False, require=True):
+    def get_mirna_props(
+            self,
+            allow_mirna_dimers: bool = False,
+            require: bool = True
+            ) -> Optional[Dict]:
         """
         Return the seg_props dict corresponding to the miRNA segment, if set.
 
@@ -545,19 +625,29 @@ def get_mirna_props(self, allow_mirna_dimers=False, require=True):
                 if allow_mirna_dimers:
                     return self.seg1_props
                 elif require:
-                    _print_and_error('Record contains a dimer of mirna-annotated segments.')
+                    message = 'Record contains a dimer of mirna-annotated segments.\n'
+                    message += 'Please use "allow_mirna_dimers=True" to allow this.'
+                    raise HybkitMiscError(message)
                 else:
                     return None
             elif self.prop('5p_mirna'):
                 return self.seg1_props
             elif self.prop('3p_mirna'):
                 return self.seg2_props
+            else:
+                message = 'Uncaught If Branch.'
+                raise RuntimeError(message)
         elif require:
-            _print_and_error('Record does not contain a miRNA-annotated segment.')
+            message = 'Record does not contain a miRNA-annotated segment.'
+            raise HybkitMiscError(message)
         else:
             return None
 
-    def get_target_props(self, allow_mirna_dimers=False, require=True):
+    def get_target_props(
+            self,
+            allow_mirna_dimers: bool = False,
+            require: bool = True,
+            ) -> Optional[Dict]:
         """
         Return the seg_props dict corresponding to the target segment, if set.
 
@@ -579,20 +669,29 @@ def get_target_props(self, allow_mirna_dimers=False, require=True):
                 if allow_mirna_dimers:
                     return self.seg2_props
                 elif require:
-                    _print_and_error('Record contains a dimer of mirna-annotated segments.')
+                    message = 'Record contains a dimer of mirna-annotated segments.\n'
+                    message += 'Please use "allow_mirna_dimers=True" to allow this.'
+                    raise HybkitMiscError(message)
                 else:
                     return None
             elif self.prop('5p_mirna'):
                 return self.seg2_props
             elif self.prop('3p_mirna'):
                 return self.seg1_props
+            else:
+                message = 'Uncaught If Branch.'
+                raise RuntimeError(message)
         elif require:
-            _print_and_error('Record does not contain a miRNA target annotated segment.')
+            message = 'Record does not contain a miRNA-annotated segment.'
+            raise HybkitMiscError(message)
         else:
             return None
 
     # HybRecord : Public Methods : Flag_Info : eval_type
-    def eval_types(self, allow_unknown=None):
+    def eval_types(
+            self,
+            allow_unknown: Optional[bool] = None
+            ) -> None:
         """
         Find the types of each segment using the the :class:`TypeFinder` class.
 
@@ -635,14 +734,18 @@ def eval_types(self, allow_unknown=None):
                 else:
                     message = 'Cannot identify segment type for segment:\n'
                     message += self._format_seg_props(seg_props, prefix=(' ' * 2)) + '\n'
-                    _print_and_error(message)
+                    raise HybkitMiscError(message)
             else:
                 types.append(seg_type)
         self.set_flag('seg1_type', types[0])
         self.set_flag('seg2_type', types[1])
 
     # HybRecord : Public Methods : fold_record
-    def set_fold_record(self, fold_record, allow_energy_mismatch=False):
+    def set_fold_record(
+            self,
+            fold_record: FlexFoldRecord,
+            allow_energy_mismatch: bool = False
+            ) -> None:
         """
         Check and set provided fold_record (:class:`FoldRecord`) as attribute fold_record.
 
@@ -653,10 +756,14 @@ def set_fold_record(self, fold_record, allow_energy_mismatch=False):
         Args:
             fold_record (FoldRecord): :attr:`FoldRecord` instance to set as
                 :ref:`HybRecord.fold_record <HybRecord-Attributes>`.
+            allow_energy_mismatch (:obj:`bool`, optional): If ``True``, allow mismatched fold_record
+                and HybRecord energy. Otherwise, raise an error.
+
         """
         # TODO: update fold_record_reading tuple
         if fold_record is None or (isinstance(fold_record, tuple) and fold_record[0] is None):
-            _print_and_error('Trying to assign None object as FoldRecord.')
+            message = 'Trying to assign None object as FoldRecord.'
+            raise HybkitMiscError(message)
 
         if isinstance(fold_record, FoldRecord):
             use_fold_record = fold_record
@@ -665,7 +772,7 @@ def set_fold_record(self, fold_record, allow_energy_mismatch=False):
         else:
             message = 'Supplied argument to fold_record: %s' % str(fold_record)
             message += '\n   is not a FoldRecord object.'
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
 
         use_fold_record.ensure_matches_hyb_record(self)
         self.fold_record = use_fold_record
@@ -676,11 +783,15 @@ def set_fold_record(self, fold_record, allow_energy_mismatch=False):
                 message = 'ERROR: HybRecord energy: %s ' % self.energy
                 message += 'and FoldRecord energy: %s ' % self.fold_record.energy
                 message += 'do not match!'
-                _print_and_error(message)
+                raise HybkitConstructorError(message)
             self.energy = self.fold_record.energy
 
     # HybRecord : Public Methods : eval_mirna
-    def eval_mirna(self, override=False, mirna_types=None):
+    def eval_mirna(
+            self,
+            override: bool = False,
+            mirna_types: bool = None
+            ) -> None:
         """
         Analyze and set miRNA properties from type properties in the hyb record.
 
@@ -720,7 +831,11 @@ def eval_mirna(self, override=False, mirna_types=None):
                 mirna_flag = 'N'
             self.set_flag('miRNA_seg', mirna_flag)
 
-    def mirna_details(self, detail='all', allow_mirna_dimers=False):
+    def mirna_details(
+            self,
+            detail: MirnaDetailsArg = 'all',
+            allow_mirna_dimers: bool = False,
+            ) -> Optional[Union[Dict, str]]:
         """
         Provide a detail about the miRNA or target following :func:`eval_mirna`.
 
@@ -755,17 +870,17 @@ def mirna_details(self, detail='all', allow_mirna_dimers=False):
             message = 'mirna_detail method requires a hybrid containing a single mirna.\n'
             message += 'hyb_record: %s does not meet this criteria ' % str(self)
             message += 'with miRNA_seg flag: %s' % mirna_flag
-            _print_and_error(message)
+            raise HybkitMiscError(message)
 
-        ALLOWED_DETAILS = {
+        allowed_details = {
             'all', 'mirna_ref', 'target_ref', 'mirna_seg_type', 'target_seg_type',
             'mirna_seq', 'target_seq', 'mirna_fold', 'target_fold',
         }
 
-        if detail not in ALLOWED_DETAILS:
+        if detail not in allowed_details:
             message = 'Requested miRNA detail: "%s" ' % detail
-            message += 'not in allowed types: \n    %s' % ', '.join(ALLOWED_DETAILS)
-            _print_and_error(message)
+            message += 'not in allowed types: \n    %s' % ', '.join(allowed_details)
+            raise HybkitArgError(message)
 
         # Analyze miRNA details
         mirna_details = {}
@@ -780,11 +895,9 @@ def mirna_details(self, detail='all', allow_mirna_dimers=False):
             mirna_details['target_seg_type'] = self.get_seg1_type(require=True)
             mirna_props = self.seg2_props
             target_props = self.seg1_props
-        # else:
-        #     message = 'Problem with eval_mirna for hyb_record: %s ' % str(self)
-        #     message += 'Undefined value: %s found for flag: miRNA_seg' % mirna_flag
-        #     print(message)
-        #     raise RuntimeError(message)
+        else:
+            message = 'Uncaught If Branch.'
+            raise RuntimeError(message)
 
         mirna_details['mirna_ref'] = mirna_props['ref_name']
         mirna_details['target_ref'] = target_props['ref_name']
@@ -803,7 +916,7 @@ def mirna_details(self, detail='all', allow_mirna_dimers=False):
             return mirna_details[detail]
 
     # HybRecord : Public Methods : Record Properties
-    def mirna_detail(self, *args, **kwargs):
+    def mirna_detail(self, *args, **kwargs):  # noqa: ANN201 ANN101 ANN002 ANN003
         """
         Deprecate, alias for :meth:`mirna_details`.
 
@@ -812,7 +925,10 @@ def mirna_detail(self, *args, **kwargs):
         return self.mirna_details(*args, **kwargs)
 
     # HybRecord : Public Methods : Record Properties
-    def is_set(self, prop):
+    def is_set(
+            self,
+            prop: str,
+            ) -> bool:
         """
         Return ``True`` if HybRecord property "prop" is set (if relevant) and is not ``None``.
 
@@ -824,7 +940,7 @@ def is_set(self, prop):
         if prop not in self._SET_PROPS_SET:
             message = 'Requested Property: %s is not defined. ' % prop
             message += 'Available properties are:\n' + ', '.join(self.SET_PROPS)
-            _print_and_error(message)
+            raise HybkitMiscError(message)
 
         if prop in {'energy', 'fold_record'}:
             ret_bool = (getattr(self, prop) is not None)
@@ -848,7 +964,10 @@ def is_set(self, prop):
         return ret_bool
 
     # HybRecord : Public Methods : Record Properties
-    def not_set(self, prop):
+    def not_set(
+            self,
+            prop: str
+            ) -> bool:
         """
         Return ``False`` if HybRecord property "prop" is set (if relevant) and is not ``None``.
 
@@ -860,7 +979,11 @@ def not_set(self, prop):
         return not self.is_set(prop)
 
     # HybRecord : Public Methods : Record Properties
-    def prop(self, prop, prop_compare=None):
+    def prop(
+            self,
+            prop: str,
+            prop_compare: Optional[str] = None,
+            ) -> bool:
         """
         Return ``True`` if HybRecord has property: ``prop``.
 
@@ -886,14 +1009,14 @@ def prop(self, prop, prop_compare=None):
         if prop not in self._HAS_PROPS_SET:
             message = 'Requested Property: %s is not defined. ' % prop
             message += 'Available properties are:\n' + ', '.join(self.HAS_PROPS)
-            _print_and_error(message)
+            raise HybkitMiscError(message)
 
         # Check if a substring compares to a desired property string.
         if prop in self._GEN_PROPS_SET:
             if prop == 'has_indels':
                 if not self.is_set('full_seg_props'):
                     message = 'Checking for indels requires full_seg_props to be set.'
-                    _print_and_error(message)
+                    raise HybkitMiscError(message)
                 has_indels = False
                 for seg_props in (self.seg1_props, self.seg2_props):
                     read_len = seg_props['read_end'] - seg_props['read_start']
@@ -908,7 +1031,7 @@ def prop(self, prop, prop_compare=None):
             if not prop_compare:
                 message = 'Property: %s  requires a comparison string. ' % prop
                 message += 'Please provide an argument to prop_compare.'
-                _print_and_error(message)
+                raise HybkitMiscError(message)
 
             if prop in self._MIRNA_STR_PROPS_SET:
                 self._ensure_set('eval_mirna')
@@ -959,7 +1082,7 @@ def prop(self, prop, prop_compare=None):
                 if info is None:
                     message = 'HybRecord Instance: %s does not have a ' % (str(self))
                     message += 'value for requested property: %s' % check_attr
-                    _print_and_error(message)
+                    raise HybkitMiscError(message)
 
             if check_type == 'prefix':
                 ret_val = any((val.startswith(prop_compare)) for val in check_info)
@@ -1009,7 +1132,7 @@ def prop(self, prop, prop_compare=None):
         return ret_val
 
     # HybRecord : Public Methods : Record Properties
-    def has_prop(self, *args, **kwargs):
+    def has_prop(self, *args, **kwargs): # noqa: ANN201 ANN101 ANN002 ANN003
         """
         Return ``True`` if HybRecord has property: ``prop``.
 
@@ -1020,7 +1143,11 @@ def has_prop(self, *args, **kwargs):
         return self.prop(*args, **kwargs)
 
     # HybRecord : Public Methods : Record Parsing
-    def to_line(self, newline=True, sep='\t'):
+    def to_line(
+            self,
+            newline: bool = True,
+            sep: str = '\t',
+            ) -> str:
         r"""
         Return a hyb-format string representation of the record.
 
@@ -1048,13 +1175,16 @@ def to_line(self, newline=True, sep='\t'):
         if flag_string:
             line_items.append(flag_string)
 
-        ret_string = sep.join((str(x) for x in line_items))
+        ret_string = sep.join(str(x) for x in line_items)
         if newline:
             ret_string += '\n'
         return ret_string
 
     # HybRecord : Public Methods : Record Parsing
-    def to_csv(self, newline=False):
+    def to_csv(
+            self,
+            newline: bool = False
+            ) -> str:
         """
         Return a comma-separated hyb-format string representation of the record.
 
@@ -1063,8 +1193,48 @@ def to_csv(self, newline=False):
         """
         return self.to_line(newline, sep=',')
 
+# HybRecord : Public Methods : Record Parsing
+    def to_fields(
+            self,
+            missing_obj : Optional[Value] = None
+            ) -> dict:
+        """
+        Return a python dictionary representation of the record.
+
+        Returns a dictionary with keys corresponding to the fields in the hyb-format
+        file, and values corresponding to the values in the record. Output can be
+        used with the pandas DataFrame constructor or csv.DictWriter.
+
+        Args:
+            missing_obj (optional): Object to use for missing values. Default = :obj:`None`.
+
+        """
+        ret_dict = {}
+        for item_key in self.HYBRID_COLUMNS:
+            item_val = getattr(self, item_key, None)
+            if item_val is None:
+                item_val = copy.deepcopy(missing_obj)
+            ret_dict[item_key] = item_val
+        for i, seg_dict in enumerate([self.seg1_props, self.seg2_props], start=1):
+            for item_key in self.SEGMENT_COLUMNS:
+                out_item_key = f'seg{i}_{item_key}'
+                if item_key in seg_dict and seg_dict[item_key] is not None:
+                    ret_dict[out_item_key] = seg_dict[item_key]
+                else:
+                    ret_dict[out_item_key] = copy.deepcopy(missing_obj)
+        if self.flags:
+            ret_dict['flags'] = copy.deepcopy(self.flags)
+        else:
+            ret_dict['flags'] = copy.deepcopy(missing_obj)
+        return ret_dict
+
     # HybRecord : Public Methods : Record Parsing
-    def to_fasta_record(self, mode='hybrid', annotate=True, allow_mirna_dimers=False):
+    def to_fasta_record(
+            self,
+            mode: ToFastaRecordArg = 'hybrid',
+            annotate: bool = True,
+            allow_mirna_dimers: bool = False
+        ) -> SeqRecord:
         """
         Return nucleotide sequence as BioPython SeqRecord object.
 
@@ -1083,16 +1253,17 @@ def to_fasta_record(self, mode='hybrid', annotate=True, allow_mirna_dimers=False
                                                    | returned as miRNA sequence (the 5p segment
                                                    | will be selected as the "miRNA").
         """
+        global Bio  # noqa: PLW0602
         if Bio is None:
-            print('Please install BioPython package and ensure it can be imported.')
-            raise bio_module_error
+            message = 'BioPython is required for fasta output.'
+            raise ModuleNotFoundError(message)
         allowed_modes = ['hybrid', 'seg1', 'seg2', 'mirna', 'target']
         allowed_modes_set = set(allowed_modes)
         mode = mode.lower()
-        if mode not in allowed_modes:
+        if mode not in allowed_modes_set:
             message = 'Mode %s not allowed for parsing as fasta record.\n' % mode
             message += '    Allowed Modes: ' + ', '.join(allowed_modes)
-            _print_and_error(message)
+            raise HybkitMiscError(message)
 
         fasta_description = ''
         if mode == 'hybrid':
@@ -1107,15 +1278,16 @@ def to_fasta_record(self, mode='hybrid', annotate=True, allow_mirna_dimers=False
 
         if mode in {'mirna', 'target'}:
             if self.not_set('eval_mirna'):
-                _print_and_error('eval_mirna must be performed before miRNA/target fasta output')
+                message = 'eval_mirna must be performed before miRNA/target fasta output'
+                raise HybkitMiscError(message)
             elif self.prop('no_mirna'):
                 message = 'miRNA / target cannot be output as fasta because record ' + str(self)
                 message += 'does not have a miRNA.'
-                _print_and_error(message)
+                raise HybkitMiscError(message)
             elif self.prop('mirna_dimer') and not allow_mirna_dimers:
                 message = 'miRNA / target cannot be output as fasta because record ' + str(self)
                 message += 'has a miRNA dimer.'
-                _print_and_error(message)
+                raise HybkitMiscError(message)
 
             if annotate:
                 fasta_description += mode + '--'
@@ -1151,7 +1323,11 @@ def to_fasta_record(self, mode='hybrid', annotate=True, allow_mirna_dimers=False
         return fasta_record
 
     # HybRecord : Public Methods : Record Parsing
-    def to_fasta_str(self, mode='hybrid', annotate=True):
+    def to_fasta_str(
+            self,
+            mode: ToFastaRecordArg = 'hybrid',
+            annotate: bool = True
+        ) -> str:
         """
         Return nucleotide sequence as a fasta string.
 
@@ -1164,34 +1340,39 @@ def to_fasta_str(self, mode='hybrid', annotate=True):
 
     # Start HybRecord Magic Methods
     # HybRecord : Public MagicMethods : Comparison
-    def __eq__(self, other):
+    def __eq__(self, other: Self) -> bool:
         """Return ``True`` if ".id" and ".seq" attributes match."""
         return (self.id == other.id and self.seq == other.seq)
 
     # HybRecord : Public MagicMethods : Evaluation
-    def __hash__(self):
+    def __hash__(self) -> int:
         """Return a hash of the record ".id" attribute."""
         return hash(self.id)
 
     # HybRecord : Public MagicMethods : Evaluation
-    def __bool__(self):
+    def __bool__(self) -> Literal[True]:
         """Return ``True`` wherever the class is defined."""
         return True
 
     # HybRecord : Public MagicMethods : Evaluation
-    def __len__(self):
+    def __len__(self) -> int:
         """Return the length of the genomic sequence."""
         return len(self.seq)
 
     # HybRecord : Public MagicMethods : Printing
-    def __str__(self):
+    def __str__(self) -> str:
         """Print the identifier of the record."""
         return '<HybRecord ID: %s>' % self.id
 
     # Start HybRecord Public Classmethods
     # HybRecord : Public Classmethods : Record Construction
     @classmethod
-    def from_line(cls, line, hybformat_id=False, hybformat_ref=False):
+    def from_line(
+            cls,
+            line: str,
+            hybformat_id: bool = False,
+            hybformat_ref: bool = False,
+            ) -> Self:
         """
         Construct a HybRecord instance from a single-line hyb-format string.
 
@@ -1214,12 +1395,14 @@ def from_line(cls, line, hybformat_id=False, hybformat_ref=False):
             :class:`HybRecord` instance containing record information.
         """
         line_items = line.strip().split('\t')
-        if len(line_items) < 15 or len(line_items) > 16:
+        if (len(line_items) < hybkit.settings.MIN_RECORD_FIELDS
+            or len(line_items) > hybkit.settings.MAX_RECORD_FIELDS
+            ):
             message = 'Hyb record lines require 15 or 16 fields '
             message += 'separated by tab ("\\t") characters, '
             message += 'but only %i were found:\n"%s"' % (len(line_items), line.strip())
             message += 'Line:\n%s' % line
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
         hyb_id = line_items[0]
         seq = line_items[1]
         energy = line_items[2]
@@ -1238,7 +1421,7 @@ def from_line(cls, line, hybformat_id=False, hybformat_ref=False):
         seg2_props['ref_end'] = line_items[13]
         seg2_props['score'] = line_items[14]
         flags = {}
-        if len(line_items) > 15:
+        if len(line_items) > hybkit.settings.MIN_RECORD_FIELDS:
             flags = cls._read_flags(line_items[15])
 
         if hybformat_id:
@@ -1270,7 +1453,7 @@ def from_line(cls, line, hybformat_id=False, hybformat_ref=False):
                     message = 'Problem reading in hybformat ref for reference: %s\n' % ref
                     message += 'Inferred type: %s\n' % seg_type
                     message += 'Does not equal current type flag: %s' % flags[seg_type_key]
-                    _print_and_error(message)
+                    raise HybkitConstructorError(message)
                 elif seg_type_key not in flags:
                     flags[seg_type_key] = seg_type
 
@@ -1279,7 +1462,14 @@ def from_line(cls, line, hybformat_id=False, hybformat_ref=False):
 
     # HybRecord : Public Classmethods : Record Construction
     @classmethod
-    def from_fasta_records(cls, seg1_record, seg2_record, hyb_id=None, energy=None, flags={}):
+    def from_fasta_records(
+            cls,
+            seg1_record: SeqRecord,
+            seg2_record: SeqRecord,
+            hyb_id: Optional[str] = None,
+            energy: Optional[StrOrNum] = None,
+            flags: Optional[FlagsDict] = None,
+            ) -> Self:
         """
         Construct a HybRecord instance from two BioPython SeqRecord Objects.
 
@@ -1318,11 +1508,14 @@ def from_fasta_records(cls, seg1_record, seg2_record, hyb_id=None, energy=None,
         Returns:
             :class:`HybRecord` instance containing record information.
         """
-        for segN_record in [seg1_record, seg2_record]:
-            if not isinstance(segN_record, Bio.SeqRecord.SeqRecord):
+        if flags is None:
+            flags = {}
+
+        for segn_record in [seg1_record, seg2_record]:
+            if not isinstance(segn_record, Bio.SeqRecord.SeqRecord):
                 message = 'Record is not a valid SeqRecord Object:\n    '
-                message += str(segN_record)
-                _print_and_error(message)
+                message += str(segn_record)
+                raise HybkitConstructorError(message)
 
         if hyb_id is None:
             hyb_id = seg1_record.id + '--' + seg2_record.id
@@ -1350,35 +1543,67 @@ def from_fasta_records(cls, seg1_record, seg2_record, hyb_id=None, energy=None,
             flags['seg2_det'] = seg2_record.description
 
         return_obj = cls(hyb_id, seq, energy, seg1_props, seg2_props, flags)
-        # print(seg1_record)
-        # print(seg2_record)
-        # print(return_obj.to_line())
-        # input()
         return return_obj
 
+    @classmethod
+    # HybRecord : Public Classmethods : Record Parsing
+    def to_fields_header(cls) -> FieldsHeaderReturn:
+        """
+        Return a list of the fields in a :class:`HybRecord` object.
+
+        For use with the :func:`to_fields` method.
+        """
+        ret_fields = copy.deepcopy(list(cls.HYBRID_COLUMNS))
+        for i in range(1,3):
+            for item_key in cls.SEGMENT_COLUMNS:
+                out_item_key = f'seg{i}_{item_key}'
+                ret_fields.append(out_item_key)
+        ret_fields.append('flags')
+        return tuple(ret_fields)
+
+    @classmethod
+    # HybRecord : Public Classmethods : Record Parsing
+    def to_csv_header(cls, newline: bool = False) -> CsvHeaderReturn:
+        """
+        Return a comma-separated string representation of the fields in the record.
+
+        For use with the :func:`to_csv` method.
+
+        Args:
+            newline (:obj:`bool`, optional): If ``True``, end the returned string with a newline.
+        """
+        ret_str = ','.join(cls.to_fields_header())
+        if newline:
+            ret_str += '\n'
+        return ret_str
+
     # Start HybRecord Private Constants
     # HybRecord : Private Constants
-    _SET_PROPS_SET = set(SET_PROPS)
-    _GEN_PROPS_SET = set(GEN_PROPS)
-    _STR_PROPS_SET = set(STR_PROPS)
-    _MIRNA_PROPS_SET = set(MIRNA_PROPS)
-    _MIRNA_STR_PROPS_SET = set(MIRNA_STR_PROPS)
-    _ALL_STR_PROPS_SET = _STR_PROPS_SET | _MIRNA_STR_PROPS_SET
+    _SET_PROPS_SET = frozenset(SET_PROPS)
+    _GEN_PROPS_SET = frozenset(GEN_PROPS)
+    _STR_PROPS_SET = frozenset(STR_PROPS)
+    _MIRNA_PROPS_SET = frozenset(MIRNA_PROPS)
+    _MIRNA_STR_PROPS_SET = frozenset(MIRNA_STR_PROPS)
+    _ALL_STR_PROPS_SET = frozenset(_STR_PROPS_SET | _MIRNA_STR_PROPS_SET)
     # _TARGET_PROPS_SET = set(TARGET_PROPS)
-    _HAS_PROPS_SET = set(HAS_PROPS)
+    _HAS_PROPS_SET = frozenset(HAS_PROPS)
 
     # Placeholder for set of allowed flags filled on first use.
     _flagset = None
 
     # Start HybRecord Private Methods
     # HybRecord : Private Methods : Initialization
-    def _post_init_tasks(self):
+    def _post_init_tasks(self) -> None:
         # Stub for subclassing
         pass
 
     # HybRecord : Private Methods : Record Parsing
     # Ensure attributes passed to constructor are valid for the respective HybRecord attributes
-    def _ensure_attr_types(self, value, attribute):
+    def _ensure_attr_types(
+            self,
+            value: Any,  # noqa: ANN401
+            attribute: str,
+            ) -> Any:  # noqa: ANN401
         err_message = None
         # Value is checked for placeholder value: '.' and converted to None if found.
         if value == '.':
@@ -1388,7 +1613,7 @@ def _ensure_attr_types(self, value, attribute):
             if not isinstance(value, str):
                 err_message = (
                     'id must be a string. Provided id is type '
-                    '%s: %s' % (type(value), value)
+                    f'{type(value)}: {value}'
                 )
             elif not value.strip():
                 err_message = ('id must be a non-empty string. '
@@ -1397,11 +1622,10 @@ def _ensure_attr_types(self, value, attribute):
         # Check "seq" is valid str
         elif attribute == 'seq':
             if not isinstance(value, str):
-                err_message = ('seq must be a string. Provided seq is type %s: %s' %
-                               (type(value), value))
+                err_message = (f'seq must be a string. Provided seq is type {type(value)}: {value}')
             elif not value.strip():
-                err_message = ('seq must be a non-empty string. Provided seq: "%s" is an empty '
-                               'string' % value)
+                err_message = (f'seq must be a non-empty string. Provided seq: "{value}" is an'
+                               ' empty string')
             elif not value.isalpha():
                 err_message = 'seq must be alphabetic. Provided seq is non-alphabetic: %s' % value
 
@@ -1410,7 +1634,7 @@ def _ensure_attr_types(self, value, attribute):
         elif attribute == 'energy':
             if not isinstance(value, (float, int, type(None), str)):
                 err_message = ('energy must be a float, int, numeric str, or None. '
-                               'Provided energy: "%s" is type %s' % (value, type(value)))
+                               f'Provided energy: "{value}" is type {type(value)}')
             if isinstance(value, str):
                 if not value.strip():
                     err_message = ('energy must be a float, int, numeric str, or None. '
@@ -1431,44 +1655,44 @@ def _ensure_attr_types(self, value, attribute):
         elif attribute == 'seg_props':
             if not isinstance(value, (dict)):  # Nonetype disallowed, need empty dict.
                 err_message = ('segN_props must be a dict. Provided segN_props is type '
-                               '%s: %s' % (type(value), value))
+                               f'{type(value)}: {value}')
             else:
                 for prop_key, prop_val in value.items():
                     if prop_key == 'ref_name':
                         if not isinstance(prop_val, (str, type(None))):
                             err_message = ('segN_props ref_name must be a string (or None). '
                                            'Provided ref_name is type '
-                                           '%s: %s' % (type(prop_val), prop_val))
+                                           f'{type(prop_val)}: {prop_val}'
+                                           )
                             break
                         elif isinstance(prop_val, str) and not prop_val.strip():
                             err_message = ('segN_props ref_name must be a non-empty string '
                                            '(or None). Provided ref_name is empty string: '
-                                           '"%s"' % prop_val)
+                                           f'"{prop_val}"')
                             break
                     elif prop_key in {'read_start', 'read_end', 'ref_start', 'ref_end'}:
                         if not isinstance(prop_val, (int, str, type(None))):
                             err_message = (
-                                'segN_props %s must be an int (or None). '
+                                f'segN_props {prop_key} must be an int (or None). '
                                 'Provided read_start: '
-                                '"%s" is type %s: %s' % (
-                                    prop_key, prop_val,
-                                    type(prop_val), prop_val
-                                )
+                                f'"{prop_val}" is type {type(prop_val)}: {prop_val}'
                             )
                             break
                         elif isinstance(prop_val, str):
                             if not prop_val.strip():
                                 err_message = (
-                                    'segN_props %s must be an int (or None). Provided %s is '
-                                    'empty string: "%s"' % (prop_key, prop_key, prop_val)
+                                    f'segN_props "{prop_key}" must be an int (or None). '
+                                    f'Provided "{prop_key}" is '
+                                    f'empty string: "{prop_val}"'
                                 )
                                 break
                             elif prop_val == '.':
                                 value[prop_key] = None
                             elif '_' in prop_val:
                                 err_message = (
-                                    'segN_props %s must be an int (or None). Provided %s is '
-                                    'non-numeric string: "%s"' % (prop_key, prop_key, prop_val)
+                                    f'segN_props {prop_key} must be an int (or None). '
+                                    f'Provided {prop_key} is '
+                                    f'non-numeric string: "{prop_val}"'
                                 )
                                 break
                             else:
@@ -1476,17 +1700,17 @@ def _ensure_attr_types(self, value, attribute):
                                     value[prop_key] = int(prop_val)
                                 except ValueError:
                                     err_message = (
-                                        'segN_props %s must be an int (or None). Provided %s '
-                                        'is non-numeric string: "%s"'
-                                        '' % (prop_key, prop_key, prop_val)
+                                        f'segN_props {prop_key} must be an int (or None). '
+                                        f'Provided {prop_key} '
+                                        f'is non-numeric string: "{prop_val}"'
                                     )
                                     break
                     elif prop_key == 'score':
                         if not isinstance(prop_val, (float, int, str, type(None))):
                             err_message = (
                                 'segN_props score must be a float, int, numeric str, or None. '
-                                'Provided score: "%s" is type %s" '
-                                '%s' % (prop_val, type(prop_val), prop_val)
+                                f'Provided score: "{prop_val}" is type {type(prop_val)}" '
+                                f'{prop_val}'
                             )
                             break
                     else:
@@ -1501,23 +1725,30 @@ def _ensure_attr_types(self, value, attribute):
             if not isinstance(value, dict):  # Nonetype disallowed, need empty dict.
                 err_message = (
                     'flags must be a dict. Provided flags is type '
-                    '%s: %s' % (type(value), value)
+                    f'{type(value)}: {value}'
                 )
             else:
                 for key, val in value.items():
                     if not isinstance(val, str):
                         err_message = (
                             'flags values must be str. Provided value for flag: '
-                            '%s is type %s: %s' % (key, type(val), val)
+                            f'{key} is type {type(val)}: {val}'
                         )
                         break
         if err_message is not None:
-            _print_and_error(err_message)
+            raise HybkitConstructorError(err_message)
+            return None
         else:
             return value
 
     # HybRecord : Private Methods : Record Parsing
-    def _format_seg_props(self, seg_props, prefix='', suffix='', indent_str=''):
+    def _format_seg_props(
+            self,
+            seg_props: SegProps,
+            prefix: str = '',
+            suffix: str = '',
+            indent_str: str = ''
+            ) -> str:
         # Returns a formatted string of the segment properties
         ret_string = prefix
         ret_string += indent_str + 'Map Reference:  %s\n' % seg_props['ref_name']
@@ -1530,24 +1761,29 @@ def _format_seg_props(self, seg_props, prefix='', suffix='', indent_str=''):
         return ret_string
 
     # HybRecord : Private Methods : Record Parsing
-    def _format_seg_props_line(self, seg_props, prefix='', suffix=''):
+    def _format_seg_props_line(
+            self,
+            seg_props: SegProps,
+            prefix: str = '',
+            suffix: str = '',
+            ) -> str:
         # Returns a single-line formatted string of the segment properties
         ret_string = prefix
         if seg_props['ref_name'] is not None:
             ret_string += seg_props['ref_name']
             read_vals = [str(seg_props[key]) if seg_props[key] is not None
                          else '??' for key in ['read_start', 'read_end']]
-            if any([read_vals[i] != '??' for i in [0, 1]]):
-                ret_string += ':%s-%s' % tuple(read_vals)
+            if any(read_vals[i] != '??' for i in [0, 1]):
+                ret_string += ':{}-{}'.format(*tuple(read_vals))
             ref_vals = [str(seg_props[key]) if seg_props[key] is not None
                         else '??' for key in ['ref_start', 'ref_end']]
-            if any([ref_vals[i] != '??' for i in [0, 1]]):
-                ret_string += '(%s-%s)' % tuple(ref_vals)
+            if any(ref_vals[i] != '??' for i in [0, 1]):
+                ret_string += '({}-{})'.format(*tuple(ref_vals))
         ret_string += suffix
         return ret_string
 
     # HybRecord : Private Methods : Segment Parsing
-    def _ensure_props_read_start_end(self):
+    def _ensure_props_read_start_end(self) -> None:
         for seg_n, seg_props in enumerate([self.seg1_props, self.seg2_props], start=1):
             for key in ('read_start', 'read_end'):
                 if key not in seg_props or seg_props[key] is None:
@@ -1555,20 +1791,20 @@ def _ensure_props_read_start_end(self):
                     message += 'segN_prop dicts for creating a "dynamic_seq" object\n'
                     message += 'Record: %s, dict: seg%i_props ' % (str(self), seg_n)
                     message += 'is missing key: %s.' % key
-                    _print_and_error(message)
+                    raise HybkitConstructorError(message)
 
     # HybRecord : Private Methods : Segment Parsing
-    def _get_seg_seq(self, seg_props):
+    def _get_seg_seq(self, seg_props: SegProps) -> str:
         if any(seg_props[v] is None for v in ['read_start', 'read_end']):
             message = 'Segment subsequence cannot be obtained for '
-            message += 'Record %s, Segment %s.\n' % (str(self), seg_props['ref_name'])
+            message += f'Record {self!s}, Segment {seg_props["ref_name"]}.\n'
             message += 'Record segment is missing one of read_start/read_end.'
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
         read_start, read_end = seg_props['read_start'], seg_props['read_end']
         return self.seq[(read_start - 1):read_end]
 
     # HybRecord : Private Methods : Segment Parsing
-    def _get_dynamic_seq(self):
+    def _get_dynamic_seq(self) -> str:
         """Return the record "dynamic" seq by constructing from segment starts/stops."""
         self._ensure_props_read_start_end()
         seq1 = self._get_seg_seq(self.seg1_props)
@@ -1576,24 +1812,28 @@ def _get_dynamic_seq(self):
         return seq1 + seq2
 
     # HybRecord : Private Methods : flags
-    def _get_flag(self, flag_key, require=False):
+    def _get_flag(
+            self,
+            flag_key: str,
+            require: bool = False
+            ) -> Any:  # noqa: ANN401
         if flag_key in self.flags:
             return self.flags[flag_key]
         elif require:
             message = 'Expected Flag Key: %s, but it is not present in record.' % flag_key
-            _print_and_error(message)
+            raise HybkitMiscError(message)
         else:
             return None
 
     # HybRecord : Private Methods : flags
-    def _make_flag_string(self, reorder_flags=None):
+    def _make_flag_string(self, reorder_flags: Optional[bool] = None) -> str:
         flag_string = ''
         for flag in self._get_flag_keys(reorder_flags=reorder_flags):
-            flag_string += ('%s=%s;' % (flag, str(self.flags[flag])))
+            flag_string += (f'{flag}={self.flags[flag]!s};')
         return flag_string.rstrip(';')
 
     # HybRecord : Private Methods : flags
-    def _get_flag_keys(self, reorder_flags=None):
+    def _get_flag_keys(self, reorder_flags: Optional[bool] = None) -> List[str]:
         # reorder_flags option returns flags in default ordering scheme.
         #  If reorder-flags argument provided, it overrides default behavior.
         #  Otherwise, the method falls back to the object-default.
@@ -1607,11 +1847,11 @@ def _get_flag_keys(self, reorder_flags=None):
         return return_list
 
     # HybRecord : Private Methods : flags
-    def _get_ordered_flag_keys(self):
-        if self._flagset is None:
-            self._flagset = set(self.ALL_FLAGS + list(self.settings['custom_flags']))
+    def _get_ordered_flag_keys(self) -> List[str]:
+        self._ensure_flagset()
         return_list = []
-        for flag in self.ALL_FLAGS + self.settings['custom_flags']:
+
+        for flag in self.ALL_FLAGS + tuple(self.settings['custom_flags']):
             if flag in self.flags:
                 return_list.append(flag)
         for flag in self.flags:
@@ -1620,28 +1860,29 @@ def _get_ordered_flag_keys(self):
         return return_list
 
     # HybRecord : Private Methods : flags
-    def _make_flags_dict(self, flag_obj):
+    def _make_flags_dict(self, flag_obj: FlagsDict) -> FlagsDict:
         #  allow_undefined_flags allows the inclusion of flags not defined in hybkit.
         #  If argument is provided to the method, it overrides default behavior.
         #  Otherwise, the method falls back to the object-defaults.
         allow_undefined_flags = self.allow_undefined_flags
 
-        if self._flagset is None:
-            self._flagset = set(self.ALL_FLAGS + list(self.settings['custom_flags']))
-
+        self._ensure_flagset()
         if not allow_undefined_flags:
             for flag in flag_obj:
                 if flag not in self._flagset:
                     message = 'Flag "%s" is not defined. Please check flag key' % flag
                     message += ' or run with: "allow_undefined_flags=True"\n'
                     message += 'Defined Flags are: '
-                    message += ', '.join(self.ALL_FLAGS + self.settings['custom_flags'])
-                    _print_and_error(message)
+                    message += ', '.join(self.ALL_FLAGS + tuple(self.settings['custom_flags']))
+                    raise HybkitMiscError(message)
 
         return {k: str(v) for k, v in flag_obj.items()}
 
     # HybRecord : Private Methods : seg_props
-    def _make_seg_props_dict(self, seg_props_obj=None):
+    def _make_seg_props_dict(
+            self,
+            seg_props_obj: Optional[SegProps] = None
+            ) -> SegProps:
         # Create a dictionary with mapping entries for all segment properties.
         return_dict = {}
         if seg_props_obj is None:
@@ -1654,53 +1895,59 @@ def _make_seg_props_dict(self, seg_props_obj=None):
         return return_dict
 
     # HybRecord : Private Methods : properties
-    def _ensure_set(self, prop):
+    def _ensure_set(
+            self,
+            prop: str,
+            ) -> Literal[True]:
         if self.not_set(prop):
             message = 'Problem with HybRecord instance: %s\n' % str(self)
             message += 'Method requires set attribute/evaluation: "%s" before use.' % prop
-            _print_and_error(message)
+            raise HybkitMiscError(message)
         return True
 
     # Start HybRecord Private Classmethods
     # HybRecord : Private Classmethods : hybformat record parsing
     @classmethod
-    def _parse_hybformat_id(cls, record_id):
+    def _parse_hybformat_id(cls, record_id: str) -> Tuple[str, str]:
         # Parse id in format: "48_50002" into read_id, read_count
         split_id = tuple(record_id.split('_'))
-        if not len(split_id) == 2:
+        if len(split_id) != 2:  # noqa: PLR2004
             message = 'Failed attempt to parse record id: %s in hyb format.\n' % record_id
             message += 'Hyb-Program format record ids have form: <read_id>_<read_count>'
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
 
         return split_id
 
     # HybRecord : Private Classmethods : hybformat record parsing
     @classmethod
-    def _parse_hybformat_ref(cls, seg_ref):
+    def _parse_hybformat_ref(cls, seg_ref: str) -> Tuple[str, str, str, str]:
         # Parse reference sequence identifier in format:
         # "ENSG00000146425_ENST00000367089_DYNLT1_mRNA"
         # into <gene_id>_<transcript_id>_<gene_name>_<seg_type> information.
         split_ref = seg_ref.split('_')
-        if not len(split_ref) == 4:
+        if len(split_ref) != 4:  # noqa: PLR2004
             message = 'Failed attempt to parse segment reference id: "%s"' % seg_ref
             message += ' in hyb format.\n'
             message += 'Hyb-Program format record ids have form:\n'
             message += '    <gene_id>_<transcript_id>_<gene_name>_<seg_type>'
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
 
         return (split_ref[0], split_ref[1], split_ref[2], split_ref[3])
 
     # HybRecord : Private Classmethods : flags
     @classmethod
-    def _read_flags(cls, flag_string, allow_undefined_flags=None):
+    def _read_flags(
+            cls,
+            flag_string: str,
+            allow_undefined_flags: Optional[bool] = None,
+            ) -> FlagsDict:
         # allow_undefined_flags allows the inclusion of flags not defined in hybkit.
         # undefined flags allowed in this method by default, to allow the object-level setting to
         # take precedence
         if allow_undefined_flags is None:
             allow_undefined_flags = cls.settings['allow_undefined_flags']
 
-        if cls._flagset is None:
-            cls._flagset = set(cls.ALL_FLAGS + list(cls.settings['custom_flags']))
+        cls._ensure_flagset()
         flag_string = flag_string.rstrip()
         flag_string = flag_string.rstrip(';')
         flag_pairs = [flag_pair.split('=') for flag_pair in flag_string.split(';')]
@@ -1709,19 +1956,28 @@ def _read_flags(cls, flag_string, allow_undefined_flags=None):
             if not allow_undefined_flags and flag_key not in cls._flagset:
                 message = 'Problem: Undefined Flag: %s\n' % flag_key
                 message += 'Defined Flags: '
-                message += ', '.join(cls.ALL_FLAGS + cls.settings['custom_flags'])
-                _print_and_error(message)
+                message += ', '.join(cls.ALL_FLAGS + tuple(cls.settings['custom_flags']))
+                raise HybkitConstructorError(message)
 
             flags[flag_key] = str(flag_value)
         return flags
 
+    # HybRecord : Private classmethods : flags
+    @classmethod
+    def _ensure_flagset(cls: Self) -> None:
+        """Ensure the flagset has been created."""
+        if cls._flagset is None:
+            cls._flagset = frozenset(cls.ALL_FLAGS + tuple(cls.settings['custom_flags']))
+
 
 # ----- Begin HybFile Class -----
-class HybFile(object):
+class HybFile:
     r"""
     Wrapper for a hyb-format text file which returns entries (lines) as HybRecord objects.
 
     Args:
+        path (str): Path to text file to open as hyb-format file.
+        *args: Arguments passed to :func:`open` function to open a text file for reading/writing.
         hybformat_id (:obj:`bool`, optional): If ``True``, during parsing of lines read count
             information from identifier in ``<read_number>_<read_count>`` format.
             Defaults to value in :attr:`settings['hybformat_id'] <HybFile.settings>`.
@@ -1729,11 +1985,17 @@ class HybFile(object):
             additional record information from
             identifier in ``<gene_id>_<transcript_id>_<gene_name>_<seg_type>`` format.
             Defaults to value in :attr:`settings['hybformat_ref'] <HybFile.settings>`.
+        from_file_like (:obj:`bool`, optional): If ``True``, the first argument is treated as a
+            file-like object (such as io.StringIO or gzip.GzipFile) and the remaining positional
+            arguments are ignored. (Default False``)
+        **kwargs: Keyword arguments passed to :func:`open` function to open a text file for
+            reading/writing.
 
     Attributes:
         hybformat_id (bool): Read count information from identifier during line parsing
         hybformat_ref (bool): Read type information from reference name
             during line parsing
+        fh (file): Underlying file handle for the HybFile object.
 
     """
 
@@ -1742,14 +2004,20 @@ class HybFile(object):
 
     # Start HybFile Public Methods
     # HybFile : Public Methods : Initialization / Closing
-    def __init__(self,
-                 *args,
-                 hybformat_id=None,
-                 hybformat_ref=None,
-                 **kwargs
-                 ):
+    def __init__(
+            self,
+            path: str,
+            *args: Any, # noqa: ANN401
+            hybformat_id: Optional[bool] = None,
+            hybformat_ref: Optional[bool] = None,
+            from_file_like: bool = False,
+            **kwargs: Any, # noqa: ANN401
+            ) -> None:
         """Describe __init__ method description in class docstring."""
-        self.fh = open(*args, **kwargs)
+        if from_file_like:
+            self.fh = path
+        else:
+            self.fh = open(path, *args, **kwargs)  # noqa: SIM115
         if hybformat_id is None:
             self.hybformat_id = self.settings['hybformat_id']
         else:
@@ -1760,22 +2028,22 @@ def __init__(self,
             self.hybformat_ref = hybformat_ref
 
     # HybFile : Public Methods : Initialization / Closing
-    def __enter__(self, *args, **kwargs):
+    def __enter__(self, *args: Any, **kwargs: Any) -> Self: # noqa: ANN401
         """Open "with" syntax."""
         return self
 
     # HybFile : Public Methods : Initialization / Closing
-    def __exit__(self, type, value, traceback):
+    def __exit__(self, etype, value, traceback) -> None:  # noqa: ANN001
         """Close "with" syntax."""
         self.close()
 
     # HybFile : Public Methods : Initialization / Closing
-    def __iter__(self):
+    def __iter__(self) -> Self:
         """Return an iterator."""
         return self
 
     # HybFile : Public Methods : Reading
-    def __next__(self):
+    def __next__(self) -> Self:
         """Return next line as HybRecord object."""
         next_line = self.fh.__next__()
         return HybRecord.from_line(
@@ -1785,25 +2053,25 @@ def __next__(self):
         )
 
     # HybFile : Public Methods : Reading
-    def close(self):
+    def close(self) -> None:
         """Close the file."""
         self.fh.close()
 
     # HybFile : Public Methods : Reading
-    def read_record(self):
+    def read_record(self) -> str:
         """Return next line of hyb file as HybRecord object."""
         return next(self)
 
     # HybFile : Public Methods : Reading
-    def read_records(self):
+    def read_records(self) -> List[str]:
         """Return list of all (remaining) records in hyb file as HybRecord objects."""
         records = []
         for record in self:
-            records.append(record)
+            records.append(record)  # noqa: PERF402
         return records
 
     # HybFile : Public Methods : Writing
-    def write_record(self, write_record):
+    def write_record(self, write_record: HybRecord) -> None:
         """
         Write a HybRecord object to file as a Hyb-format string.
 
@@ -1813,12 +2081,12 @@ def write_record(self, write_record):
         Args:
             write_record (HybRecord): Record to write.
         """
-        self._ensure_HybRecord(write_record)
+        self._ensure_hybrecord(write_record)
         record_string = write_record.to_line(newline=True)
         self.fh.write(record_string)
 
     # HybFile : Public Methods : Writing
-    def write_records(self, write_records):
+    def write_records(self, write_records: Iterable[HybRecord]) -> None:
         """
         Write a sequence of HybRecord objects as hyb-format lines to the Hyb file.
 
@@ -1832,12 +2100,12 @@ def write_records(self, write_records):
             self.write_record(write_record)
 
     # HybFile : Public Methods : Writing
-    def write_fh(self, *args, **kwargs):
+    def write_fh(self:Self, *args, **kwargs) -> None:  # noqa: ANN002 ANN003
         """Write directly to the underlying file handle."""
         self.fh.write(*args, **kwargs)
 
     # HybFile : Public Methods : Writing
-    def write(self, *args, **kwargs):
+    def write(self, *_args, **_kwargs) -> None:  # noqa: ANN002 ANN003
         """
         Implement no-op / error for "write" method to catch errors.
 
@@ -1850,19 +2118,67 @@ def write(self, *args, **kwargs):
     # Start HybFile Public Classmethods
     # HybFile : Public Classmethods : Initialization
     @classmethod
-    def open(cls, *args, **kwargs):
-        """Return a new HybFile object."""
-        return cls(*args, **kwargs)
+    def open(
+            cls,
+            path: str,
+            *args: Any,  # noqa: ANN401
+            hybformat_id: Optional[bool] = None,
+            hybformat_ref: Optional[bool] = None,
+            **kwargs: Any,  # noqa: ANN401
+            ) -> Self:
+        """
+        Open a path to a text file using :func:`open` and return a HybFile object.
+
+        Arguments match those of the Python3 built-in :func:`open` function and are
+        passed directly to it.
+
+        This method is provided as a convenience function for drop-in replacement of the
+        built-in :func:`open` function.
+
+        Specific keyword arguments are provided for HybFile-specific settings:
+
+        Args:
+            path (str): Path to file to open.
+            hybformat_id (:obj:`bool`, optional): If ``True``, during parsing of lines read count
+                information from identifier in ``<read_number>_<read_count>`` format.
+                Defaults to value in :attr:`settings['hybformat_id'] <HybFile.settings>`.
+            hybformat_ref (:obj:`bool`, optional): If ``True``, during parsing of lines read
+                additional record information from
+                identifier in ``<gene_id>_<transcript_id>_<gene_name>_<seg_type>`` format.
+                Defaults to value in :attr:`settings['hybformat_ref'] <HybFile.settings>`.
+
+        Example usage:
+            ::
+
+                with HybFile.open('path/to/file.hyb', 'r') as hyb_file:
+                    for record in hyb_file:
+                        print(record)
+
+        Args:
+            *args: Passed directly to :func:`open`.
+            **kwargs: Passed directly to :func:`open`.
+
+        Returns:
+            :class:`HybFile` object.
+        """
+        return cls(
+            path,
+            *args,
+            hybformat_id=hybformat_id,
+            hybformat_ref=hybformat_ref,
+            from_file_like=False,
+            **kwargs,
+        )
 
     # HybFile : Private Methods
     # Check if provided argument ("record") is an instance of HybRecord.
-    def _ensure_HybRecord(self, record):
+    def _ensure_hybrecord(self, record: HybRecord) -> None:
         if not isinstance(record, HybRecord):
-            _print_and_error('Item: "%s" is not a HybRecord object.' % record)
+            raise HybkitMiscError('Item: "%s" is not a HybRecord object.' % record)
 
 
 # ----- Begin FoldRecord Class -----
-class FoldRecord(object):
+class FoldRecord:
     r"""
     Class for storing secondary structure (folding) information for a nucleotide sequence.
 
@@ -1977,12 +2293,21 @@ class FoldRecord(object):
     #: Class-level settings. See :obj:`hybkit.settings.FoldRecord_settings_info` for descriptions.
     settings = hybkit.settings.FoldRecord_settings
 
-    _seq_type_choices = set(hybkit.settings.FoldRecord_settings_info['seq_type'][4]['choices'])
-    _error_mode_choices = set(hybkit.settings.FoldRecord_settings_info['error_mode'][4]['choices'])
+    _seq_type_choices = frozenset(
+        hybkit.settings.FoldRecord_settings_info['seq_type'][4]['choices'])
+    _error_mode_choices = frozenset(
+        hybkit.settings.FoldRecord_settings_info['error_mode'][4]['choices'])
 
     # Start FoldRecord Public Methods
     # FoldRecord : Public Methods : Initialization
-    def __init__(self, id, seq, fold, energy=None, seq_type=None):
+    def __init__(
+            self,
+            id: str,
+            seq: str,
+            fold: str,
+            energy: StrOrNum = None,
+            seq_type: Optional[FoldSeqArg] = None
+            ) -> None:
         """Describe __init__ method description in class docstring."""
         # Sequence Identifier (often seg1name-seg2name)
         self.id = self._ensure_attr_types(id, 'id')
@@ -1997,14 +2322,14 @@ def __init__(self, id, seq, fold, energy=None, seq_type=None):
             if seq_type.lower() in self._seq_type_choices:
                 self.seq_type = seq_type.lower()
             else:
-                message = 'seq_type must be one of: {}\n'.format(self._seq_type_choices)
-                message += 'Provided: {}'.format(seq_type)
-                _print_and_error(message)
+                message = (f'seq_type must be one of: {self._seq_type_choices}\n'
+                           f'Provided: {seq_type}')
+                raise HybkitConstructorError(message)
         else:
             self.seq_type = self.settings['seq_type']
 
     # FoldRecord : Public Methods : Parsing : Vienna
-    def to_vienna_lines(self, newline=True):
+    def to_vienna_lines(self, newline: bool = True) -> List[str]:
         """
         Return a list of lines for the record in vienna format.
 
@@ -2029,17 +2354,17 @@ def to_vienna_lines(self, newline=True):
             energy_str = '.'
         elif isinstance(self.energy, str):
             energy_str = self.energy
-        elif abs(self.energy - round(self.energy)) > 0.00001:
-            energy_str = ("%.5f" % self.energy).rstrip('0')
+        elif abs(self.energy - round(self.energy)) > 0.00001:  # noqa: PLR2004
+            energy_str = ('%.5f' % self.energy).rstrip('0')
         else:
-            energy_str = "%i" % int(round(self.energy))
+            energy_str = '%i' % int(round(self.energy))
 
-        line_3 = '%s\t(%s)' % (self.fold, energy_str)
+        line_3 = f'{self.fold}\t({energy_str})'
         ret_lines.append(line_3 + suffix)    # Add line 3, fold representation and energy
         return ret_lines
 
     # FoldRecord : Public Methods : Parsing : Vienna
-    def to_vienna_string(self, newline=True):
+    def to_vienna_string(self, newline: bool = True) -> str:
         """
         Return a 3-line string for the record in vienna format.
 
@@ -2057,7 +2382,7 @@ def to_vienna_string(self, newline=True):
         return ('\n'.join(self.to_vienna_lines(newline=False)) + suffix)
 
     # FoldRecord : Public Methods : HybRecord Comparison
-    def count_hyb_record_mismatches(self, hyb_record):
+    def count_hyb_record_mismatches(self, hyb_record: HybRecord) -> int:
         """
         Count mismatches between ``hyb_record.seq`` and ``fold_record.seq``.
 
@@ -2071,9 +2396,13 @@ def count_hyb_record_mismatches(self, hyb_record):
             return self.static_count_hyb_record_mismatches(hyb_record)
         elif self.seq_type == 'dynamic':
             return self.dynamic_count_hyb_record_mismatches(hyb_record)
+        else:
+            message = 'seq_type must be one of: %s\n' % str(self._seq_type_choices)
+            message += 'Provided: %s' % str(self.seq_type)
+            raise HybkitArgError(message)
 
     # FoldRecord : Public Methods : HybRecord Comparison
-    def static_count_hyb_record_mismatches(self, hyb_record):
+    def static_count_hyb_record_mismatches(self, hyb_record: HybRecord) -> int:
         """
         Count mismatches between ``hyb_record.seq`` and ``fold_record.seq``.
 
@@ -2090,7 +2419,7 @@ def static_count_hyb_record_mismatches(self, hyb_record):
             return mismatches
 
     # FoldRecord : Public Methods : HybRecord Comparison
-    def dynamic_count_hyb_record_mismatches(self, hyb_record):
+    def dynamic_count_hyb_record_mismatches(self, hyb_record: HybRecord) -> int:
         """
         Count mismatches between hyb_record.seq and dynamic fold_record.seq.
 
@@ -2108,7 +2437,11 @@ def dynamic_count_hyb_record_mismatches(self, hyb_record):
             return mismatches
 
     # FoldRecord : Public Methods : HybFile Comparison
-    def matches_hyb_record(self, hyb_record, allowed_mismatches=None):
+    def matches_hyb_record(
+            self,
+            hyb_record: HybRecord,
+            allowed_mismatches: Optional[int] = None
+            ) -> bool:
         """
         Return ``True`` if self.seq and hyb_record.seq mismatches are <= allowed_mismatches.
 
@@ -2124,7 +2457,11 @@ def matches_hyb_record(self, hyb_record, allowed_mismatches=None):
         return (mismatches <= allowed_mismatches)
 
     # FoldRecord : Public Methods : HybFile Comparison
-    def ensure_matches_hyb_record(self, hyb_record, allowed_mismatches=None):
+    def ensure_matches_hyb_record(
+            self,
+            hyb_record: HybRecord,
+            allowed_mismatches: Optional[int] = None
+            ) -> None:
         """
         Ensure self.seq matches hyb_record.seq, else raise an error.
 
@@ -2157,31 +2494,31 @@ def ensure_matches_hyb_record(self, hyb_record, allowed_mismatches=None):
                 message += '(%i of %i)\n' % (mismatch_count,
                                              self.settings['allowed_mismatches'])
                 message += 'dynamic FoldRecord Seq: %s\t(%i)\n' % (self.seq, len(self.seq))
-            _print_and_error(message)
+            raise HybkitMiscError(message)
 
     # Start FoldRecord Magic Methods
     # FoldRecord : Public MagicMethods : Comparison
-    def __eq__(self, other):
+    def __eq__(self, other: Self) -> bool:
         """Return ``True`` if two records have matching sequences and identifiers."""
         return (self.id == other.id and self.seq == other.seq)
 
     # FoldRecord : Public MagicMethods : Evaluation
-    def __hash__(self):
+    def __hash__(self) -> int:
         """Return a hash of the record ".id" attribute."""
         return hash(self.id)
 
     # FoldRecord : Public MagicMethods : Evaluation
-    def __bool__(self):
+    def __bool__(self) -> Literal[True]:
         """Return ``True`` wherever the class is defined."""
         return True
 
     # FoldRecord : Public MagicMethods : Evaluation
-    def __len__(self):
+    def __len__(self) -> int:
         """Return the length of the genomic sequence."""
         return len(self.seq)
 
     # FoldRecord : Public MagicMethods : Printing
-    def __str__(self):
+    def __str__(self) -> str:
         """Print the identifier of the record."""
         return '<FoldRecord ID: %s>' % self.id
 
@@ -2210,7 +2547,12 @@ def __str__(self):
 
     # FoldRecord : Public Classmethods : Construction : Vienna
     @classmethod
-    def from_vienna_lines(cls, record_lines, error_mode=None, seq_type=None):
+    def from_vienna_lines(
+            cls: Self,
+            record_lines: List[str],
+            error_mode: Optional[ErrorModeArg] = None,
+            seq_type: Optional[FoldSeqArg] = None,
+            ) -> FoldReturn:
         """
         Construct instance from a list of 3 strings of vienna-format ([ViennaFormat]_) lines.
 
@@ -2227,20 +2569,20 @@ def from_vienna_lines(cls, record_lines, error_mode=None, seq_type=None):
         elif error_mode not in cls._error_mode_choices:
             message = 'Provided error mode: %s is not in allowed options\n' % error_mode
             message += '    ' + ', '.join(cls._error_mode_choices)
-            _print_and_error(message)
+            raise HybkitArgError(message)
 
         fail_ret_val = (None, ''.join(record_lines))
 
-        if len(record_lines) != 3:
+        if len(record_lines) != 3:  # noqa: PLR2004
             error = 'Provided Vienna Record Lines:\n'
             error += '\n'.join([line.rstrip() for line in record_lines])
             error += '\n  ... are not in required 3-line format.'
             if 'return' in error_mode:
                 if 'warn' in error_mode:
-                    print('WARNING: ' + error)
+                    logging.warning(error)
                 return fail_ret_val
             else:
-                _print_and_error('ERROR: ' + error)
+                raise HybkitConstructorError('ERROR: ' + error)
 
         rec_id = record_lines[0].strip().lstrip('>')
         seq = record_lines[1].strip()
@@ -2248,26 +2590,26 @@ def from_vienna_lines(cls, record_lines, error_mode=None, seq_type=None):
         line_3_split = line_3.split('\t')
 
         # If no fold was created, potentially due to low-complexity sequence
-        if "(99" in line_3:
+        if '(99' in line_3:
             error = 'Improper Vienna: No Fold (Energy = 99*.*)'
             if 'return' in error_mode:
                 if 'warn' in error_mode:
-                    print('WARNING: ' + error)
+                    logging.warning(error)
                 return ('NOFOLD', ''.join(record_lines))
             else:
-                _print_and_error('ERROR: ' + error)
+                raise HybkitConstructorError('ERROR: ' + error)
 
-        if len(line_3_split) != 2 or not line_3_split[1].strip('()'):
+        if len(line_3_split) != 2 or not line_3_split[1].strip('()'):  # noqa: PLR2004
             error = 'Provided Vienna Record Line 3:\n'
             error += line_3.rstrip() + '\n'
             error += str(line_3_split) + '\n'
             error += '\n  ... does not have required "..((.))..<tab>(-1.23)" format.'
             if 'return' in error_mode:
                 if 'warn' in error_mode:
-                    print('WARNING: ' + error)
+                    logging.warning(error)
                 return ('NOENERGY', ''.join(record_lines))
             else:
-                _print_and_error('ERROR: ' + error)
+                raise HybkitConstructorError('ERROR: ' + error)
 
         fold = line_3_split[0]
         energy = line_3_split[1].strip('()')
@@ -2280,7 +2622,12 @@ def from_vienna_lines(cls, record_lines, error_mode=None, seq_type=None):
 
     # FoldRecord : Public Classmethods : Construction : Vienna
     @classmethod
-    def from_vienna_string(cls, record_string, error_mode=None, seq_type=None):
+    def from_vienna_string(
+            cls,
+            record_string: str,
+            error_mode: Optional[ErrorModeArg] = None,
+            seq_type: Optional[FoldSeqArg] = None,
+            ) -> FoldReturn:
         """
         Construct instance from a string representing 3 vienna-format ([ViennaFormat]_) lines.
 
@@ -2300,7 +2647,12 @@ def from_vienna_string(cls, record_string, error_mode=None, seq_type=None):
 
     # FoldRecord : Public Classmethods : Construction : Ct
     @classmethod
-    def from_ct_lines(cls, record_lines, error_mode=None, seq_type=None):
+    def from_ct_lines(
+            cls,
+            record_lines: List[str],
+            error_mode: Optional[ErrorModeArg] = None,
+            seq_type: Optional[FoldSeqArg] = None,
+            ) -> FoldReturn:
         """
         Create a FoldRecord from a list of record lines in ".ct" format ([CTFormat]_).
 
@@ -2319,14 +2671,14 @@ def from_ct_lines(cls, record_lines, error_mode=None, seq_type=None):
         elif error_mode not in cls._error_mode_choices:
             message = 'Provided error mode: %s is not in allowed options\n' % error_mode
             message += '    ' + ', '.join(cls._error_mode_choices)
-            _print_and_error(message)
+            raise HybkitArgError(message)
 
         header_line = record_lines[0].strip()
-        if not any((x in header_line for x in ['dG', 'Energy', 'ENERGY'])):
+        if not any(x in header_line for x in ['dG', 'Energy', 'ENERGY']):
             message = 'Provided ct Record Lines:\n'
             message += '\n'.join([line.rstrip() for line in record_lines])
             message += '\n  ... do not begin with expected header.'
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
 
         header_items = header_line.split('\t')
         expected_seq_len = int(header_items[0])
@@ -2337,22 +2689,22 @@ def from_ct_lines(cls, record_lines, error_mode=None, seq_type=None):
             message += '\n'.join([line.rstrip() for line in record_lines])
             message += '\n  ... do not match the expected %i ' % expected_seq_len
             message += 'lines from the header.'
-            _print_and_error(message)
+            raise HybkitConstructorError(message)
 
         energy_string = header_items[1]
-        check_energies = ["=99%i." % i for i in range(10)]
+        check_energies = ['=99%i.' % i for i in range(10)]
         if any(e in energy_string for e in check_energies):
             message = 'Improper CT: No Fold (Energy = 99*.*)'
             if 'return' in error_mode:
                 if 'warn' in error_mode:
-                    print('WARNING:', message)
+                    logging.warning(message)
                 return ('NOFOLD', ''.join(record_lines))
             else:
-                _print_and_error('ERROR: ' + message)
+                raise HybkitConstructorError('ERROR: ' + message)
 
         energy = float(energy_string.split()[-1])
-        enthalpy_string = header_items[2]
-        enthalpy = float(enthalpy_string.split()[-1])
+        # enthalpy_string = header_items[2]
+        # enthalpy = float(enthalpy_string.split()[-1])
         full_name = header_items[3]
 
         seq = ''
@@ -2363,7 +2715,7 @@ def from_ct_lines(cls, record_lines, error_mode=None, seq_type=None):
                 message = 'Provided ct Record Line:\n'
                 message += line.rstrip() + '\n'
                 message += '\n  ... does not have 6-column or 8-column format'
-                _print_and_error(message)
+                raise HybkitConstructorError(message)
 
             base = line_split[1]
             seq += base
@@ -2375,17 +2727,17 @@ def from_ct_lines(cls, record_lines, error_mode=None, seq_type=None):
             elif fold_i < i:
                 fold_char = ')'
             else:
-                raise RuntimeError()
+                raise RuntimeError
             fold += fold_char
 
         if not len(fold):
             message = 'Improper CT: No Fold (Len = 0)'
             if 'return' in error_mode:
                 if 'warn' in error_mode:
-                    print('WARNING:', message)
+                    logging.warning(message)
                 return ('NOFOLD', ''.join(record_lines))
             else:
-                _print_and_error('ERROR: ' + message)
+                raise HybkitConstructorError('ERROR: ' + message)
 
         return_obj = cls(full_name, seq, fold, energy, seq_type=seq_type)
         return return_obj
@@ -2395,7 +2747,12 @@ def from_ct_lines(cls, record_lines, error_mode=None, seq_type=None):
 
     # FoldRecord : Public Classmethods : Construction : Ct
     @classmethod
-    def from_ct_string(cls, record_string, error_mode=None, seq_type=None):
+    def from_ct_string(
+            cls,
+            record_string: str,
+            error_mode: Optional[ErrorModeArg] = None,
+            seq_type: Optional[FoldSeqArg] = None,
+            ) -> FoldReturn:
         """
         Create a FoldRecord entry from a multi-line string from ".ct" format ([CTFormat]_).
 
@@ -2417,7 +2774,11 @@ def from_ct_string(cls, record_string, error_mode=None, seq_type=None):
 
     # FoldRecord : Private Methods : Record Parsing
     # Ensure attributes passed to constructor are valid for the respective FoldRecord attributes
-    def _ensure_attr_types(self, value, attribute):
+    def _ensure_attr_types(
+            self,
+            value: Any,  # noqa: ANN401
+            attribute: Any,  # noqa: ANN401
+            ) -> Any:  # noqa: ANN401
         err_message = None
         # Value is checked for placeholder value: '.' and converted to None if found.
         if value == '.':
@@ -2426,15 +2787,15 @@ def _ensure_attr_types(self, value, attribute):
             if not isinstance(value, str):
                 err_message = (
                     'id must be a string. Provided id is type '
-                    '%s: %s' % (type(value), value)
+                    f'{type(value)}: {value}'
                 )
             elif not value.strip():
                 err_message = ('id must be a non-empty string. '
-                               'Provided id: "%s" is an empty string' % value)
+                               f'Provided id: "{value}" is an empty string')
         elif attribute == 'seq':
             if not isinstance(value, str):
-                err_message = ('seq must be a string. Provided seq is type %s: %s' %
-                               (type(value), value))
+                err_message = ('seq must be a string. Provided seq is type '
+                               f'{type(value)}: {value}')
             elif not value.strip():
                 err_message = ('seq must be a non-empty string. Provided seq: "%s" is an empty '
                                'string' % value)
@@ -2443,27 +2804,27 @@ def _ensure_attr_types(self, value, attribute):
         elif attribute == 'energy':
             if not isinstance(value, (float, int, type(None), str)):
                 err_message = ('energy must be a float, int, numeric str, or None. '
-                               'Provided energy: "%s" is type %s' % (value, type(value)))
+                               f'Provided energy: "{value}" is type {type(value)}')
             if isinstance(value, str):
                 if not value.strip():
                     err_message = ('energy must be a float, int, numeric str, or None. '
-                                   'Provided energy is empty string: "%s"' % value)
+                                   f'Provided energy is empty string: "{value}"')
                 elif '_' in value:
                     err_message = ('energy must be a float, int, numeric str, or None. '
-                                   'Provided energy is non-numeric string: "%s"' % value)
+                                   f'Provided energy is non-numeric string: "{value}"')
                 else:
                     try:
                         float(value)
                     except ValueError:
                         err_message = ('energy must be a float, int, numeric str, or None. '
-                                       'Provided energy is non-numeric string: "%s"' % value)
+                                       f'Provided energy is non-numeric string: "{value}"')
             # Store energy value as a string
             elif isinstance(value, (float, int)):
-                value = "%.1f" % value
+                value = '%.1f' % value
         elif attribute == 'fold':
             if not isinstance(value, str):
-                err_message = ('fold must be a string. Provided fold is type %s: %s' %
-                               (type(value), value))
+                err_message = ('fold must be a string. Provided fold is type '
+                               f'{type(value)}: {value}')
             elif not value.strip():
                 err_message = ('fold must be a non-empty string. Provided fold: "%s" is an empty '
                                'string' % value)
@@ -2471,7 +2832,8 @@ def _ensure_attr_types(self, value, attribute):
                 err_message = ('fold must be a string of characters in "().-". '
                                'Provided fold: "%s" contains invalid characters' % value)
         if err_message is not None:
-            _print_and_error(err_message)
+            raise HybkitConstructorError(err_message)
+            return None
         else:
             return value
 
@@ -2492,19 +2854,35 @@ def _ensure_attr_types(self, value, attribute):
 
     # Start FoldRecord Private Methods
     # FoldRecord : Private Methods : Segment Parsing
-    def _get_seg_fold(self, seg_props, hyb_record=None):
+    def _get_seg_fold(
+            self,
+            seg_props: SegProps,
+            hyb_record: Optional[HybRecord] = None,
+            ) -> str:
         if self.seq_type == 'static':
             return self._static_get_seg_fold(seg_props, hyb_record)
         elif self.seq_type == 'dynamic':
             return self._dynamic_get_seg_fold(seg_props, hyb_record)
+        else:
+            message = 'seq_type must be one of: %s\n' % str(self._seq_type_choices)
+            message += 'Provided: %s' % str(self.seq_type)
+            raise HybkitArgError(message)
 
     # FoldRecord : Private Methods : Segment Parsing
-    def _static_get_seg_fold(self, seg_props, hyb_record=None):
+    def _static_get_seg_fold(
+            self,
+            seg_props: SegProps,
+            hyb_record: Optional[HybRecord] = None
+            ) -> str:
         seg_start, seg_end = seg_props['read_start'], seg_props['read_end']
         return self.fold[(seg_start - 1):seg_end]
 
     # FoldRecord : Private Methods : Segment Parsing
-    def _dynamic_get_seg_fold(self, seg_props, hyb_record):
+    def _dynamic_get_seg_fold(
+            self,
+            seg_props: SegProps,
+            hyb_record: Optional[HybRecord] = None,
+            ) -> str:
         hyb_record._ensure_props_read_start_end()
         seg_start, seg_end = seg_props['read_start'], seg_props['read_end']
         seg1_start = hyb_record.seg1_props['read_start']
@@ -2514,9 +2892,6 @@ def _dynamic_get_seg_fold(self, seg_props, hyb_record):
         seg2_end = hyb_record.seg2_props['read_end']
         seg1_fold = self.fold[0:seg1_len]
         seg2_fold = self.fold[seg1_len:]
-        # print(seg1_fold, seg2_fold)
-        # print(self.fold, self.seq)
-        # print(len(seg1_fold + seg2_fold), len(self.seq), len(hyb_record._get_dynamic_seq()))
         assert len(seg1_fold + seg2_fold) == len(self.seq) == len(hyb_record._get_dynamic_seq())
         assert len(seg1_fold) == len(hyb_record._get_seg_seq(hyb_record.seg1_props))
         if (seg_start, seg_end) == (seg1_start, seg1_end):
@@ -2524,10 +2899,10 @@ def _dynamic_get_seg_fold(self, seg_props, hyb_record):
         elif (seg_start, seg_end) == (seg2_start, seg2_end):
             return seg2_fold
         else:
-            raise RuntimeError()
+            raise RuntimeError
 
     # FoldRecord : Private Methods : Seq Comparison
-    def _get_seq_mismatch_string(seq1, seq2):
+    def _get_seq_mismatch_string(self, seq1: str, seq2: str) -> str:
         match_str = ''
         for i in range(max([len(seq1), len(seq2)])):
             if seq1[i:(i + 1)] == seq2[i:(i + 1)]:
@@ -2548,12 +2923,15 @@ def _get_seq_mismatch_string(seq1, seq2):
             (if not provided, uses
             :attr:`FoldRecord.settings['seq_type'] <FoldRecord.settings>`).
         error_mode (:obj:`str`, optional): String representing the error mode.
-                If None, defaults to the value set in
-                :attr:`settings['error_mode'] <FoldRecord.settings>`.
-                Options:
-                "raise": Raise an error when encountered and exit program;
-                "warn_return": Print a warning and return the error_value;
-                "return": Return the error value with no warnings.
+            If None, defaults to the value set in
+            :attr:`settings['error_mode'] <FoldRecord.settings>`.
+            Options:
+            "raise": Raise an error when encountered and exit program;
+            "warn_return": Print a warning and return the error_value;
+            "return": Return the error value with no warnings.
+        from_file_like (:obj:`bool`, optional): If True, treat the first argument
+            as a file-like object (such as io.StringIO or gzip.GzipFile) and the
+            remaining positional arguments are ignored (Default ``False``).
         *args: Passed to :func:`open()`.
         **kwargs: Passed to :func:`open()`.
 
@@ -2570,7 +2948,7 @@ def _get_seq_mismatch_string(seq1, seq2):
 )
 
 
-class FoldFile(object):
+class FoldFile:
     """
     #Base class for file-object wrappers that return file lines as FoldRecord objects.
 
@@ -2583,14 +2961,24 @@ class FoldFile(object):
     settings = hybkit.settings.FoldFile_settings
 
     _foldrecord_settings_info = hybkit.settings.FoldRecord_settings_info
-    _foldrecord_seq_type_choices = set(_foldrecord_settings_info['seq_type'][4]['choices'])
-    _error_mode_choices = set(_foldrecord_settings_info['error_mode'][4]['choices'])
+    _foldrecord_seq_type_choices = frozenset(_foldrecord_settings_info['seq_type'][4]['choices'])
+    _error_mode_choices = frozenset(_foldrecord_settings_info['error_mode'][4]['choices'])
 
     # Start FoldFile Public Methods
     # FoldFile : Public Methods : Initialization / Closing
-    def __init__(self, *args, seq_type=None, error_mode=None, **kwargs):
+    def __init__(
+            self,
+            *args: Any,  # noqa: ANN401
+            seq_type: Optional[FoldSeqArg] = None,
+            error_mode: Optional[ErrorModeArg] = None,
+            from_file_like: bool = False,
+            **kwargs: Any,  # noqa: ANN401
+            ) -> None:
         """Wrap for open() function that stores resulting file."""
-        self.fh = open(*args, **kwargs)
+        if from_file_like:
+            self.fh = args[0]
+        else:
+            self.fh = open(*args, **kwargs)  # noqa: SIM115
 
         # Set foldrecord_type
         if seq_type is None:
@@ -2598,9 +2986,9 @@ def __init__(self, *args, seq_type=None, error_mode=None, **kwargs):
         elif seq_type in self._foldrecord_seq_type_choices:
             self.foldrecord_seq_type = seq_type
         else:
-            message = 'Invalid foldrecord_type: %s. Allowed values: %s' % (
+            message = 'Invalid foldrecord_type: {}. Allowed values: {}'.format(
                 seq_type, self._foldrecord_seq_type_choices)
-            _print_and_error(message)
+            raise HybkitArgError(message)
 
         # Set error_mode
         if error_mode is None:
@@ -2608,33 +2996,33 @@ def __init__(self, *args, seq_type=None, error_mode=None, **kwargs):
         elif error_mode in self._error_mode_choices:
             self.error_mode = error_mode
         else:
-            message = 'Invalid error_mode: %s. Allowed values: %s' % (
-                error_mode, self._error_mode_choices)
-            _print_and_error(message)
+            message = (f'Invalid error_mode: {error_mode}. '
+                       f'Allowed values: {self._error_mode_choices!s}')
+            raise HybkitArgError(message)
         self._post_init_tasks()  # Throws error on base class
 
     # FoldFile : Public Methods : Initialization / Closing
-    def __enter__(self, *args, **kwargs):
+    def __enter__(self, *args: Any, **kwargs: Any) -> Self:  # noqa: ANN401
         """Open "with" syntax."""
         return self
 
     # FoldFile : Public Methods : Initialization / Closing
-    def __exit__(self, type, value, traceback):
+    def __exit__(self, etype: Any, value: Any, traceback: Any) -> None:  # noqa: ANN401
         """Close "with" syntax."""
         self.close()
 
     # FoldFile : Public Methods : Initialization / Closing
-    def __iter__(self):
+    def __iter__(self) -> Self:
         """Return an iterator."""
         return self
 
     # FoldFile : Public Methods : Reading
-    def __next__(self):
+    def __next__(self) -> FoldRecord:
         """Return :class:`FoldRecord` via :meth:`read_record` for this file type."""
         return self.read_record()
 
     # FoldFile : Public Methods : Reading
-    def close(self):
+    def close(self) -> None:
         """Close the file handle."""
         self.fh.close()
 
@@ -2643,15 +3031,15 @@ def close(self):
     read_record = None
 
     # FoldFile : Public Methods : Reading
-    def read_records(self):
+    def read_records(self) -> List[FoldRecord]:
         """Return list of all :class:`FoldRecord` objects for this file type."""
         records = []
         for record in self:
-            records.append(record)
+            records.append(record)  # noqa: PERF402
         return records
 
     # FoldFile : Public Methods : Writing
-    def write_record(self, write_record):
+    def write_record(self, write_record: FoldRecord) -> None:
         """
         Write a FoldRecord object for this file type.
 
@@ -2661,12 +3049,12 @@ def write_record(self, write_record):
         Args:
             write_record (:class:`FoldRecord`): :class:`FoldRecord` objects to write.
         """
-        self._ensure_FoldRecord(write_record)
+        self._ensure_foldrecord(write_record)
         record_string = self._to_record_string(write_record, newline=True)
         self.fh.write(record_string)
 
     # FoldFile : Public Methods : Writing
-    def write_records(self, write_records):
+    def write_records(self, write_records: Iterable[FoldRecord]) -> None:
         """
         Write a sequence of FoldRecord objects for this file type.
 
@@ -2680,35 +3068,64 @@ def write_records(self, write_records):
             self.write_record(write_record)
 
     # FoldFile : Public Methods : Writing
-    def write_fh(self, *args, **kwargs):
+    def write_fh(self, *args: Any, **kwargs: Any) -> None:  # noqa: ANN401
         """Write directly to the underlying file handle."""
         self.fh.write(*args, **kwargs)
 
     # FoldFile : Public Classmethods : Initialization
     @classmethod
-    def open(cls, *args, **kwargs):
-        """Return a new FoldFile object."""
-        return cls(*args, **kwargs)
+    def open(
+        cls,
+        path: str,
+        *args: Any,  # noqa: ANN401
+        **kwargs: Any,  # noqa: ANN401
+        ) -> Self:
+        """
+        Open a path to a text file using :func:`open` and return relevant file object.
+
+        Arguments match those of the Python3 built-in :func:`open` function and are
+        passed directly to it.
+
+        This method is provided as a convenience function for drop-in replacement of the
+        built-in :func:`open` function.
+
+        Specific keyword arguments are provided for fold-file-specific settings:
+
+        Args:
+            path (str): Path to file to open.
+            seq_type (:obj:`str`, optional): Type of FoldRecord to return:
+                ``static``, or ``dynamic``
+                (if not provided, uses
+                :attr:`FoldRecord.settings['seq_type'] <FoldRecord.settings>`).
+            error_mode (:obj:`str`, optional): String representing the error mode.
+                If None, defaults to the value set in
+                :attr:`settings['error_mode'] <FoldRecord.settings>`.
+                Options:
+                "raise": Raise an error when encountered and exit program;
+                "warn_return": Print a warning and return the error_value;
+                "return": Return the error value with no warnings.
+            *args: Passed directly to :func:`open`.
+            **kwargs: Passed directly to :func:`open`.
+
+        Returns:
+            :class:`HybFile` object.
+        """
+        return cls(path, *args, **kwargs, from_file_like=False)
 
     # Start FoldFile Private Methods
     # FoldFile : Private Methods
-    def _post_init_tasks(self):
+    def _post_init_tasks(self) -> None:
         """Stub for subclassing. Raise error on base class."""
         message = 'FoldFile is a base class and is not meant to be used directly.'
-        _print_and_error(message, error_type=NotImplementedError)
+        raise NotImplementedError(message)
 
     # FoldFile : Private Methods
-    def _ensure_FoldRecord(self, record):
+    def _ensure_foldrecord(self, record: Self) -> None:
         if not isinstance(record, FoldRecord):
-            _print_and_error('Item: "%s" is not a FoldRecord object.' % record)
+            raise HybkitMiscError('Item: "%s" is not a FoldRecord object.' % record)
 
     # Stub method to be replaced by subclasses.
     _to_record_string = None
-    # def _to_record_string(self, write_record, newline):
-    #     """Stub method to be replaced by subclasses."""
-    #     message = 'FoldFile is a base class and is not meant to be used directly.'
-    #     print(message)
-    #     raise NotImplementedError(message)
 
 
 # Append common FoldFile Args and Attributes description to docstring:
@@ -2729,7 +3146,7 @@ class ViennaFile(FoldFile):
 
     # Start ViennaFile Methods
     # ViennaFile : Public Methods : Reading
-    def read_record(self, override_error_mode=None):
+    def read_record(self, override_error_mode: Optional[ErrorModeArg] = None) -> FoldReturn:
         """
         Read next three lines and return output as FoldRecord object.
 
@@ -2748,19 +3165,19 @@ def read_record(self, override_error_mode=None):
             use_error_mode = override_error_mode
         record = FoldRecord.from_vienna_lines(
             (line_1, line_2, line_3),
-            error_mode=override_error_mode,
+            error_mode=use_error_mode,
             seq_type=self.foldrecord_seq_type,
         )
 
         return record
 
     # ViennaFile : Private Methods
-    def _post_init_tasks(self):
+    def _post_init_tasks(self) -> None:
         """Hold place for any post-initialization tasks."""
         pass
 
     # ViennaFile : Private Methods
-    def _to_record_string(self, write_record, newline):
+    def _to_record_string(self, write_record: FoldRecord, newline: bool) -> str:
         """Return a :class:`Fold Record` as a Vienna-format string."""
         return write_record.to_vienna_string(newline=newline)
 
@@ -2784,7 +3201,7 @@ class CtFile(FoldFile):
 
     # Start CtFile Methods
     # CtFile : Public Methods
-    def read_record(self):
+    def read_record(self) -> FoldReturn:
         """
         Return the next CT record as a :class:`FoldRecord` object.
 
@@ -2795,7 +3212,7 @@ def read_record(self):
         header = next(self.fh)
         record_lines = [header]
         expected_line_num = int(header.strip().split()[0])
-        for i in range(expected_line_num):
+        for _ in range(expected_line_num):
             record_lines.append(next(self.fh))
         record_type = self._foldrecord_types[self.settings['foldrecord_type']]
         record = record_type.from_ct_lines(
@@ -2812,16 +3229,15 @@ def read_record(self):
     write_records = None
 
     # CtFile : Private Methods
-    def _post_init_tasks(self):
+    def _post_init_tasks(self) -> None:
         """Hold place for any post-initialization tasks."""
         pass
 
     # CtFile : Private Methods
-    def _to_record_string(self, write_record, newline):
+    def _to_record_string(self, write_record: FoldRecord, newline: bool) -> None:
         """(NOOP / Not-Implemented) Return a :class:`Fold Record` as a CT-format string."""
         message = 'No write_record is implemented for ct files, as the FoldRecord '
         message += 'object does not contain the complete set of ct record information.'
-        print(message)
         raise NotImplementedError(message)
 
 
@@ -2830,7 +3246,7 @@ def _to_record_string(self, write_record, newline):
 
 
 # ----- Begin HybFoldIter Class -----
-class HybFoldIter(object):
+class HybFoldIter:
     """
     Iterator for simultaneous iteration over a :class:`HybFile` and ``FoldFile`` object.
 
@@ -2858,25 +3274,31 @@ class HybFoldIter(object):
     #: Class-level settings. See :attr:`settings.HybFoldIter_settings_info` for descriptions.
     settings = hybkit.settings.HybFoldIter_settings
 
-    _iter_error_modes = set(hybkit.settings.HybFoldIter_settings_info
-                            ['iter_error_mode'][4]['choices'])
+    _iter_error_modes = frozenset(
+        hybkit.settings.HybFoldIter_settings_info['iter_error_mode'][4]['choices'])
 
     # Start HybFoldIter Methods
     # HybFoldIter : Public Methods
-    def __init__(self, hybfile_handle, foldfile_handle, combine=False, iter_error_mode=None):
+    def __init__(
+            self,
+            hybfile_handle: HybFile,
+            foldfile_handle: FoldFile,
+            combine: bool = False,
+            iter_error_mode: Optional[IterErrorModeArg] = None
+            ) -> None:
         """Please see :class:`HybFoldIter` for initialization information."""
         if not isinstance(hybfile_handle, HybFile):
             message = 'hybfile_handle must be an instance of a HybFile object.'
-            _print_and_error(message)
+            raise HybkitIterError(message)
         if not isinstance(foldfile_handle, FoldFile):
             message = 'foldfile_handle must be an instance of a FoldFile object.'
-            _print_and_error(message)
+            raise HybkitIterError(message)
         if iter_error_mode is None:
             self.iter_error_mode = self.settings['iter_error_mode']
         elif iter_error_mode not in self._iter_error_modes:
             message = 'iter_error_mode must be one of the following: '
             message += ', '.join(self._iter_error_modes)
-            _print_and_error(message)
+            raise HybkitIterError(message)
         else:
             self.iter_error_mode = iter_error_mode
         self.hybfile_handle = hybfile_handle
@@ -2888,7 +3310,7 @@ def __init__(self, hybfile_handle, foldfile_handle, combine=False, iter_error_mo
         self.last_fold_record = None
 
     # HybFoldIter : Public Methods
-    def report(self):
+    def report(self) -> List[str]:
         """Return a report of information from iteration."""
         ret_lines = ['HybFoldIter Iteration Report:']
         add_line = 'Combined Iteration Attempts: '
@@ -2907,18 +3329,24 @@ def report(self):
         return ret_lines
 
     # HybFoldIter : Public Methods
-    def print_report(self):
+    def print_report(self) -> None:
         """Print a report of information from iteration."""
         ret_lines = self.report()
         print('\n'.join(ret_lines) + '\n')
 
     # HybFoldIter : Public Methods
-    def __iter__(self):
+    def __iter__(self) -> Self:
         """Return an iterator object."""
         return self
 
     # HybFoldIter : Public Methods
-    def __next__(self):
+    def __next__(
+        self
+        ) -> Union[
+            HybRecord,
+            Tuple[HybRecord, FoldRecord],
+            Tuple[HybRecord, FoldRecord, str],
+            ]:
         """Read and return (:class:`HybRecord`, :class:`FoldRecord`)."""
         self.counters['total_read_attempts'] += 1
         next_hyb_record = None
@@ -2935,9 +3363,11 @@ def __next__(self):
             error = ''
             do_skip = False
             # Check for "NoFold" error
-            if 'foldrecord_nofold' in self.settings['error_checks']:
-                if isinstance(next_fold_record, tuple) and next_fold_record[0] == 'NOFOLD':
-                    error = 'Improper FoldRecord: No Fold (Energy = 99*.*)'
+            if ('foldrecord_nofold' in self.settings['error_checks']
+                    and isinstance(next_fold_record, tuple)
+                    and next_fold_record[0] == 'NOFOLD'
+                ):
+                error = 'Improper FoldRecord: No Fold (Energy = 99*.*)'
 
             # Check for "NoEnergy" error
             if (not error and isinstance(next_fold_record, tuple)
@@ -2945,9 +3375,11 @@ def __next__(self):
                 error = 'Improper FoldRecord: No Energy (no <Tab> in 3rd line)'
 
             # Check for "InDel" errors
-            if not error and 'hybrecord_indel' in self.settings['error_checks']:
-                if next_hyb_record.prop('has_indels'):
-                    error = 'HybRecord: %s has InDels.' % str(next_hyb_record)
+            if (not error
+                    and 'hybrecord_indel' in self.settings['error_checks']
+                    and next_hyb_record.prop('has_indels')
+                ):
+                error = 'HybRecord: %s has InDels.' % str(next_hyb_record)
 
             # Check for "Mismatch" errors
             if not error and 'max_mismatch' in self.settings['error_checks']:
@@ -2959,10 +3391,12 @@ def __next__(self):
                     error += '%i allowed ' % FoldRecord.settings['allowed_mismatches']
 
             # Check for "EnergyMismatch" errors
-            if not error and 'energy_mismatch' in self.settings['error_checks']:
-                if (next_fold_record.energy is not None
-                        and next_hyb_record.energy not in {None, '.'}
-                        and str(next_fold_record.energy) != str(next_hyb_record.energy)):
+            if (not error
+                    and 'energy_mismatch' in self.settings['error_checks']
+                    and next_fold_record.energy is not None
+                    and next_hyb_record.energy not in {None, '.'}
+                    and str(next_fold_record.energy) != str(next_hyb_record.energy)
+                ):
                     if not error:
                         error = 'HybRecord: %s ' % str(next_hyb_record)
                     error += 'has hyb-record / fold-record energy mismatch: '
@@ -2973,11 +3407,11 @@ def __next__(self):
             # If an error exists, deal with it depending on the value of iter_error_mode
             if error:
                 if iter_error_mode == 'raise':
-                    _print_and_error('ERROR: ' + error)
+                    raise HybkitIterError('ERROR: ' + error)
                 elif iter_error_mode == 'warn_skip':
-                    print('WARNING: SkipPair:', error)
+                    logging.warning(error)
                 elif iter_error_mode == 'warn_return':
-                    print('WARNING:', error)
+                    logging.warning(error)
 
                 if 'skip' in iter_error_mode:
                     self.sequential_skips += 1
@@ -2987,7 +3421,7 @@ def __next__(self):
                         message += 'record pairs in a row '
                         message += '(max: %i)\n' % self.settings['max_sequential_skips']
                         message += 'Check for misalignment of records, or disable setting.'
-                        _print_and_error(message)
+                        raise HybkitIterError(message)
                     do_skip = True
 
         except StopIteration:
@@ -3002,7 +3436,7 @@ def __next__(self):
             if next_fold_record is not None:
                 message += 'Next FoldRecord: %s\n' % str(next_fold_record)
             message += '\n' + '\n'.join(self.report()) + '\n'
-            print(message)
+            logging.warning(message)
             raise
 
         if do_skip:
@@ -3025,14 +3459,6 @@ def __next__(self):
         self.sequential_skips = 0
         return ret_obj
 
-
-# Utility functions.
-def _print_and_error(message, error_type=RuntimeError):
-    """Print message and raise error_type."""
-    print(message)
-    raise error_type(message)
-
-
 # Import the remainder of hybkit code to connect.
 import hybkit.analysis
 import hybkit.plot
diff --git a/hybkit/analysis.py b/hybkit/analysis.py
index 524cb28..aa367c5 100644
--- a/hybkit/analysis.py
+++ b/hybkit/analysis.py
@@ -5,19 +5,37 @@
 
 """Functions for analysis of HybRecord and FoldRecord objects."""
 
-import sys
 import copy
 from collections import Counter
-import hybkit
-import numpy as np
+from typing import Any, Dict, List, Literal, Optional, Union
 
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
+import numpy as np
 
+import hybkit
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+from hybkit.errors import HybkitArgError
+
+# ----- File-Specific Linting Directives:
+# ruff: noqa: F401 SLF001
+
+# ----- Begin Typing Directives -----
+AnalysisOptions = Literal['energy', 'type', 'mirna', 'target']
+AnalysisArg = Union[AnalysisOptions, List[AnalysisOptions]]
+QuantModeArg = Literal['single', 'reads', 'records']
 
 # --- Hybkit Analysis --- #
-class Analysis(object):
+class Analysis:
     """
     Class for analysis of hybkit HybRecord and FoldRecord objects.
 
@@ -203,48 +221,53 @@ class Analysis(object):
 
     # Class private variables:
     _result_keys = {
-        'energy': [
+        'energy': (
             'energy_analysis_count', 'has_energy_val', 'no_energy_val',
             'energy_min', 'energy_max', 'energy_mean', 'energy_std',
             'binned_energy_vals',
-        ],
-        'type': [
+        ),
+        'type': (
             'types_analysis_count', 'hybrid_types', 'reordered_hybrid_types',
             'mirna_hybrid_types', 'seg1_types', 'seg2_types', 'all_seg_types',
-        ],
-        'mirna': [
+        ),
+        'mirna': (
             'mirna_analysis_count', 'mirnas_5p', 'mirnas_3p', 'mirna_dimers',
             'non_mirna', 'has_mirna',
-        ],
-        'target': [
+        ),
+        'target': (
             'target_analysis_count', 'target_evals', 'target_names', 'target_types',
-        ],
-        'fold': [
+        ),
+        'fold': (
             'fold_analysis_count', 'mirna_nt_fold_counts', 'mirna_nt_fold_props',
             'fold_match_counts',
-        ],
+        ),
     }
-    _all_result_keys_list = []
+    _all_result_keys_list_temp = []  # noqa: RUF012
     for key in _result_keys:
-        _all_result_keys_list += _result_keys[key]
-    _all_result_keys_set = set(_all_result_keys_list)
+        _all_result_keys_list_temp += _result_keys[key]
 
-    _quant_mode_options = set(hybkit.settings.Analysis_settings_info['quant_mode'][4]['choices'])
+    _all_result_keys_list = tuple(_all_result_keys_list_temp)
+    _all_result_keys_set = frozenset(_all_result_keys_list_temp)
+    del _all_result_keys_list_temp
+
+    _quant_mode_options = frozenset(
+        hybkit.settings.Analysis_settings_info['quant_mode'][4]['choices']
+        )
 
     # ----- Begin Analysis Class -----
     # Start Analysis Public Methods
     # Analysis : Public Methods
-    def __init__(self,
-                 analysis_types=None,
-                 name=None,
-                 quant_mode=None,
-                 ):
+    def __init__(
+            self,
+            analysis_types: AnalysisArg,
+            name: Optional[str] = None,
+            quant_mode: Optional[QuantModeArg] = None,
+            ) -> None:
         """Describe in class docstring."""
         if analysis_types is None or not analysis_types:
             message = 'No analysis types provided. Analysis types must be provided'
             message += ' as a list of strings.\nOptions: %s' % ', '.join(self.analysis_options)
-            print(message)
-            raise RuntimeError(message)
+            raise HybkitArgError(message)
         if isinstance(analysis_types, str):
             analysis_types = [analysis_types]
         self.analysis_types = []
@@ -252,11 +275,10 @@ def __init__(self,
             if (not isinstance(analysis_type, str)
                     or analysis_type.lower() not in self.analysis_options):
                 message = (
-                    'Analysis type "%s" not recognized.'
-                    '\nChoices: %s' % (str(analysis_type), ', '.join(self.analysis_options))
+                    f'Analysis type "{analysis_type!s}" not recognized.'
+                    '\nChoices: {}'.format(', '.join(self.analysis_options))
                 )
-                print(message)
-                raise RuntimeError(message)
+                raise HybkitArgError(message)
             else:
                 self.analysis_types.append(analysis_type.lower())
         self.name = self._sanitize_name(name)
@@ -265,11 +287,10 @@ def __init__(self,
             self.quant_mode = self.settings['quant_mode']
         elif quant_mode not in self._quant_mode_options:
             message = (
-                'Quantification mode "%s" not recognized.'
-                '\nChoices: %s' % (str(quant_mode), ', '.join(self._quant_mode_options))
+                f'Quantification mode "{quant_mode!s}" not recognized.'
+                '\nChoices: {}'.format(', '.join(self._quant_mode_options))
             )
-            print(message)
-            raise RuntimeError(message)
+            raise HybkitArgError(message)
         else:
             self.quant_mode = quant_mode
 
@@ -277,7 +298,7 @@ def __init__(self,
             getattr(self, '_init_' + analysis_type)()
 
     # Analysis : Public Methods : Add HybRecord
-    def add_hyb_record(self, hyb_record):
+    def add_hyb_record(self, hyb_record: hybkit.HybRecord) -> None:
         """
         Add a HybRecord object to the analysis.
 
@@ -289,7 +310,12 @@ def add_hyb_record(self, hyb_record):
             getattr(self, '_add_' + analysis_type)(hyb_record)
 
     # Analysis : Public Methods : Add HybRecords
-    def add_hyb_records(self, hyb_records, eval_types=False, eval_mirna=False):
+    def add_hyb_records(
+            self,
+            hyb_records: List[hybkit.HybRecord],
+            eval_types: bool = False,
+            eval_mirna: bool = False
+            ) -> None:
         """
         Add a list of HybRecord objects to the analysis.
 
@@ -313,7 +339,7 @@ def add_hyb_records(self, hyb_records, eval_types=False, eval_mirna=False):
 
     # Start Results Methods
     # Analysis : Public Methods : Results : get_all_results
-    def get_all_results(self):
+    def get_all_results(self) -> dict:
         """
         Return a dictionary with all results for all active analyses.
 
@@ -329,7 +355,7 @@ def get_all_results(self):
         return results
 
     # Analysis : Public Methods : Results : get_analysis_result
-    def get_analysis_results(self, analysis):
+    def get_analysis_results(self, analysis: AnalysisOptions) -> Dict:
         """
         Return a dictionary with all results for a specific analysis.
 
@@ -346,7 +372,7 @@ def get_analysis_results(self, analysis):
         return getattr(self, '_get_' + analysis + '_results')()
 
     # Analysis : Public Methods : Results : get_specific_result
-    def get_specific_result(self, result_key):
+    def get_specific_result(self, result_key: str) -> Any:  # noqa: ANN401
         """
         Get a specific result from the analysis.
 
@@ -360,24 +386,27 @@ def get_specific_result(self, result_key):
         """
         if result_key not in self._all_result_keys_set:
             message = (
-                'Result key "%s" not recognized.'
-                '\nChoices: %s' % (result_key, ', '.join(self._all_result_keys_list))
+                f'Result key "{result_key!s}" not recognized.'
+                '\nChoices: {}'.format(', '.join(self._all_result_keys_list))
             )
-            print(message)
-            raise RuntimeError(message)
+            raise HybkitArgError(message)
         for analysis_type in self.analysis_options:
             if result_key in self._result_keys[analysis_type]:
                 if analysis_type not in self.analysis_types:
                     message = (
-                        'Result "%s" cannot be gotten because analysis type "%s" is not'
-                        'active.' % (result_key, analysis_type)
+                        f'Result "{result_key}" cannot be gotten because analysis '
+                        f'type "{analysis_type}" is not active'
                     )
-                    print(message)
-                    raise RuntimeError(message)
+                    raise HybkitArgError(message)
                 return getattr(self, '_get_' + analysis_type + '_results')()[result_key]
+        raise HybkitArgError('Result key "%s" not found.' % result_key)
 
     # Analysis : Public Methods : Results : get_analysis_delim_str
-    def get_analysis_delim_str(self, analysis=None, out_delim=None):
+    def get_analysis_delim_str(
+            self,
+            analysis: AnalysisOptions = None,
+            out_delim: Optional[str] = None,
+            ) -> str:
         """
         Return a delimited string containing the results of the analysis.
 
@@ -410,7 +439,12 @@ def get_analysis_delim_str(self, analysis=None, out_delim=None):
 
     # Start Write Methods
     # Analysis : Public Methods : Write Results : All Analyses
-    def write_analysis_delim_str(self, out_file_name=None, analysis=None, out_delim=None):
+    def write_analysis_delim_str(
+            self,
+            out_file_name: Optional[str] = None,
+            analysis: Optional[AnalysisArg] = None,
+            out_delim: Optional[str] = None,
+            ) -> None:
         """
         Write the results of the analysis to a delimited text file.
 
@@ -450,8 +484,12 @@ def write_analysis_delim_str(self, out_file_name=None, analysis=None, out_delim=
             out_file.write(out_delim_str)
 
     # Analysis : Public Methods : Write Results : write_analysis_results_special
-    def write_analysis_results_special(self, out_basename=None,
-                                       analysis=None, out_delim=None):
+    def write_analysis_results_special(
+            self,
+            out_basename: Optional[str] = None,
+            analysis: Optional[AnalysisArg] = None,
+            out_delim: Optional[str] = None,
+            ) -> List[str]:
         """
         Write the results of the analyses to specialized text files.
 
@@ -496,7 +534,11 @@ def write_analysis_results_special(self, out_basename=None,
         return all_out_files
 
     # Analysis : Public Methods : Plot Results : plot_analysis_results
-    def plot_analysis_results(self, out_basename=None, analysis=None):
+    def plot_analysis_results(
+            self,
+            out_basename: Optional[str] = None,
+            analysis: Optional[AnalysisArg] = None,
+            ) -> List[str]:
         """
         Plot the results of the analyses.
 
@@ -534,17 +576,19 @@ def plot_analysis_results(self, out_basename=None, analysis=None):
 
     # Start Helper Methods
     # Analysis : Private Methods : Helper Methods
-    def _get_quant(self, hyb_record):
+    def _get_quant(self, hyb_record: hybkit.HybRecord) -> int:
         if self.quant_mode == 'single':
             return 1
         elif self.quant_mode == 'reads':
             return hyb_record.get_read_count(require=True)
         elif self.quant_mode == 'records':
             return hyb_record.get_record_count(require=True)
+        else:
+            raise HybkitArgError('Quantification mode "%s" not recognized.' % self.quant_mode)
 
     # Start Init Methods
     # Analysis : Private Methods : Init Methods : Energy Analysis
-    def _init_energy(self):
+    def _init_energy(self) -> None:
         self._energy_analysis_count = 0
         self._has_energy_val = 0
         self._no_energy_val = 0
@@ -554,7 +598,7 @@ def _init_energy(self):
             self._binned_energy_vals[i] = 0
 
     # Analysis : Private Methods : Init Methods : Type Analysis
-    def _init_type(self):
+    def _init_type(self) -> None:
         self._types_analysis_count = 0
         self._hybrid_types = Counter()
         self._reordered_hybrid_types = Counter()
@@ -564,7 +608,7 @@ def _init_type(self):
         self._all_seg_types = Counter()
 
     # Analysis : Private Methods : Init Methods : miRNA Analysis
-    def _init_mirna(self):
+    def _init_mirna(self) -> None:
         self._mirna_analysis_count = 0
         self._mirnas_5p = 0
         self._mirnas_3p = 0
@@ -573,14 +617,14 @@ def _init_mirna(self):
         self._has_mirna = 0
 
     # Analysis : Private Methods : Init Methods : Target Analysis
-    def _init_target(self):
+    def _init_target(self) -> None:
         self._target_analysis_count = 0
         self._target_evals = 0
         self._target_names = Counter()
         self._target_types = Counter()
 
     # Analysis : Private Methods : Init Methods : Fold Analysis
-    def _init_fold(self):
+    def _init_fold(self) -> None:
         self._fold_analysis_count = 0
         self._folds_recorded = 0
         self._mirna_nt_fold_counts = Counter()
@@ -588,7 +632,7 @@ def _init_fold(self):
 
     # Start Add Methods
     # Analysis : Private Methods : Add Methods : Energy Analysis
-    def _add_energy(self, hyb_record):
+    def _add_energy(self, hyb_record: hybkit.HybRecord) -> None:
         count = self._get_quant(hyb_record)
         self._energy_analysis_count += 1
         if hyb_record.energy is not None:
@@ -601,7 +645,7 @@ def _add_energy(self, hyb_record):
             self._no_energy_val += count
 
     # Analysis : Private Methods : Add Methods : Type Analysis
-    def _add_type(self, hyb_record):
+    def _add_type(self, hyb_record: hybkit.HybRecord) -> None:
         hyb_record._ensure_set('eval_types')
         count = self._get_quant(hyb_record)
         self._types_analysis_count += 1
@@ -625,7 +669,7 @@ def _add_type(self, hyb_record):
         self._all_seg_types[seg2_type] += count
 
     # Analysis : Private Methods : Add Methods : miRNA Analysis
-    def _add_mirna(self, hyb_record):
+    def _add_mirna(self, hyb_record: hybkit.HybRecord) -> None:
         hyb_record._ensure_set('eval_mirna')
         count = self._get_quant(hyb_record)
         self._mirna_analysis_count += 1
@@ -641,7 +685,7 @@ def _add_mirna(self, hyb_record):
             self._non_mirna += count
 
     # Analysis : Private Methods : Add Methods : Target Analysis
-    def _add_target(self, hyb_record):
+    def _add_target(self, hyb_record: hybkit.HybRecord) -> None:
         hyb_record._ensure_set('eval_mirna')
         self._target_analysis_count += 1
         count = self._get_quant(hyb_record)
@@ -652,7 +696,7 @@ def _add_target(self, hyb_record):
             self._target_types[mirna_details['target_seg_type']] += count
 
     # Analysis : Private Methods : Add Methods : Fold Analysis
-    def _add_fold(self, hyb_record):
+    def _add_fold(self, hyb_record: hybkit.HybRecord) -> None:
         hyb_record._ensure_set('fold_record')
         hyb_record._ensure_set('eval_mirna')
         self._fold_analysis_count += 1
@@ -669,7 +713,7 @@ def _add_fold(self, hyb_record):
 
     # Start Get Results Methods
     # Analysis : Private Methods : Get Methods : Energy Analysis
-    def _get_energy_results(self):
+    def _get_energy_results(self) -> dict:
         energy_results = {}
         energy_results['energy_analysis_count'] = copy.deepcopy(self._energy_analysis_count)
         energy_results['has_energy_val'] = copy.deepcopy(self._has_energy_val)
@@ -705,7 +749,7 @@ def _get_energy_results(self):
         return energy_results
 
     # Analysis : Private Methods : Get Methods : Type Analysis
-    def _get_type_results(self):
+    def _get_type_results(self) -> dict:
         type_results = {}
         type_results['types_analysis_count'] = copy.deepcopy(self._types_analysis_count)
         type_results['hybrid_types'] = copy.deepcopy(self._hybrid_types)
@@ -717,7 +761,7 @@ def _get_type_results(self):
         return type_results
 
     # Analysis : Private Methods : Get Methods : miRNA Analysis
-    def _get_mirna_results(self):
+    def _get_mirna_results(self) -> dict:
         mirna_results = {}
         mirna_results['mirna_analysis_count'] = copy.deepcopy(self._mirna_analysis_count)
         mirna_results['has_mirna'] = copy.deepcopy(self._has_mirna)
@@ -728,7 +772,7 @@ def _get_mirna_results(self):
         return mirna_results
 
     # Analysis : Private Methods : Get Methods : Target Analysis
-    def _get_target_results(self):
+    def _get_target_results(self) -> dict:
         target_results = {}
         target_results['target_analysis_count'] = copy.deepcopy(self._target_analysis_count)
         target_results['target_evals'] = copy.deepcopy(self._target_evals)
@@ -737,7 +781,7 @@ def _get_target_results(self):
         return target_results
 
     # Analysis : Private Methods : Get Methods : Fold Analysis
-    def _get_fold_results(self):
+    def _get_fold_results(self) -> dict:
         fold_results = {}
         fold_results['fold_analysis_count'] = copy.deepcopy(self._fold_analysis_count)
         fold_results['folds_recorded'] = copy.deepcopy(self._folds_recorded)
@@ -750,7 +794,11 @@ def _get_fold_results(self):
 
     # Start Get Results String Methods
     # Analysis : Private Methods : Result String Methods : All Analyses
-    def _get_analysis_results_delim_str(self, analysis, out_delim=None):
+    def _get_analysis_results_delim_str(
+            self,
+            analysis: AnalysisOptions,
+            out_delim: Optional[str] = None,
+            ) -> str:
         if out_delim is None:
             out_delim = self.settings['out_delim']
 
@@ -779,7 +827,11 @@ def _get_analysis_results_delim_str(self, analysis, out_delim=None):
 
     # Start Write Methods
     # Analysis : Private Methods : Result Special Writing Methods : Energy Analysis
-    def _write_energy_results_special(self, basename, out_delim=None):
+    def _write_energy_results_special(
+            self,
+            basename: str,
+            out_delim: Optional[str] = None,
+            ) -> List[str]:
         if out_delim is None:
             out_delim = self.settings['out_delim']
 
@@ -805,7 +857,11 @@ def _write_energy_results_special(self, basename, out_delim=None):
         return out_file_names
 
     # Analysis : Private Methods : Result Special Writing Methods : Type Analysis
-    def _write_type_results_special(self, basename, out_delim=None):
+    def _write_type_results_special(
+            self,
+            basename: str,
+            out_delim: Optional[str] = None,
+            ) -> List[str]:
         if out_delim is None:
             out_delim = self.settings['out_delim']
 
@@ -840,7 +896,11 @@ def _write_type_results_special(self, basename, out_delim=None):
         return out_file_names
 
     # Analysis : Private Methods : Result Special Writing Methods : miRNA Analysis
-    def _write_mirna_results_special(self, basename, out_delim=None):
+    def _write_mirna_results_special(
+            self,
+            basename: str,
+            out_delim: Optional[str] = None,
+            ) -> List[str]:
         if out_delim is None:
             out_delim = self.settings['out_delim']
 
@@ -861,7 +921,11 @@ def _write_mirna_results_special(self, basename, out_delim=None):
         return out_file_names
 
     # Analysis : Private Methods : Result Special Writing Methods : Target Analysis
-    def _write_target_results_special(self, basename, out_delim=None):
+    def _write_target_results_special(
+            self,
+            basename: str,
+            out_delim: Optional[str] = None,
+            ) -> List[str]:
         if out_delim is None:
             out_delim = self.settings['out_delim']
 
@@ -893,7 +957,11 @@ def _write_target_results_special(self, basename, out_delim=None):
         return out_file_names
 
     # Analysis : Private Methods : Result Special Writing Methods : Fold Analysis
-    def _write_fold_results_special(self, basename, out_delim=None):
+    def _write_fold_results_special(
+            self,
+            basename: str,
+            out_delim: Optional[str] = None,
+            ) -> List[str]:
         if out_delim is None:
             out_delim = self.settings['out_delim']
 
@@ -931,7 +999,7 @@ def _write_fold_results_special(self, basename, out_delim=None):
 
     # Start Plot Methods
     # Analysis : Private Methods : Result Plotting Methods : Energy Analysis
-    def _plot_energy_results(self, basename):
+    def _plot_energy_results(self, basename: str) -> List[str]:
         energy_results = self._get_energy_results()
         out_files = []
         # Plot Histogram
@@ -946,10 +1014,10 @@ def _plot_energy_results(self, basename):
         return out_files
 
     # Analysis : Private Methods : Result Plotting Methods : Type Analysis
-    def _plot_type_results(self, basename):
+    def _plot_type_results(self, basename: str) -> List[str]:
         type_results = self._get_type_results()
         out_files = []
-        ype_results = {}
+        type_results = {}
         type_results['types_analysis_count'] = copy.deepcopy(self._types_analysis_count)
         type_results['hybrid_types'] = copy.deepcopy(self._hybrid_types)
         type_results['reordered_hybrid_types'] = copy.deepcopy(self._reordered_hybrid_types)
@@ -985,7 +1053,7 @@ def _plot_type_results(self, basename):
         hybkit.plot.type_count_dual(
             type_results['mirna_hybrid_types'],
             mirna_hybrid_types_file_name,
-            title='Reordered miRNA\' Hybrid Types',
+            title="Reordered miRNA' Hybrid Types",
             name=self.name,
             join_entries=True,
         )
@@ -1023,7 +1091,7 @@ def _plot_type_results(self, basename):
         return out_files
 
     # Analysis : Private Methods : Result Plotting Methods : Target Analysis
-    def _plot_target_results(self, basename):
+    def _plot_target_results(self, basename: str) -> List[str]:
         target_results = self._get_target_results()
         out_files = []
 
@@ -1049,7 +1117,7 @@ def _plot_target_results(self, basename):
         return out_files
 
     # Analysis : Private Methods : Result Plotting Methods : Fold Analysis
-    def _plot_fold_results(self, basename):
+    def _plot_fold_results(self, basename: str) -> List[str]:
         fold_results = self._get_fold_results()
         out_files = []
 
@@ -1086,25 +1154,25 @@ def _plot_fold_results(self, basename):
         return out_files
 
     # Analysis : Private Methods : Utility Methods
-    def _ensure_analyses_active(self, analyses):
+    def _ensure_analyses_active(self, analyses: AnalysisArg) -> None:
         if isinstance(analyses, str):
             all_test_analyses = [analyses]
         elif isinstance(analyses, list):
             all_test_analyses = analyses
         else:
-            raise TypeError('Analyses must be a string or list of strings.')
+            message = 'Analyses must be a string or list of strings.'
+            raise TypeError(message)
         for test_analysis in all_test_analyses:
             if test_analysis not in self.analysis_types:
                 message = (
-                    'Analysis type "%s" not an active analysis.'
-                    '\nActive choices: %s' % (test_analysis, ', '.join(self.analysis_types))
+                    f'Analysis type "{test_analysis!s}" not an active analysis.'
+                    '\nActive choices: {}'.format(', '.join(self.analysis_types))
                 )
-                print(message)
-                raise RuntimeError(message)
+                raise HybkitArgError(message)
 
     # Analysis : Private Classmethods
     @classmethod
-    def _sanitize_name(self, file_name):
+    def _sanitize_name(cls, file_name: str) -> str:
         for char, replace in [('*', 'star'), (',', 'com')]:
             file_name = file_name.replace(char, replace)
         return file_name
diff --git a/hybkit/errors.py b/hybkit/errors.py
new file mode 100644
index 0000000..662007a
--- /dev/null
+++ b/hybkit/errors.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Daniel Stribling  |  ORCID: 0000-0002-0649-9506
+# Renne Lab, University of Florida
+# Hybkit Project : https://www.github.com/RenneLab/hybkit
+
+r"""
+Module storing hybkit error classes.
+"""
+
+# ----- Begin Error Classes -----
+class HybkitError(Exception):
+    """
+    Base class for Hybkit errors.
+
+    Attributes:
+        message (str): Human-readable string describing the error.
+    """
+
+
+class HybkitArgError(HybkitError):
+    """
+    Error raised when an invalid argument is provided to a Hybkit function.
+
+    Subclass of :class:`HybkitError`.
+
+    Attributes:
+        message (str): Human-readable string describing the error.
+    """
+
+
+class HybkitConstructorError(HybkitError):
+    """
+    Error raised when a read error occurs.
+
+    Subclass of :class:`HybkitError`.
+
+    Attributes:
+        message (str): Human-readable string describing the error.
+    """
+
+
+class HybkitIterError(HybkitError):
+    """
+    Error raised when an error is encountered during Hybkit iteration.
+
+    Subclass of :class:`HybkitError`.
+
+    Attributes:
+        message (str): Human-readable string describing the error.
+    """
+
+
+class HybkitMiscError(HybkitError):
+    """
+    Error raised when an error is encountered during Hybkit usage.
+
+    Subclass of :class:`HybkitError`.
+
+    Attributes:
+        message (str): Human-readable string describing the error.
+    """
diff --git a/hybkit/plot.py b/hybkit/plot.py
index a553e96..f900aca 100644
--- a/hybkit/plot.py
+++ b/hybkit/plot.py
@@ -5,17 +5,40 @@
 
 """Methods for plotting analyses of HybRecord and FoldRecord objects."""
 
-import matplotlib
-matplotlib.use('Agg')
+import copy
+import logging
+from collections import Counter
+from typing import Any, Callable, Dict, Generic, Optional, TypeVar
+
+import matplotlib as mpl
 import matplotlib.pyplot as plt
 import matplotlib.ticker as mtick
-from collections import Counter
-import copy
-import hybkit
 
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
+import hybkit
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+from hybkit.errors import HybkitMiscError
+
+# ----- File-Specific Linting Directives:
+# ruff: noqa: F401 SLF001
+
+# ----- Begin Typing Assist -----
+# F = TypeVar('F', bound=Callable[..., Any])
+# class copy_signature(Generic[F]):
+#     def __init__(self, target: F) -> None: ...
+#     def __call__(self, wrapped: Callable[..., Any]) -> F: ...
+
+mpl.use('Agg')
 
 # ----- Begin Plot Constants -----
 #: Default Colors for colored plots:
@@ -23,7 +46,7 @@
 #: Colors in RGB nomenclature (1-255):
 #: Black (0,0,0), Orange (230,159,0), Sky Blue (86,180,233), Bluish Green (0,158,115),
 #: Yellow (240,228,66), Blue (0,114,178), Vermilion (213,94,0), Reddish Purple (204,121,167)
-COLOR_DICT = {
+COLOR_DICT = ({
     'Blue': '#0072B2',
     'Vermilion': '#D55E00',
     'Bluish Green': '#009E73',
@@ -34,28 +57,29 @@
     # 'Orange': '#E69F00',
     'Yellow': '#F0E442',
     # 'Black': '#000000',
-}
+})
 
 #: List of default colors for colored plots.
 COLOR_LIST = list(COLOR_DICT.values())
 
-# Base defaults for matplotlib rcParams.
-RC_PARAMS_DEFAULTS = {
+# Base defaults for mpl rcParams.
+RC_PARAMS_DEFAULTS = ({
     'figure.dpi': 1200,
-    'figure.figsize': matplotlib.rcParams['figure.figsize'],  # 6.4, 4.8 in
+    'figure.figsize': mpl.rcParams['figure.figsize'],  # 6.4, 4.8 in
     'axes.titlepad': 15,
-}
+})
 
-# Default matplotlib rcParams for pie charts.
-PIE_RC_PARAMS = copy.deepcopy(RC_PARAMS_DEFAULTS)
+# Default mpl rcParams for pie charts.
+PIE_RC_PARAMS = dict(copy.deepcopy(RC_PARAMS_DEFAULTS))
 PIE_RC_PARAMS.update({
     'axes.titleweight': 'bold',
     'axes.labelweight': 'bold',
     # 'figure.figsize': (10, 4.8),
-}),
+})
+PIE_RC_PARAMS = (copy.deepcopy(PIE_RC_PARAMS))
 
-# Default matplotlib rcParams for bar charts.
-BAR_RC_PARAMS = copy.deepcopy(RC_PARAMS_DEFAULTS)
+# Default mpl rcParams for bar charts.
+BAR_RC_PARAMS = dict(copy.deepcopy(RC_PARAMS_DEFAULTS))
 BAR_RC_PARAMS.update({
     'axes.titlesize': 'large',
     # 'axes.titlesize': 'x-large',
@@ -64,29 +88,34 @@
     'axes.labelweight': 'bold',
     # 'xtick.labelsize': 'large',
     # 'ytick.labelsize': 'large',
-}),
+})
+BAR_RC_PARAMS = (copy.deepcopy(BAR_RC_PARAMS))
 
-#: Default matplotlib rcParams for energy analysis histograms.
-ENERGY_HIST_RC_PARAMS = copy.deepcopy(BAR_RC_PARAMS)
+#: Default mpl rcParams for energy analysis histograms.
+ENERGY_HIST_RC_PARAMS = dict(copy.deepcopy(BAR_RC_PARAMS))
 ENERGY_HIST_RC_PARAMS.update({})
+ENERGY_HIST_RC_PARAMS = (copy.deepcopy(ENERGY_HIST_RC_PARAMS))
 
-#: Default matplotlib rcParams for type analysis pie charts.
-TYPE_PIE_SINGLE_RC_PARAMS = copy.deepcopy(PIE_RC_PARAMS)
+#: Default mpl rcParams for type analysis pie charts.
+TYPE_PIE_SINGLE_RC_PARAMS = dict(copy.deepcopy(PIE_RC_PARAMS))
 TYPE_PIE_SINGLE_RC_PARAMS.update({})
 # MATCH_DEFAULTS = {
 #     'MATCH_MIN_COUNT': 0,
 #     'MATCH_XLABEL': 'Predicted Base-Pairs',
 #     'MATCH_YLABEL': 'Hybrid Count',
 # }
+TYPE_PIE_SINGLE_RC_PARAMS = (copy.deepcopy(TYPE_PIE_SINGLE_RC_PARAMS))
+
 
-#: Default matplotlib rcParams for type analysis pie charts.
-TYPE_PIE_DUAL_RC_PARAMS = copy.deepcopy(PIE_RC_PARAMS)
+#: Default mpl rcParams for type analysis pie charts.
+TYPE_PIE_DUAL_RC_PARAMS = dict(copy.deepcopy(PIE_RC_PARAMS))
 TYPE_PIE_DUAL_RC_PARAMS.update({
     'figure.figsize': (8, 4.8),
 })
+TYPE_PIE_DUAL_RC_PARAMS = (copy.deepcopy(TYPE_PIE_DUAL_RC_PARAMS))
 
-#: Default matplotlib rcParams for target analysis pie charts.
-TARGET_PIE_RC_PARAMS = copy.deepcopy(PIE_RC_PARAMS)
+#: Default mpl rcParams for target analysis pie charts.
+TARGET_PIE_RC_PARAMS = dict(copy.deepcopy(PIE_RC_PARAMS))
 TARGET_PIE_RC_PARAMS.update({
     'figure.figsize': (9.6, 4.8),
 })
@@ -94,20 +123,21 @@
 #     'TARGET_TITLE': 'Targets',
 #     'TARGET_TYPE_TITLE': 'Target Types',
 # }
+TARGET_PIE_RC_PARAMS = (copy.deepcopy(TARGET_PIE_RC_PARAMS))
 
-#: Default matplotlib rcParams for fold match analysis histograms.
-FOLD_MATCH_HIST_RC_PARAMS = copy.deepcopy(BAR_RC_PARAMS)
+#: Default mpl rcParams for fold match analysis histograms.
+FOLD_MATCH_HIST_RC_PARAMS = dict(copy.deepcopy(BAR_RC_PARAMS))
 FOLD_MATCH_HIST_RC_PARAMS.update({})
 # FOLD_DEFAULTS = {
 #     'ENERGY_DENSITIES_TITLE': 'Normalized Hybrid Predicted Energies',
 #     'MATCH_TITLE': 'Hybrid Predicted Base Pairs',
 # }
+FOLD_MATCH_HIST_RC_PARAMS = (copy.deepcopy(FOLD_MATCH_HIST_RC_PARAMS))
 
-#: Default matplotlib rcParams for fold nt counts analysis histograms.
-FOLD_NT_COUNTS_HIST_RC_PARAMS = copy.deepcopy(BAR_RC_PARAMS)
-FOLD_NT_COUNTS_HIST_RC_PARAMS.update({
-
-})
+#: Default mpl rcParams for fold nt counts analysis histograms.
+FOLD_NT_COUNTS_HIST_RC_PARAMS = dict(copy.deepcopy(BAR_RC_PARAMS))
+FOLD_NT_COUNTS_HIST_RC_PARAMS.update({})
+FOLD_NT_COUNTS_HIST_RC_PARAMS = (copy.deepcopy(FOLD_NT_COUNTS_HIST_RC_PARAMS))
 
 # _FORMAT_NAME_MAP = {
 #     'mirnas_5p': "5'_miRNA_Hybrids",
@@ -118,7 +148,7 @@
 
 
 #: Default Pie Chart Plot Settings.
-PIE_DEFAULTS = {
+PIE_DEFAULTS = ({
     'SETTINGS': {
         'autopct': '%1.1f%%',
         'shadow': False,
@@ -128,30 +158,31 @@
     'COLORS': COLOR_LIST,
     'OTHER_THRESHOLD': 0.05,
     'MIN_WEDGE_SIZE': 0.04,
-}
+})
 # PIE['DEFAULT_TARGET_SETTINGS'] = copy.deepcopy(PIE['DEFAULT_SETTINGS'])
 # PIE['DEFAULT_TARGET_SETTINGS'].update({'textprops': {'size': 'small'}})
 
 #: Default Bar Chart Plot Settings.
-BAR_DEFAULTS = {
+BAR_DEFAULTS = ({
     'BAR_WIDTH': 0.9,
     'BAR_ALIGN': 'edge',
     'BAR_EDGE_COLOR': None,
-}
+})
 
 #: Default Bar Chart of Integer Plot Settings.
-BAR_INT_DEFAULTS = copy.deepcopy(BAR_DEFAULTS)
+BAR_INT_DEFAULTS = dict(copy.deepcopy(BAR_DEFAULTS))
 BAR_INT_DEFAULTS.update({
     'BAR_ALIGN': 'center',
 })
+# BAR_INT_DEFAULTS = (BAR_INT_DEFAULTS)
 
 #: Default Bar Chart Plot Settings for Energy Histograms.
-ENERGY_DEFAULTS = {
+ENERGY_DEFAULTS = ({
     'MIN_COUNT': 0,
     'MIN_DENSITY': 0.0,
     'XLABEL': 'Hybrid Gibbs Free Energy (kcal/mol)',
     'YLABEL': 'Hybrid Count',
-}
+})
 
 # LINE_RC_PARAMS = copy.deepcopy(RC_PARAMS_DEFAULTS)
 # LINE_RC_PARAMS.update({
@@ -171,13 +202,14 @@
 
 # ----- Begin Plotting Methods -----
 # Public Methods : Energy : energy_histogram
-def energy_histogram(results,
-                     plot_file_name,
-                     title,
-                     name=None,
-                     rc_params=copy.deepcopy(ENERGY_HIST_RC_PARAMS),
-                     bar_params=copy.deepcopy(BAR_DEFAULTS),
-                     ):
+def energy_histogram(
+        results: Dict[str, Any],
+        plot_file_name: str,
+        title: str,
+        name: Optional[str] = None,
+        rc_params: Dict[str, Any] = ENERGY_HIST_RC_PARAMS,
+        bar_params: Dict[str, Any] = BAR_DEFAULTS,
+        ) -> None:
     """
     Plot histogram of hybrid energies from an :class:`~hybkit.analysis.Analysis` fold analysis.
 
@@ -187,19 +219,18 @@ def energy_histogram(results,
         plot_file_name (str): Name of output file.
         title (str): Title of plot.
         name (:obj:`str`, optional): Name of analysis to be included in plot title.
-        rc_params (:obj:`dict`, optional): Dictionary of matplotlib rcParams. Defaults
+        rc_params (:obj:`dict`, optional): Dictionary of mpl rcParams. Defaults
             to :obj:`~hybkit.plot.ENERGY_HIST_RC_PARAMS`.
         bar_params (:obj:`dict`, optional): Dictionary of bar plot parameters. Defaults
             to :obj:`~hybkit.plot.BAR_DEFAULTS`.
     """
     # Check if empty
     max_count = 0
-    for energy, count in results.items():
+    for _energy, count in results.items():
         max_count = max(max_count, count)
     if not max_count:
         message = 'Warning: Attempted to create empty plot to name: %s' % plot_file_name
-        print(message)
-        raise RuntimeError(message)
+        logging.warning(message)
 
     # Plot energy bins
     energies, counts = [], []
@@ -232,13 +263,14 @@ def energy_histogram(results,
 
 # Public Methods : Type : type_count
 # Plot a pie plot from a hybkit Type Analysis
-def type_count(results,
-               plot_file_name,
-               title,
-               name=None,
-               join_entries=False,
-               rc_params=copy.deepcopy(TYPE_PIE_SINGLE_RC_PARAMS),
-               ):
+def type_count(
+        results: Counter,
+        plot_file_name: str,
+        title: str,
+        name: Optional[str] = None,
+        join_entries: bool = False,
+        rc_params: Dict[str, Any] = TYPE_PIE_SINGLE_RC_PARAMS,
+        ) -> None:
     """
     Plot pie chart of hybrid type counts from an :class:`~hybkit.analysis.Analysis` type analysis.
 
@@ -250,15 +282,16 @@ def type_count(results,
         name (:obj:`str`, optional): Name of analysis to be included in plot title.
         join_entries (:obj:`bool`, optional): If True, join two-tuple pairs into a
             single string for plot labels.
-        rc_params (:obj:`dict`, optional): Dictionary of matplotlib rcParams. Defaults
+        rc_params (:obj:`dict`, optional): Dictionary of mpl rcParams. Defaults
             to :obj:`~hybkit.plot.TYPE_PIE_RC_PARAMS`.
     """
     # Sort hybrid_type_counts counter object in descending order
     labels, counts = [], []
     for key, count in results.most_common():
+        use_key = key
         if join_entries:
-            key = '--'.join(key)
-        labels.append(key)
+            use_key = '--'.join(key)
+        labels.append(use_key)
         counts.append(count)
 
     if name is not None:
@@ -281,13 +314,14 @@ def type_count(results,
 
 # Public Methods : Type : type_count
 # Plot a pie plot for two types from a hybkit Type Analysis
-def type_count_dual(results,
-                    plot_file_name,
-                    title,
-                    name=None,
-                    join_entries=False,
-                    rc_params=copy.deepcopy(TYPE_PIE_DUAL_RC_PARAMS),
-                    ):
+def type_count_dual(
+        results: Counter,
+        plot_file_name: str,
+        title: str,
+        name: Optional[str] = None,
+        join_entries: bool = False,
+        rc_params: Dict[str, Any] = TYPE_PIE_DUAL_RC_PARAMS,
+        ) -> None:
     """Hold Place for replaced docstring."""
     return type_count(results, plot_file_name, title, name, join_entries, rc_params)
 
@@ -298,7 +332,8 @@ def type_count_dual(results,
 
 # Public Methods : Target : target_count
 # Plot a pie plot for targets from a hybkit target analysis
-def target_count(*args, **kwargs):
+# @copy_signature(type_count)
+def target_count(*args, **kwargs) -> None:  # noqa: ANN003, ANN002
     """Hold Place for replaced docstring."""
     return type_count(*args, rc_params=TARGET_PIE_RC_PARAMS, **kwargs)
 
@@ -310,14 +345,15 @@ def target_count(*args, **kwargs):
     target_count.__doc__ = target_count.__doc__.replace(q, r)
 
 
-def fold_match_counts_histogram(results,
-                                plot_file_name,
-                                title,
-                                is_prop=False,
-                                name=None,
-                                rc_params=copy.deepcopy(FOLD_MATCH_HIST_RC_PARAMS),
-                                bar_params=copy.deepcopy(BAR_INT_DEFAULTS),
-                                ):
+def fold_match_counts_histogram(
+        results: Counter,
+        plot_file_name: str,
+        title: str,
+        name: Optional[str] = None,
+        is_prop: bool = False,
+        rc_params: Dict[str, Any]= FOLD_MATCH_HIST_RC_PARAMS,
+        bar_params: Dict[str, Any] = BAR_INT_DEFAULTS,
+        ) -> None:
     """
     Plot histogram of predicted miRNA/target match count.
 
@@ -328,23 +364,25 @@ def fold_match_counts_histogram(results,
         title (str): Title of plot.
         is_prop (:obj:`bool`, optional): If True, y axis is proportion.
         name (:obj:`str`, optional): Name of analysis to be included in plot title.
-        rc_params (:obj:`dict`, optional): Dictionary of matplotlib rcParams. Defaults
+        rc_params (:obj:`dict`, optional): Dictionary of mpl rcParams. Defaults
             to :obj:`~hybkit.plot.FOLD_MATCH_HIST_RC_PARAMS`.
         bar_params (:obj:`dict`, optional): Dictionary of bar plot parameters. Defaults
             to :obj:`~hybkit.plot.BAR_INT_DEFAULTS`.
     """
     x_vals = []
     y_vals = []
+    init_max_val = -10000
     min_x = 10000
     max_x = -10000
-    max_val = -10000
+    max_val = init_max_val
     for x_val, y_val in results.items():
         min_x = min(min_x, x_val)
         max_x = max(max_x, x_val)
         max_val = max(max_val, y_val)
 
-    if max_val == -10000:
-        print('Warning: Attempted to create empty plot to name: %s' % plot_file_name)
+    if max_val == init_max_val:
+        message = f'Warning: Attempted to create empty plot to name: {plot_file_name}'
+        logging.warning(message)
         return
 
     for x_val in range(min_x, (max_x + 1)):
@@ -380,8 +418,8 @@ def fold_match_counts_histogram(results,
 
     _plot_int_hist(plot_params=plot_params)
 
-
-def fold_mirna_nt_counts_histogram(*args, **kwargs):
+# @copy_signature(fold_match_counts_histogram)
+def fold_mirna_nt_counts_histogram(*args, **kwargs) -> None:  # noqa: ANN002, ANN003
     """Hold Place for replaced docstring."""
     if 'rc_params' not in kwargs:
         kwargs['rc_params'] = copy.deepcopy(FOLD_NT_COUNTS_HIST_RC_PARAMS)
@@ -397,7 +435,7 @@ def fold_mirna_nt_counts_histogram(*args, **kwargs):
 
 # ----- Begin Private Plotting Methods -----
 # Private Methods : Energy : _plot_energy_histogram
-def _plot_energy_histogram(plot_params):
+def _plot_energy_histogram(plot_params: Dict[str, Any]) -> None:
     # Update plot parameters
     plt.rcParams.update(plot_params['rc_params'])
 
@@ -405,7 +443,7 @@ def _plot_energy_histogram(plot_params):
     fig, ax = plt.subplots()
 
     # Create the bars for the histogram
-    bars = ax.bar(
+    _bars = ax.bar(
         plot_params['x_vals'],
         plot_params['y_vals'],
         width=plot_params['width'],
@@ -414,11 +452,11 @@ def _plot_energy_histogram(plot_params):
     )
 
     # for items in zip(plot_params['x_vals'], plot_params['y_vals']):
-    #     print(items)
+    #     pass
 
     # Invert the x-axis and set tick locators
     ax.invert_xaxis()
-    ax.xaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(1))
+    ax.xaxis.set_minor_locator(mpl.ticker.MultipleLocator(1))
 
     # Set x-axis limits and ticks
     left_xlim = max(0, int(plot_params['x_vals'][0]))
@@ -431,7 +469,7 @@ def _plot_energy_histogram(plot_params):
     plt.ylabel(plot_params['ylabel'])
 
     # Set y-axis minor tick locators
-    ax.yaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
+    ax.yaxis.set_minor_locator(mpl.ticker.AutoMinorLocator())
 
     # Set the title with custom padding
     plt.title(plot_params['title'])
@@ -448,18 +486,17 @@ def _plot_energy_histogram(plot_params):
     # Clear the current figure and close it to release resources
     plt.clf()
     plt.close()
-    matplotlib.rcdefaults()
+    mpl.rcdefaults()
 
 
 # Private Methods : Energy : _plot_types_pie_chart
-def _plot_types_pie_chart(plot_params):
+def _plot_types_pie_chart(plot_params: Dict[str, Any]) -> None:
     # Calculate the total size and the size of each fraction
     total_size = sum(plot_params['sizes'])
-    if total_size < 0.00000001:
+    if total_size < 0.00000001:  #noqa: PLR2004
         message = 'Warning: Attempted to create empty plot to name:'
         message += ' %s' % plot_params['plot_file_name']
-        print(message)
-        raise RuntimeError(message)
+        logging.warning(message)
 
     fraction_sizes = [(size / total_size) for size in plot_params['sizes']]
 
@@ -467,10 +504,9 @@ def _plot_types_pie_chart(plot_params):
     use_labels, use_sizes = [], []
     for i in range(len(plot_params['labels'])):
         total_fraction = sum(use_sizes) / total_size
-        if fraction_sizes[i] < plot_params['min_wedge_size']:
-            break
-        elif (total_fraction > (1 - plot_params['other_threshold'])
-              and i != (len(plot_params['labels']) - 1)):
+        if (fraction_sizes[i] < plot_params['min_wedge_size']
+            or (total_fraction > (1 - plot_params['other_threshold'])
+              and i != (len(plot_params['labels']) - 1))):
             break
         else:
             use_labels.append(plot_params['labels'][i])
@@ -518,11 +554,14 @@ def _plot_types_pie_chart(plot_params):
     # Clear the current figure and close it to release resources
     plt.clf()
     plt.close()
-    matplotlib.rcdefaults()
+    mpl.rcdefaults()
 
 
 # Private Methods : Energy : _plot_energy_histogram
-def _plot_int_hist(plot_params, truncate_to_first_int=True):
+def _plot_int_hist(
+        plot_params: Dict[str, Any],
+        truncate_to_first_int: bool = True
+        ) -> None:
     # Update plot parameters
     plt.rcParams.update(plot_params['rc_params'])
 
@@ -530,7 +569,7 @@ def _plot_int_hist(plot_params, truncate_to_first_int=True):
     fig, ax = plt.subplots()
 
     # Create the bars for the histogram
-    bars = ax.bar(
+    _bars = ax.bar(
         plot_params['x_vals'],
         plot_params['y_vals'],
         width=plot_params['width'],
@@ -541,7 +580,7 @@ def _plot_int_hist(plot_params, truncate_to_first_int=True):
 
     # Invert the x-axis and set tick locators
     # ax.invert_xaxis()
-    # ax.xaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(1))
+    # ax.xaxis.set_minor_locator(mpl.ticker.MultipleLocator(1))
 
     # Set x-axis limits and ticks
     if truncate_to_first_int and int(plot_params['x_vals'][0]) > 0:
@@ -554,7 +593,7 @@ def _plot_int_hist(plot_params, truncate_to_first_int=True):
     plt.ylabel(plot_params['ylabel'])
 
     # Set y-axis minor tick locators
-    # ax.yaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
+    # ax.yaxis.set_minor_locator(mpl.ticker.AutoMinorLocator())
 
     # Set the title with custom padding
     plt.title(plot_params['title'])
@@ -571,4 +610,4 @@ def _plot_int_hist(plot_params, truncate_to_first_int=True):
     # Clear the current figure and close it to release resources
     plt.clf()
     plt.close()
-    matplotlib.rcdefaults()
+    mpl.rcdefaults()
diff --git a/hybkit/settings.py b/hybkit/settings.py
index 1b4fd74..ad472e4 100644
--- a/hybkit/settings.py
+++ b/hybkit/settings.py
@@ -6,16 +6,17 @@
 """This module contains settings information for hybkit classes and methods."""
 
 import copy
+from typing import Dict, List
 
 
 # ----- Begin Settings Helper Functions -----
 # Util : Settings Helper Functions
-def _all_str_cases(in_str):
+def _all_str_cases(in_str: str) -> List[str]:
     return [in_str.lower(), in_str.title(), in_str.upper()]
 
 
 # Util : Settings Helper Functions
-def _settings_info_to_settings(settings_info_dict):
+def _settings_info_to_settings(settings_info_dict: Dict) -> dict:
     ret_dict = {}
     for key in settings_info_dict:
         ret_dict[key] = copy.deepcopy(settings_info_dict[key][0])
@@ -49,6 +50,12 @@ def _settings_info_to_settings(settings_info_dict):
 ANALYSIS_TYPE_OPTIONS_FOLD = ['fold']
 ANALYSIS_TYPE_OPTIONS = [*ANALYSIS_TYPE_OPTIONS_HYB, *ANALYSIS_TYPE_OPTIONS_FOLD]
 
+#: Minimum number of fields in hyb line:
+MIN_RECORD_FIELDS = 15
+
+#: Maximum number of fields in hyb line:
+MAX_RECORD_FIELDS = 16
+
 # #: Default minimum Gibbs Free Energy for bins in :class:`EnergyAnalysis`
 # #: (range: ENERGY_MIN_BIN <= 0).
 # ENERGY_MIN_BIN = '-45.0'
diff --git a/hybkit/type_finder.py b/hybkit/type_finder.py
index d4ba619..8f3aebb 100644
--- a/hybkit/type_finder.py
+++ b/hybkit/type_finder.py
@@ -12,9 +12,16 @@
 
 import os
 import types
+from typing import Any, Callable, Dict, List, NoReturn, Optional, Union
 
+from hybkit.errors import HybkitArgError, HybkitMiscError
 
-class TypeFinder(object):
+# ----- Begin Typing Variables ----- #
+SegProps = Dict[str, Union[float, int, str]]
+
+
+# ----- Begin TypeFinder Class ----- #
+class TypeFinder:
     """
     Class for parsing identifiers to identify sequence 'type'.
 
@@ -84,13 +91,18 @@ class TypeFinder(object):
 
     # TypeFinder : Public Methods : Initialization
     # STUB, class is designed to be used with class-level functions.
-    def __init__(self):
+    def __init__(self) -> NoReturn:
         """Class not used with initialization."""
-        raise RuntimeError('WARN: TypeFinder class not intended to be initialized for use.')
+        message = 'TypeFinder class not intended to be initialized for use.'
+        raise RuntimeError(message)
 
     # TypeFinder : Public Classmethods : method
     @classmethod
-    def set_method(cls, method, params={}):
+    def set_method(
+            cls,
+            method: str,
+            params: Optional[Dict[str, Any]] = None
+            ) -> None:
         """
         Select method to use when finding types.
 
@@ -104,14 +116,17 @@ def set_method(cls, method, params={}):
         if method not in cls.methods:
             message = 'Selected method: %s is not defined.\n' % method
             message += 'Allowed Options:' + ', '.join(cls.methods.keys())
-            print(message)
-            raise RuntimeError(message)
+            raise HybkitArgError(message)
         cls.find_with_params = getattr(cls, cls.methods[method])
-        cls.params = params
+        if params is not None:
+            use_params = params
+        else:
+            use_params = {}
+        cls.params = use_params
 
     # TypeFinder : Public Classmethods : method
     @classmethod
-    def method_is_set(cls):
+    def method_is_set(cls) -> bool:
         """
         Return whether a TypeFinder method has been set.
 
@@ -124,14 +139,14 @@ def method_is_set(cls):
 
     # TypeFinder : Public Classmethods : method
     @classmethod
-    def check_set_method(cls):
+    def check_set_method(cls) -> None:
         """If no TypeFinder method set, set as :attr:`default_method`."""
         if not cls.method_is_set():
             cls.set_method(cls.default_method)
 
     # TypeFinder : Public Classmethods : method
     @classmethod
-    def find(cls, seg_props):
+    def find(cls, seg_props: SegProps) -> Optional[str]:
         """
         Find type of segment using :meth:`TypeFinder.find_custom_method`.
 
@@ -148,12 +163,17 @@ def find(cls, seg_props):
             str: Type of the provided segment, or None if a type cannot be identified.
         """
         if cls.find_with_params is None:
-            raise RuntimeError('TypeFinder method has not been set.')
+            message = 'TypeFinder method has not been set.'
+            raise RuntimeError(message)
         return cls.find_with_params(seg_props, cls.params)
 
     # TypeFinder : Public Classmethods : method
     @classmethod
-    def set_custom_method(cls, method, params={}):
+    def set_custom_method(
+            cls,
+            method: Callable,
+            params: Optional[dict] = None
+            ) -> None:
         """
         Set the method for use to find seg types.
 
@@ -173,11 +193,17 @@ def set_custom_method(cls, method, params={}):
             params (dict, optional): dict of custom parameters to set for use.
         """
         cls.find_with_params = types.MethodType(method, cls)
-        cls.params = params
+        if params is not None:
+            cls.params = params
+        else:
+            cls.params = {}
 
     # TypeFinder : Public Staticmethods : find_seg_type
     @staticmethod
-    def method_hybformat(seg_props, params={}):
+    def method_hybformat(
+            seg_props: SegProps,
+            params: Optional[dict] = None,
+            ) -> Optional[str]:
         """
         Return the type of the provided segment, or None if segment cannot be identified.
 
@@ -200,11 +226,10 @@ def method_hybformat(seg_props, params={}):
             seg_props (dict): :obj:`seg_props` from :class:`hybkit.HybRecord`
             params (dict, optional): Unused in this method.
         """
-        if params:
+        if params is not None and params:
             message = 'method_hybformat does not use params, but params were provided:\n'
             message += str(params)
-            print(message)
-            raise RuntimeError(message)
+            raise HybkitArgError(message)
         if '_' not in seg_props['ref_name']:
             return None
         else:
@@ -213,7 +238,10 @@ def method_hybformat(seg_props, params={}):
 
     # TypeFinder : Public Staticmethods : methods
     @staticmethod
-    def method_string_match(seg_props, params={}):
+    def method_string_match(
+            seg_props: SegProps,
+            params: Optional[dict] = None,
+            ) -> Optional[str]:
         """
         Return the type of the provided segment, or None if unidentified.
 
@@ -242,13 +270,11 @@ def method_string_match(seg_props, params={}):
                 to evaluate.
             params (dict, optional): Dict with search parameters as described above.
         """
-        if not params:
+        if params is None or not params:
             message = 'method_string_match requires params, but none were provided.'
-            print(message)
-            raise RuntimeError(message)
+            raise HybkitArgError(message)
         seg_name = seg_props['ref_name']
         found_type = None
-        check_done = False
         if 'startswith' in params and not found_type:
             for search_string, search_type in params['startswith']:
                 if seg_name.startswith(search_string):
@@ -274,7 +300,7 @@ def method_string_match(seg_props, params={}):
 
     # TypeFinder : Public Staticmethods : find_seg_type
     @staticmethod
-    def make_string_match_params(legend_file):
+    def make_string_match_params(legend_file: str) -> dict:
         """
         Read csv and return a dict of search parameters for :meth:`method_string_match`.
 
@@ -295,16 +321,14 @@ def make_string_match_params(legend_file):
                           ('_trans', 'mRNA')   ]}
 
         """
-        ALLOWED_SEARCH_TYPES = {'startswith', 'contains', 'endswith', 'matches'}
+        allowed_search_types = {'startswith', 'contains', 'endswith', 'matches'}
         return_dict = {}
         if not isinstance(legend_file, (str)):
             message = 'legend_file must be a string, not %s' % type(legend_file)
-            print(message)
-            raise TypeError(message)
+            raise HybkitArgError(message)
         if not os.path.isfile(legend_file):
             message = 'File: %s for make_string_match_params() method ' % legend_file
             message += 'not found.'
-            print(message)
             raise FileNotFoundError(message)
 
         with open(legend_file, 'r') as legend_file_obj:
@@ -315,19 +339,17 @@ def make_string_match_params(legend_file):
                 # Skip Commented Lines
                 if line.lstrip().startswith('#'):
                     continue
-                line = line.strip()
-                split_line = line.split(',')
-                if len(split_line) != 3:
-                    message = 'Error reading legend line: \n%s\n%s' % (str(line), str(split_line))
+                use_line = line.strip()
+                split_line = use_line.split(',')
+                if len(split_line) != 3: # noqa: PLR2004
+                    message = f'Error reading legend line: \n{use_line!s}\n{split_line!s}'
                     message += '\nThree comma-separated entries expected.'
-                    print(message)
                     raise RuntimeError(message)
                 search_type, search_string, seg_type = split_line
-                if search_type not in ALLOWED_SEARCH_TYPES:
-                    message = 'Read Search type: "%s"\n' % search_type
-                    message += 'Not in allowed types: %s' % ', '.join(ALLOWED_SEARCH_TYPES)
-                    message += '\nFor legend line: \n%s\n' % (str(line))
-                    print(message)
+                if search_type not in allowed_search_types:
+                    message = f'Read Search type: "{search_type}"\n'
+                    message += 'Not in allowed types: {}'.format(', '.join(allowed_search_types))
+                    message += f'\nFor legend line: \n{use_line!s}\n'
                     raise RuntimeError(message)
 
                 if search_type not in return_dict:
@@ -339,7 +361,10 @@ def make_string_match_params(legend_file):
 
     # TypeFinder : Public Methods : Flag_Info : find_seg_type
     @staticmethod
-    def method_id_map(seg_props, params={}):
+    def method_id_map(
+            seg_props: SegProps,
+            params: dict = None,
+            ) -> Optional[str]:
         """
         Return the type of the provided segment or None if it cannot be identified.
 
@@ -356,6 +381,8 @@ def method_id_map(seg_props, params={}):
         This dict can be generated with the associated :meth:`make_id_map_params` method.
 
         Args:
+            seg_props (dict): :class:`~hybkit.HybRecord` segment properties dict
+                to evaluate.
             params (dict): Dict of mapping of sequence identifiers to sequence types.
 
         Returns:
@@ -363,6 +390,8 @@ def method_id_map(seg_props, params={}):
 
         """
         seg_name = seg_props['ref_name']
+        if params is None:
+            params = {}
         if seg_name in params:
             return params[seg_name]
         else:
@@ -370,7 +399,7 @@ def method_id_map(seg_props, params={}):
 
     # TypeFinder : Public Staticmethods : find_seg_type
     @staticmethod
-    def make_id_map_params(mapped_id_files):
+    def make_id_map_params(mapped_id_files: List[str]) -> dict:
         """
         Read file(s) into a mapping of sequence identifiers.
 
@@ -395,7 +424,6 @@ def make_id_map_params(mapped_id_files):
         if not isinstance(mapped_id_files, (str, list, tuple)):
             message = 'arguments passed to mapped_id_files and type_file_pairs must be '
             message += 'provided as a list or tuple.\n  Provided: "%s"' % str(mapped_id_files)
-            print(message)
             raise TypeError(message)
         if isinstance(mapped_id_files, str):
             mapped_id_files = [mapped_id_files]
@@ -404,31 +432,28 @@ def make_id_map_params(mapped_id_files):
             # Check if file not exists and raise error
             if not os.path.isfile(mapped_id_file):
                 message = 'File: %s for make_id_map_params() method not found.' % mapped_id_file
-                print(message)
                 raise FileNotFoundError(message)
             with open(mapped_id_file, 'r') as mapped_id_file_obj:
-                for line in mapped_id_file_obj:
+                for raw_line in mapped_id_file_obj:
                     # Skip Blank Lines
-                    line = line.strip()
+                    line = raw_line.strip()
                     if not line.split():
                         continue
                     # Skip Commented Lines
                     if line.startswith('#'):
                         continue
                     split_line = line.split(',')
-                    if len(split_line) != 2:
+                    if len(split_line) != 2:  # noqa: PLR2004
                         message = 'Error reading mapped-id line: '
-                        message += '\n%s\n%s' % (str(line), str(split_line))
+                        message += f'\n{line!s}\n{split_line!s}'
                         message += '\nTwo comma-separated entries expected.'
-                        print(message)
-                        raise RuntimeError(message)
+                        raise HybkitMiscError(message)
                     seq_id, seg_type = split_line
 
                     if seq_id in return_dict and seg_type != return_dict[seq_id]:
                         message = 'Conflicting types assigned for sequence id: %s\n' % seq_id
-                        message += '  %s  |  %s' % (return_dict[seq_id], seg_type)
-                        print(message)
-                        raise RuntimeError(message)
+                        message += f'  {return_dict[seq_id]}  |  {seg_type}'
+                        raise HybkitMiscError(message)
                     else:
                         return_dict[seq_id] = seg_type
 
@@ -436,7 +461,7 @@ def make_id_map_params(mapped_id_files):
 
     # TypeFinder : Private classmethods : find_seg_type
     @classmethod
-    def _reset(cls):
+    def _reset(cls) -> None:
         """
         Reset the class to its initial state.
 
diff --git a/hybkit/util.py b/hybkit/util.py
index 7f231bf..41b9683 100644
--- a/hybkit/util.py
+++ b/hybkit/util.py
@@ -5,22 +5,34 @@
 
 """This module contains helper functions for hybkit's command line scripts."""
 
+import argparse
+import copy
 import os
 import sys
-import argparse
 import textwrap
-import copy
+from typing import Any, List, Optional, Union
 
-sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
 from hybkit import settings, type_finder
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+from hybkit.errors import HybkitArgError
 
+# ----- Linting Directives:
+# ruff: noqa: F401 E402 SLF001
 
 # ----- Begin Argparse Helper Functions -----
 # Util : Argparse Helper Functions
-def get_argparse_doc(docstring):
+def get_argparse_doc(docstring: str) -> str:
     """
     Get the argparse description from a docstring.
 
@@ -48,7 +60,7 @@ def get_argparse_doc(docstring):
 
 
 # Util : Argparse Helper Functions
-def _bool_from_string(value):
+def _bool_from_string(value: Optional[Union[str, bool]]) -> bool:
     if isinstance(value, bool):
         return value
     if value.lower() in ('yes', 'true', 't', 'y', '1'):
@@ -72,7 +84,7 @@ def _bool_from_string(value):
 
 
 # Util : Path Helper Functions
-def dir_exists(dir_name):
+def dir_exists(dir_name: str) -> str:
     """
     Check if a directory exists at the provided path (else raise), and return a normalized path.
 
@@ -99,7 +111,7 @@ def dir_exists(dir_name):
 
 
 # Util : Path Helper Functions
-def file_exists(file_name, required_suffixes=[]):
+def file_exists(file_name: str, required_suffixes: Optional[List[str]]=None) -> str:
     """
     Check if a file exists at the provided path, and return a normalized path.
 
@@ -122,16 +134,15 @@ def file_exists(file_name, required_suffixes=[]):
         raise argparse.ArgumentTypeError(message)
 
     # If required_suffixes provided, ensure the file has a required suffix.
-    if required_suffixes:
-        if not any(file_name.endswith(suffix) for suffix in required_suffixes):
+    if (required_suffixes is not None
+        and not any(file_name.endswith(suffix) for suffix in required_suffixes)):
             message = ('Provided Input File: %s' % file_name
                        + ' does not have an allowed suffix.'
                        + ' {%s} ' % ', '.join(required_suffixes)
                        )
-            print(message)
             raise argparse.ArgumentTypeError(message)
 
-    # Normalize the file path
+    # Normalize the file patha
     file_name = os.path.normpath(file_name)
 
     # If global option set, then find the absolute path.
@@ -142,7 +153,7 @@ def file_exists(file_name, required_suffixes=[]):
 
 
 # Util : Path Helper Functions
-def hyb_exists(file_name):
+def hyb_exists(file_name: str) -> str:
     """
     Check if a .hyb file exists at the provided path, and return a normalized path.
 
@@ -160,7 +171,7 @@ def hyb_exists(file_name):
 
 
 # Util : Path Helper Functions
-def vienna_exists(file_name):
+def vienna_exists(file_name: str) -> str:
     """
     Check if a .vienna file exists at the provided path, and return a normalized path.
 
@@ -178,7 +189,7 @@ def vienna_exists(file_name):
 
 
 # Util : Path Helper Functions
-def ct_exists(file_name):
+def ct_exists(file_name: str) -> str:
     """
     Check if a .ct file exists at the provided path, and return a normalized path.
 
@@ -196,7 +207,7 @@ def ct_exists(file_name):
 
 
 # Util : Path Helper Functions
-def fold_exists(file_name):
+def fold_exists(file_name: str) -> str:
     """
     Check if a fold-representing file exists at the provided path, and return a normalized path.
 
@@ -214,7 +225,7 @@ def fold_exists(file_name):
 
 
 # Util : Path Helper Functions
-def out_path_exists(file_name):
+def out_path_exists(file_name: str) -> str:
     """
     Check if the directory of the specified output path exists, and return a normalized path.
 
@@ -239,8 +250,14 @@ def out_path_exists(file_name):
 
 
 # Util : Path Helper Functions
-def make_out_file_name(in_file_name, name_suffix='out', in_suffix='', out_suffix='',
-                       out_dir='', seg_sep='_'):
+def make_out_file_name(
+        in_file_name: str,
+        name_suffix: str = 'out',
+        in_suffix: str = '',
+        out_suffix: str = '',
+        out_dir: str = '',
+        seg_sep: str = '_'
+        ) -> str:
     """
     Given an input file name, generate an output file name.
 
@@ -279,7 +296,10 @@ def make_out_file_name(in_file_name, name_suffix='out', in_suffix='', out_suffix
 
 
 # Util : Path Helper Functions
-def validate_args(args, parser=None):
+def validate_args(
+        args: argparse.Namespace,
+        parser: Optional[argparse.ArgumentParser] = None
+        ) -> bool:
     """
     Check supplied arguments to make sure there are no hidden contradictions.
 
@@ -309,9 +329,9 @@ def validate_args(args, parser=None):
             message += 'The number of input hyb files and output hyb files provided '
             message += 'do not match. ( %i and %i )' % (len_in_hyb, len_out_hyb)
             message += '\n\nInput Files:\n    '
-            message += '\n    '.join([f for f in args.in_hyb])
+            message += '\n    '.join(list(args.in_hyb))
             message += '\n\nOutput Files:\n    '
-            message += '\n    '.join([f for f in args.out_hyb])
+            message += '\n    '.join(list(args.out_hyb))
             print(message + suffix)
             ret_val = False
 
@@ -323,9 +343,9 @@ def validate_args(args, parser=None):
             message += 'The number of input fold files and output fold files provided '
             message += 'do not match. ( %i and %i )' % (len_in_fold, len_out_fold)
             message += '\n\nInput Files:\n    '
-            message += '\n    '.join([f for f in args.in_fold])
+            message += '\n    '.join(list(args.in_fold))
             message += '\n\nOutput Files:\n    '
-            message += '\n    '.join([f for f in args.out_fold])
+            message += '\n    '.join(list(args.out_fold))
             print(message + suffix)
             ret_val = False
 
@@ -336,9 +356,9 @@ def validate_args(args, parser=None):
             message += 'The number of input hyb files and input fold files provided '
             message += 'do not match. ( %i and %i )' % (len_in_hyb, len_in_fold)
             message += '\n\nInput Files:\n    '
-            message += '\n    '.join([f for f in args.in_hyb])
+            message += '\n    '.join(list(args.in_hyb))
             message += '\n\nOutput Files:\n    '
-            message += '\n    '.join([f for f in args.in_fold])
+            message += '\n    '.join(list(args.in_fold))
             print(message + suffix)
             ret_val = False
 
@@ -346,7 +366,10 @@ def validate_args(args, parser=None):
 
 
 # Util : Path Helper Functions
-def validate_args_exit(args, parser=None):
+def validate_args_exit(
+        args: argparse.Namespace,
+        parser: Optional[argparse.ArgumentParser] = None
+        ) -> None:
     """
     Check supplied arguments using :func:`validate_args`, and exit if a conflict exists.
 
@@ -361,8 +384,10 @@ def validate_args_exit(args, parser=None):
 
 # ----- Begin Argparse Helper Classes -----
 # Util : Argparse Helper Formatter Class
-class _HybkitFormatter(argparse.ArgumentDefaultsHelpFormatter,
-                       argparse.RawDescriptionHelpFormatter):
+class _HybkitFormatter(
+    argparse.ArgumentDefaultsHelpFormatter,
+    argparse.RawDescriptionHelpFormatter,
+    ):
     pass
 
 
@@ -574,7 +599,7 @@ class _HybkitFormatter(argparse.ArgumentDefaultsHelpFormatter,
     Print version and exit.
     """
 )
-_arg_version_str = '%s  (hybkit API: %s)' % (__version__, __version__)
+_arg_version_str = f'{__version__}  (hybkit API: {__version__})'
 gen_opts_parser.add_argument(
     '--version', action='version', version='    %(prog)s ' + _arg_version_str,
     help=_this_arg_help,
@@ -950,7 +975,11 @@ class _HybkitFormatter(argparse.ArgumentDefaultsHelpFormatter,
 
 
 # ------ Begin Settings Manipulation Functions ------
-def set_setting(setting, set_value, verbose=False):
+def set_setting(
+        setting: str,
+        set_value: Any,  # noqa: ANN401
+        verbose: bool = False
+        ) -> str:
     """
     Take a namespace object as from an argparse parser and update settings.
 
@@ -988,20 +1017,20 @@ def set_setting(setting, set_value, verbose=False):
                 if do_check_list:
                     for check_value in set_value:
                         if check_value not in choices:
-                            message = 'Invalid value for %s setting: %s' % (setting, check_value)
+                            message = f'Invalid value for {setting} setting: {check_value}'
                             message += '\nChoices are: %s' % str(choices)
-                            raise RuntimeError(message)
+                            raise HybkitArgError(message)
                 elif set_value not in choices:
-                    message = 'Invalid value for %s setting: %s' % (setting, set_value)
+                    message = f'Invalid value for {setting} setting: {set_value}'
                     message += '\nChoices are: %s' % str(choices)
-                    raise RuntimeError(message)
+                    raise HybkitArgError(message)
             if old_setting is not None and set_value != old_setting:
                 out_report += 'Setting %s Setting: ' % class_name
-                out_report += '"%s" to "%s"\n' % (setting, str(set_value))
+                out_report += f'"{setting}" to "{set_value!s}"\n'
                 cls_settings[setting] = set_value
     if not setting_found:
         message = 'Setting "%s" not found' % setting
-        raise RuntimeError(message)
+        raise HybkitArgError(message)
     if verbose and out_report.strip():
         print(out_report)
     if out_report:
@@ -1009,7 +1038,10 @@ def set_setting(setting, set_value, verbose=False):
     return out_report
 
 
-def set_settings_from_namespace(nspace, verbose=False):
+def set_settings_from_namespace(
+        nspace: argparse.Namespace,
+        verbose: bool = False,
+        ) -> None:
     """
     Take a namespace object as from an argparse parser and update settings.
 
@@ -1022,7 +1054,7 @@ def set_settings_from_namespace(nspace, verbose=False):
     out_report = '\n'
     for class_name in ['HybRecord', 'HybFile', 'FoldRecord',
                        'FoldFile', 'HybFoldIter', 'Analysis']:
-        cls_settings_info = getattr(settings, class_name + '_settings_info')
+        _cls_settings_info = getattr(settings, class_name + '_settings_info')
         cls_settings = getattr(settings, class_name + '_settings')
         for setting_key in cls_settings:
             if hasattr(nspace, setting_key):
diff --git a/requirements.txt b/requirements.txt
index 3bda540..1a74bab 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
 biopython>=1.79
 matplotlib>=3.7.1
+typing_extensions>=4.8.0
diff --git a/scripts/hyb_analyze b/scripts/hyb_analyze
index fe270e1..eb49906 100755
--- a/scripts/hyb_analyze
+++ b/scripts/hyb_analyze
@@ -3,9 +3,8 @@
 # Renne Lab, University of Florida
 # Hybkit Project : http://www.github.com/RenneLab/hybkit
 
-"""
-Read one or more hyb format and corresponding vienna or ct format files
-and analyze the fold information in the contained hybrid sequences.
+r"""
+Read hyb / vienna files and analyze the fold information in the contained hybrid sequences.
 
 Analysis Types:
 
@@ -36,18 +35,34 @@ Example system calls:
                     --make_plots False
 """
 
-import os
 import argparse
 import contextlib
-import hybkit
-
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
+import os
+from typing import List, Optional, Union
 
+import hybkit
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+
+# ----- Linting Directives:
+# ruff: noqa: F401 SLF001
+
+# ----- Begin Custom Types -----
+AnalysisTypesArg = Union[str, List[str]]
 
 # Create Command-line Argument Parser
-def make_parser():
+def make_parser() -> argparse.ArgumentParser:
+    """Create the command-line argument parser for the hyb_analyze script."""
     parser_components = [
         hybkit.util.in_hybs_parser,
         hybkit.util.in_folds_parser,
@@ -73,18 +88,19 @@ def make_parser():
 
 
 # Define main script function.
-def hyb_analyze(in_hyb_files,
-                analysis_types,
-                in_fold_files=None,
-                out_dir='.',
-                out_suffix=None,
-                out_basenames=None,
-                analysis_name=None,
-                make_plots=False,
-                verbose=False, silent=False
-                ):
+def hyb_analyze(
+        in_hyb_files: List[str],
+        analysis_types: AnalysisTypesArg,
+        in_fold_files: Optional[List[str]] = None,
+        out_dir: str = '.',
+        out_suffix: Optional[str] = None,
+        out_basenames: Optional[List[str]] = None,
+        analysis_name: Optional[str] = None,
+        make_plots: bool = False,
+        verbose: bool = False,
+        silent: bool = False,
+        ) -> None:
     """Perform main script function."""
-
     if not silent:
         print('\nPerforming Analysis of Hyb Files...')
 
@@ -116,7 +132,7 @@ def hyb_analyze(in_hyb_files,
         else:
             in_hyb_file, in_fold_file = use_files, None
         file_basename = os.path.basename(in_hyb_file)
-        file_label = file_basename.replace('.hyb', '')
+        _file_label = file_basename.replace('.hyb', '')
 
         if out_basenames is not None:
             out_basename = out_basenames[i]
@@ -132,15 +148,15 @@ def hyb_analyze(in_hyb_files,
 
         if in_fold_file is not None:
             if any(in_fold_file.endswith(s) for s in hybkit.settings.VIENNA_SUFFIXES):
-                InFoldClass = hybkit.ViennaFile
+                in_fold_class = hybkit.ViennaFile
             elif any(in_fold_file.endswith(s) for s in hybkit.settings.CT_SUFFIXES):
-                InFoldClass = hybkit.CtFile
+                in_fold_class = hybkit.CtFile
             else:
                 raise ValueError('Unrecognized fold file type: %s' % in_fold_file)
             in_fold_args = (in_fold_file, 'r')
         else:
-            InFoldClass = contextlib.nullcontext
-            in_fold_args = tuple()
+            in_fold_class = contextlib.nullcontext
+            in_fold_args = ()
 
         if verbose:
             print('Analyzing Files:')
@@ -151,13 +167,13 @@ def hyb_analyze(in_hyb_files,
 
         # Start Record Iteration
         with hybkit.HybFile(in_hyb_file, 'r') as in_hyb, \
-             InFoldClass(*in_fold_args) as in_fold:
+             in_fold_class(*in_fold_args) as in_fold:
             if in_fold_file is None:
                 record_iter = in_hyb
             else:
                 record_iter = hybkit.HybFoldIter(in_hyb, in_fold, combine=True)
 
-            for i, hyb_record in enumerate(record_iter, start=1):
+            for _, hyb_record in enumerate(record_iter, start=1):
                 combined_analysis.add_hyb_record(hyb_record)
 
         combined_analysis.write_analysis_results_special(out_basename=out_basename)
diff --git a/scripts/hyb_check b/scripts/hyb_check
index 6fb523e..1602b81 100755
--- a/scripts/hyb_check
+++ b/scripts/hyb_check
@@ -3,7 +3,7 @@
 # Renne Lab, University of Florida
 # Hybkit Project : http://www.github.com/RenneLab/hybkit
 
-"""
+r"""
 Read one or more hyb (and optional fold) format files and check for errors.
 
 This utility reads in one or more files in hyb-format
@@ -21,18 +21,31 @@ Example system calls:
 
 """
 
-import os
 import argparse
 import contextlib
-import hybkit
-
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
+import os
+from typing import List, Optional, Union
 
+import hybkit
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+
+# ----- Linting Directives:
+# ruff: noqa: F401 SLF001
 
 # Create Command-line Argument Parser
-def make_parser():
+def make_parser() -> argparse.ArgumentParser:
+    """Create and return the argparse.ArgumentParser for the hyb_check script."""
     parser_components = [
         hybkit.util.in_hybs_parser,
         hybkit.util.in_folds_parser,
@@ -53,13 +66,13 @@ def make_parser():
 
 
 # Define main script function.
-def hyb_check(in_hyb_files,
-              in_fold_files,
-              verbose=False,
-              silent=False,
-              ):
+def hyb_check(
+        in_hyb_files: List[str],
+        in_fold_files: Optional[List[str]] = None,
+        verbose: bool = False,
+        silent: bool = False,
+        ) -> None:
     """Perform main script function."""
-
     if not silent:
         print('\nChecking Hyb and Fold Files...')
 
@@ -69,25 +82,25 @@ def hyb_check(in_hyb_files,
     else:
         file_iter = in_hyb_files
 
-    for i, use_files in enumerate(file_iter):
+    for _i, use_files in enumerate(file_iter):
         if in_fold_files:
             in_hyb_file, in_fold_file = use_files
         else:
             in_hyb_file, in_fold_file = use_files, None
         file_basename = os.path.basename(in_hyb_file)
-        file_label = file_basename.replace('.hyb', '')
+        _file_label = file_basename.replace('.hyb', '')
 
         if in_fold_file is not None:
             if any(in_fold_file.endswith(s) for s in hybkit.settings.VIENNA_SUFFIXES):
-                InFoldClass = hybkit.ViennaFile
+                in_fold_class = hybkit.ViennaFile
             elif any(in_fold_file.endswith(s) for s in hybkit.settings.CT_SUFFIXES):
-                InFoldClass = hybkit.CtFile
+                in_fold_class = hybkit.CtFile
             else:
                 raise ValueError('Unrecognized fold file type: %s' % in_fold_file)
             in_fold_args = (in_fold_file, 'r')
         else:
-            InFoldClass = contextlib.nullcontext
-            in_fold_args = tuple()
+            in_fold_class = contextlib.nullcontext
+            in_fold_args = ()
 
         if verbose:
             print('Checking Files:')
@@ -97,13 +110,13 @@ def hyb_check(in_hyb_files,
 
         # Start Record Iteration
         with hybkit.HybFile(in_hyb_file, 'r') as in_hyb, \
-             InFoldClass(*in_fold_args) as in_fold:
+             in_fold_class(*in_fold_args) as in_fold:
             if in_fold_file is None:
                 record_iter = in_hyb
             else:
                 record_iter = hybkit.HybFoldIter(in_hyb, in_fold, combine=True)
 
-            for i, hyb_record in enumerate(record_iter, start=1):
+            for _j, _hyb_record in enumerate(record_iter, start=1):
                 pass
 
     if verbose:
diff --git a/scripts/hyb_eval b/scripts/hyb_eval
index dd11434..e4002a6 100755
--- a/scripts/hyb_eval
+++ b/scripts/hyb_eval
@@ -3,7 +3,7 @@
 # Renne Lab, University of Florida
 # Hybkit Project : http://www.github.com/RenneLab/hybkit
 
-"""
+r"""
 Read hyb files (and optional matched fold files) and evaluate the contained hybrids.
 
 This utility reads in one or more files in hyb-format
@@ -66,18 +66,32 @@ Evaluation Types:
                         --mirna_types miRNA kshv-miRNA
 """
 
-import os
 import argparse
 import contextlib
-import hybkit
-
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
+import os
+from typing import List, Optional, Union
 
+import hybkit
+from hybkit import HybkitMiscError
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+
+# ----- Linting Directives:
+# ruff: noqa: F401 SLF001
 
 # Create Command-line Argument Parser
-def make_parser():
+def make_parser() -> argparse.ArgumentParser:
+    """Create the command-line argument parser for the hyb_eval script."""
     parser_components = [
         hybkit.util.cmb_hyb_fold_io_parser,
         hybkit.util.cmb_out_opts_parser,
@@ -102,22 +116,22 @@ def make_parser():
 
 
 # Define main script function.
-def hyb_eval(in_hyb_files,
-             eval_types,
-             out_dir='.',
-             in_fold_files=None,
-             out_hyb_files=None,
-             out_fold_files=None,
-             out_suffix=hybkit.settings._EVAL_OUT_SUFFIX,
-             type_method=None,
-             type_params=None,
-             type_params_file=None,
-             set_dataset=None,
-             verbose=False,
-             silent=False,
-             ):
+def hyb_eval(
+        in_hyb_files: List[str],
+        eval_types: Union[str, List[str]],
+        out_dir: str = '.',
+        in_fold_files: Optional[List[str]] = None,
+        out_hyb_files: Optional[List[str]] = None,
+        out_fold_files: Optional[List[str]] = None,
+        out_suffix: str = hybkit.settings._EVAL_OUT_SUFFIX,
+        type_method: Optional[str] = None,
+        type_params: Optional[dict] = None,
+        type_params_file: Optional[str] = None,
+        set_dataset: Optional[str] = None,
+        verbose: bool = False,
+        silent: bool = False,
+        ) -> None:
     """Perform main script function."""
-
     if not silent:
         print('\nPerforming Evaluation of Hyb and Fold Files...')
 
@@ -126,7 +140,7 @@ def hyb_eval(in_hyb_files,
     do_mirna = ('mirna' in eval_types)
 
     if verbose:
-        print('\nPerforming Evaluation Types: ' + ', '.join([x for x in eval_types]))
+        print('\nPerforming Evaluation Types: ' + ', '.join(list(eval_types)))
         print()
 
     # Prepare for type eval.
@@ -139,18 +153,16 @@ def hyb_eval(in_hyb_files,
         if type_method != make_parser().get_default('type_method'):
             if verbose:
                 print('Setting non-default type finding method: %s' % type_method)
-            params = {}
             params_method_str = hybkit.HybRecord.TypeFinder.param_methods[type_method]
             if params_method_str is not None:
-                params_method = getattr(hybkit.HybRecord.TypeFinder, params_method_str)
-                if type_params is None:
+                param_method = getattr(hybkit.HybRecord.TypeFinder, params_method_str)
+                if type_params is None:  # noqa: SIM102
                     if hybkit.HybRecord.TypeFinder.param_methods_needs_file[type_method]:
                         if type_params_file is None:
                             message = 'Type-Finding Parameter Method: %s ' % type_method
                             message += 'requires an input file.\nPlease set type_params or '
                             message += 'type_params_file setting.'
-                            print(message)
-                            raise Exception(message)
+                            raise HybkitMiscError(message)
                         if verbose:
                             print('Using type parameter file: %s' % type_params_file)
                         param_method_str = hybkit.HybRecord.TypeFinder.param_methods[type_method]
@@ -160,7 +172,7 @@ def hyb_eval(in_hyb_files,
                     print()
             hybkit.HybRecord.TypeFinder.set_method(type_method, type_params)
 
-    if do_mirna:
+    if do_mirna:  # noqa: SIM102
         if verbose:
             print('Assigning types as miRNA:')
             print('   ', ', '.join(hybkit.settings.HybRecord_settings['mirna_types']), '\n')
@@ -200,19 +212,19 @@ def hyb_eval(in_hyb_files,
 
         if in_fold_file is not None:
             if any(in_fold_file.endswith(s) for s in hybkit.settings.VIENNA_SUFFIXES):
-                InFoldClass = hybkit.ViennaFile
+                in_fold_class = hybkit.ViennaFile
             elif any(in_fold_file.endswith(s) for s in hybkit.settings.CT_SUFFIXES):
-                InFoldClass = hybkit.CtFile
+                in_fold_class = hybkit.CtFile
             else:
                 raise ValueError('Unrecognized fold file type: %s' % in_fold_file)
-            OutFoldClass = hybkit.ViennaFile
+            out_fold_class = hybkit.ViennaFile
             in_fold_args = (in_fold_file, 'r')
             out_fold_args = (out_fold_file, 'w')
         else:
-            InFoldClass = contextlib.nullcontext
-            OutFoldClass = contextlib.nullcontext
-            in_fold_args = tuple()
-            out_fold_args = tuple()
+            in_fold_class = contextlib.nullcontext
+            out_fold_class = contextlib.nullcontext
+            in_fold_args = ()
+            out_fold_args = ()
 
         if verbose:
             print('Evaluating Files:')
@@ -223,20 +235,17 @@ def hyb_eval(in_hyb_files,
             if in_fold_files:
                 print('    Output Fold: ' + out_fold_file)
 
-        include_count = 0
-        exclude_count = 0
-
         # Start Record Iteration
         with hybkit.HybFile(in_hyb_file, 'r') as in_hyb, \
              hybkit.HybFile(out_hyb_file, 'w') as out_hyb, \
-             InFoldClass(*in_fold_args) as in_fold, \
-             OutFoldClass(*out_fold_args) as out_fold:
+             in_fold_class(*in_fold_args) as in_fold, \
+             out_fold_class(*out_fold_args) as out_fold:
             if in_fold_file is None:
                 record_iter = in_hyb
             else:
                 record_iter = hybkit.HybFoldIter(in_hyb, in_fold, combine=True)
 
-            for i, hyb_record in enumerate(record_iter, start=1):
+            for _i, hyb_record in enumerate(record_iter, start=1):
                 if set_dataset:
                     hyb_record.set_flag('dataset', file_label)
                 if do_type:
diff --git a/scripts/hyb_filter b/scripts/hyb_filter
index 9898782..1ecc7e4 100755
--- a/scripts/hyb_filter
+++ b/scripts/hyb_filter
@@ -3,7 +3,7 @@
 # Renne Lab, University of Florida
 # Hybkit Project : http://www.github.com/RenneLab/hybkit
 
-"""
+r"""
 Filter hyb (and corresponding fold) files to meet (or exclude) specific criteria.
 
 This script takes one or more filter and/or exclusion criteria
@@ -62,19 +62,32 @@ Example System Calls (match ANY criteria):
         #   either "miRNA" or "lncRNA" (case-sensitive)
 """
 
-import sys
-import os
 import argparse
-import hybkit
 import contextlib
+import os
+import sys
+from typing import List, Literal, Optional, Tuple
 
-# Import module-level dunder-names:
-from hybkit.__about__ import (__author__, __contact__, __credits__, __date__, __deprecated__,
-                              __email__, __license__, __maintainer__, __status__, __version__)
-
+import hybkit
+from hybkit.__about__ import (
+    __author__,
+    __contact__,
+    __credits__,
+    __date__,
+    __deprecated__,
+    __email__,
+    __license__,
+    __maintainer__,
+    __status__,
+    __version__,
+)
+
+# ----- Linting Directives:
+# ruff: noqa: F401 SLF001
 
 # Create Command-line Argument Parser
-def make_parser():
+def make_parser() -> argparse.ArgumentParser:
+    """Create and return the argparse.ArgumentParser for the hyb_filter script."""
     parser_components = [
         hybkit.util.cmb_hyb_fold_io_parser,
         hybkit.util.cmb_out_opts_parser,
@@ -99,23 +112,23 @@ def make_parser():
 
 
 # Define main script function.
-def hyb_filter(in_hyb_files,
-               filter_params,
-               exclude_params,
-               in_fold_files=None,
-               filter_mode='all',
-               out_dir='.',
-               out_suffix=hybkit.settings._FILTER_OUT_SUFFIX,
-               out_hyb_files=None,
-               out_fold_files=None,
-               set_dataset=None,
-               skip_dup_id_before=False,
-               skip_dup_id_after=False,
-               verbose=False,
-               silent=False,
-               ):
+def hyb_filter(
+        in_hyb_files: List[str],
+        filter_params: List[Tuple[str, str]],
+        exclude_params: List[Tuple[str, str]],
+        in_fold_files: Optional[List[str]] = None,
+        filter_mode: Literal['all', 'any'] = 'all',
+        out_dir: str = '.',
+        out_suffix: str = hybkit.settings._FILTER_OUT_SUFFIX,
+        out_hyb_files: Optional[List[str]] = None,
+        out_fold_files: Optional[List[str]] = None,
+        set_dataset: Optional[str] = None,
+        skip_dup_id_before: bool = False,
+        skip_dup_id_after: bool = False,
+        verbose: bool = False,
+        silent: bool = False,
+        ) -> None:
     """Perform main script function."""
-
     if not silent:
         print('\nFiltering Hyb Files...')
 
@@ -127,7 +140,7 @@ def hyb_filter(in_hyb_files,
 
         if exclude_params:
             print('Using Exclusion Filter Parameters:')
-            print('    ' + '\n    '.join((' '.join(p) for p in exclude_params)) + '\n')
+            print('    ' + '\n    '.join(' '.join(p) for p in exclude_params) + '\n')
 
     allowed_modes = {'all', 'any'}
     if filter_mode not in allowed_modes:
@@ -171,19 +184,19 @@ def hyb_filter(in_hyb_files,
 
         if in_fold_file is not None:
             if any(in_fold_file.endswith(s) for s in hybkit.settings.VIENNA_SUFFIXES):
-                InFoldClass = hybkit.ViennaFile
+                in_fold_class = hybkit.ViennaFile
             elif any(in_fold_file.endswith(s) for s in hybkit.settings.CT_SUFFIXES):
-                InFoldClass = hybkit.CtFile
+                in_fold_class = hybkit.CtFile
             else:
                 raise ValueError('Unrecognized fold file type: %s' % in_fold_file)
-            OutFoldClass = hybkit.ViennaFile
+            out_fold_class = hybkit.ViennaFile
             in_fold_args = (in_fold_file, 'r')
             out_fold_args = (out_fold_file, 'w')
         else:
-            InFoldClass = contextlib.nullcontext
-            OutFoldClass = contextlib.nullcontext
-            in_fold_args = tuple()
-            out_fold_args = tuple()
+            in_fold_class = contextlib.nullcontext
+            out_fold_class = contextlib.nullcontext
+            in_fold_args = ()
+            out_fold_args = ()
 
         if verbose:
             print('Filtering File:')
@@ -199,8 +212,8 @@ def hyb_filter(in_hyb_files,
 
         with hybkit.HybFile(in_hyb_file, 'r') as in_hyb, \
              hybkit.HybFile(out_hyb_file, 'w') as out_hyb, \
-             InFoldClass(*in_fold_args) as in_fold, \
-             OutFoldClass(*out_fold_args) as out_fold:
+             in_fold_class(*in_fold_args) as in_fold, \
+             out_fold_class(*out_fold_args) as out_fold:
             if in_fold_file is None:
                 record_iter = in_hyb
             else:
@@ -208,7 +221,7 @@ def hyb_filter(in_hyb_files,
 
             last_record_id = None
 
-            for i, hyb_record in enumerate(record_iter, start=1):
+            for _i, hyb_record in enumerate(record_iter, start=1):
                 if skip_dup_id_before:
                     if hyb_record.id == last_record_id:
                         continue
@@ -273,7 +286,7 @@ if __name__ == '__main__':
     filter_params = []
     for param_set in [getattr(args, key) for key in ['filter', 'filter_2', 'filter_3']]:
         if param_set is not None and bool(param_set):
-            if len(param_set) > 2:
+            if len(param_set) > 2:  # noqa: PLR2004
                 print('Invalid Filtering Parameter Set Encountered: %s ' % str(param_set))
                 print('Parameter sets should have 1 or 2 arguments.\n')
                 sys.exit()
@@ -284,7 +297,7 @@ if __name__ == '__main__':
     exclude_params = []
     for param_set in [getattr(args, key) for key in ['exclude', 'exclude_2', 'exclude_3']]:
         if param_set is not None and bool(param_set):
-            if len(param_set) > 2:
+            if len(param_set) > 2:  # noqa: PLR2004
                 print('Invalid Exclusion Filtering Parameter Set Encountered: '
                       + '%s ' % str(param_set))
                 print('Parameter sets should have 1 or 2 arguments.\n')
diff --git a/setup.py b/setup.py
index 84221d0..9aef56b 100644
--- a/setup.py
+++ b/setup.py
@@ -5,13 +5,15 @@
 
 """Setup module for the hybkit project."""
 
-import setuptools
-import os
-import hybkit
-import glob
 import fnmatch
+import glob
+import os
 import pprint
 
+import setuptools
+
+# import hybkit
+
 # Set project directory
 proj_dir = os.path.abspath(os.path.dirname(__file__))
 
@@ -25,7 +27,7 @@
 # Get the remaining project details variables from the "__about__.py" file.
 about_vars = {}
 with open(os.path.join(proj_dir, 'hybkit', '__about__.py')) as f:
-    exec(f.read(), about_vars)
+    exec(f.read(), about_vars)  #noqa: S102
 
 # Dynamically generate reference data file tuples:
 data_files = []
@@ -37,12 +39,12 @@
         data_file_dirs.append(item)
 
 ignore_file_patterns = []
-with open('.gitignore', 'r') as git_ignore:
+with open('.gitignore') as git_ignore:
     for line in git_ignore:
-        line = line.strip()
-        if line.startswith('#') or not line:
+        use_line = line.strip()
+        if use_line.startswith('#') or not use_line:
             continue
-        ignore_file_patterns.append(line)
+        ignore_file_patterns.append(use_line)
 
 for dir_name in data_file_dirs:
     file_list = [f for f in glob.glob(os.path.join(dir_name, '*'))


Test patch:
diff --git a/auto_tests/auto_test.sh b/auto_tests/auto_test.sh
index faf0bc0..0516d41 100755
--- a/auto_tests/auto_test.sh
+++ b/auto_tests/auto_test.sh
@@ -57,14 +57,13 @@ hyb_filter -i "${OUT_DIR}/${IN_HYB/.hyb/_evaluated.hyb}" \
            --exclude_2 any_seg_type_is mitoch-rRNA \
 
 for mode in "energy" "type" "mirna" "target" "fold" "energy type mirna target fold"; do
-
-hyb_analyze -i "${OUT_DIR}/${IN_HYB/.hyb/_evaluated_filtered.hyb}" --verbose \
-            -f "${OUT_DIR}/${IN_HYB/.hyb/_evaluated_filtered.vienna}" \
-            --out_dir "${OUT_DIR}" \
-            --analysis_types ${mode} \
-            --analysis_name "TEST_FOLD" \
-            --seq_type dynamic \
-            --allowed_mismatches 0
+  hyb_analyze -i "${OUT_DIR}/${IN_HYB/.hyb/_evaluated_filtered.hyb}" --verbose \
+              -f "${OUT_DIR}/${IN_HYB/.hyb/_evaluated_filtered.vienna}" \
+              --out_dir "${OUT_DIR}" \
+              --analysis_types ${mode} \
+              --analysis_name "TEST_FOLD" \
+              --seq_type dynamic \
+              --allowed_mismatches 0
 
 done
 
diff --git a/auto_tests/test_Analyses.py b/auto_tests/test_Analyses.py
index f09745a..3e9c56d 100755
--- a/auto_tests/test_Analyses.py
+++ b/auto_tests/test_Analyses.py
@@ -7,27 +7,26 @@
 Automatic testing of hybkit code.
 """
 
+import os
 
-import copy
-from contextlib import nullcontext as does_not_raise
 import pytest
-# mport hybkit
 
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, hyb_file_name
+import hybkit
+from auto_tests.test_helper_data import (
+    ART_HYB_PROPS_ALL,
+    ART_HYB_VIENNA_PROPS_1,
+    ART_HYB_VIENNA_PROPS_2,
+    HALF,
+    NEG_10,
+    ONE,
+    ZERO,
+)
+from hybkit.errors import HybkitArgError, HybkitError
 
+# from auto_tests.test_helper_functions import ()
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
-
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 # ----- Begin Analysis Tests -----
 # ----- Test Analysis Class Standard energy, type, and mirna Analyses -----
@@ -37,8 +36,9 @@
 ]
 
 
-@pytest.mark.parametrize("test_name,individual_add", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'individual_add'), [*test_parameters])
 def test_analysis_hyb(test_name, individual_add, tmp_path):
+    """Test Analysis class with standard energy, type, and mirna analyses."""
     hyb_autotest_file_path = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     test_hyb_strs = [props['hyb_str'] for props in ART_HYB_PROPS_ALL]
     test_hyb_strs_dup = []
@@ -70,11 +70,11 @@ def test_analysis_hyb(test_name, individual_add, tmp_path):
     # Check energy results
     assert all_results['energy']['energy_analysis_count'] == record_count
     assert all_results['energy']['has_energy_val'] == record_count
-    assert all_results['energy']['no_energy_val'] == 0
-    assert all_results['energy']['energy_min'] == -10.0
-    assert all_results['energy']['energy_max'] == -10.0
-    assert all_results['energy']['energy_mean'] == -10.0
-    assert all_results['energy']['energy_std'] == 0.0
+    assert all_results['energy']['no_energy_val'] == ZERO
+    assert all_results['energy']['energy_min'] == NEG_10
+    assert all_results['energy']['energy_max'] == NEG_10
+    assert all_results['energy']['energy_mean'] == NEG_10
+    assert all_results['energy']['energy_std'] == ZERO
     assert all_results['energy']['binned_energy_vals'][-10] == record_count
 
     # Check type results
@@ -139,8 +139,8 @@ def test_analysis_hyb(test_name, individual_add, tmp_path):
 
     # Get Analysis Delim String:
     out_analysis_file_name = os.path.join(tmp_path, 'analysis_delim_str.csv')
-    mirna_analysis_delim_str = hyb_analysis.get_analysis_delim_str(analysis='mirna')
-    analysis_delim_str = hyb_analysis.get_analysis_delim_str()
+    mirna_analysis_delim_str = hyb_analysis.get_analysis_delim_str(analysis='mirna')  # noqa: F841
+    analysis_delim_str = hyb_analysis.get_analysis_delim_str()  # noqa: F841
 
     # Execution-only writing tests
     hyb_analysis.write_analysis_delim_str(analysis='mirna', out_file_name=out_analysis_file_name)
@@ -162,40 +162,41 @@ def test_analysis_hyb(test_name, individual_add, tmp_path):
     hyb_analysis.plot_analysis_results(out_basename=out_special_file_base)
 
     # Check erroring on bad results requests:
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis.get_analysis_results('bad_analysis')
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis.get_specific_result('bad_result')
 
 
 # ----- Test Analysis Class Standard energy, type, and mirna Analyses -----
 def test_analysis_problems(tmp_path):
+    """Test Analysis class with standard energy, type, and mirna analyses."""
     hyb_autotest_file_path = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     test_hyb_strs = [props['hyb_str'] for props in ART_HYB_PROPS_ALL]
     test_hyb_strs_dup = []
     for test_hyb_str in test_hyb_strs:
         test_hyb_strs_dup += ([test_hyb_str] * 15)
-    record_count = len(test_hyb_strs_dup)
+    record_count = len(test_hyb_strs_dup)  # noqa: F841
     combined_hyb_strs = ''.join(test_hyb_strs_dup)
     with open(hyb_autotest_file_path, 'w') as hyb_autotest_file:
         hyb_autotest_file.write(combined_hyb_strs)
 
     # Test raise error with no analysis
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis = hybkit.analysis.Analysis(
             analysis_types=[],
             name='test_analysis',
         )
 
     # Test raise error with bad analysis
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis = hybkit.analysis.Analysis(
             analysis_types=['bad_analysis'],
             name='test_analysis',
         )
 
     # Test raise error with non-string analysis
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis = hybkit.analysis.Analysis(
             analysis_types=[1],
             name='test_analysis',
@@ -207,20 +208,20 @@ def test_analysis_problems(tmp_path):
     )
 
     # Test raise error if no energy values
-    with pytest.raises(RuntimeError):
-        with hybkit.HybFile(hyb_autotest_file_path, 'r') as hyb_file:
+    with pytest.raises(HybkitError):
+        with hybkit.HybFile(hyb_autotest_file_path) as hyb_file:
             for hyb_record in hyb_file:
                 hyb_record.energy = None
                 hyb_analysis.add_hyb_record(hyb_record)
 
     # Test raise error if no type values
-    with pytest.raises(RuntimeError):
-        with hybkit.HybFile(hyb_autotest_file_path, 'r') as hyb_file:
+    with pytest.raises(HybkitError):
+        with hybkit.HybFile(hyb_autotest_file_path) as hyb_file:
             for hyb_record in hyb_file:
                 hyb_analysis.add_hyb_record(hyb_record)
 
     # Test raise error if no mirna values
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitError):
         with hybkit.HybFile(hyb_autotest_file_path, 'r') as hyb_file:
             for hyb_record in hyb_file:
                 hyb_record.eval_types()
@@ -231,18 +232,18 @@ def test_analysis_problems(tmp_path):
         analysis_types=['target'],
         name='test_analysis',
     )
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitError):
         with hybkit.HybFile(hyb_autotest_file_path, 'r') as hyb_file:
             for hyb_record in hyb_file:
                 hyb_record.eval_types()
                 hyb_analysis.add_hyb_record(hyb_record)
 
     # Test raise error for bad detail request
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis.get_specific_result('bad_result')
 
     # Test raise error for inactive detail request
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitArgError):
         hyb_analysis.get_specific_result('fold_match_counts')
 
     # Test raise error if no fold_record values
@@ -250,7 +251,7 @@ def test_analysis_problems(tmp_path):
         analysis_types=['mirna', 'fold'],
         name='test_analysis',
     )
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitError):
         with hybkit.HybFile(hyb_autotest_file_path, 'r') as hyb_file:
             for hyb_record in hyb_file:
                 hyb_record.eval_types()
@@ -260,6 +261,7 @@ def test_analysis_problems(tmp_path):
 
 # ----- Test Analysis Class Standard fold Analysis -----
 def test_analysis_fold(tmp_path):
+    """Test Analysis class with standard fold analysis."""
     hyb_autotest_file_path = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     vienna_autotest_file_path = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     all_hyb_vienna_props = [ART_HYB_VIENNA_PROPS_1, ART_HYB_VIENNA_PROPS_2]
@@ -283,8 +285,8 @@ def test_analysis_fold(tmp_path):
         analysis_types='fold',
         name='test_analysis',
     )
-    with open(hyb_autotest_file_path, 'r') as hyb_autotest_file, \
-         open(vienna_autotest_file_path, 'r') as vienna_autotest_file:
+    with open(hyb_autotest_file_path) as hyb_autotest_file, \
+         open(vienna_autotest_file_path) as vienna_autotest_file:
         assert len(hyb_autotest_file.readlines()) == record_count
         assert len(vienna_autotest_file.readlines()) == record_count * 3
 
@@ -317,14 +319,14 @@ def test_analysis_fold(tmp_path):
     }
     assert fold_results['mirna_nt_fold_counts'][4] == record_count
     assert fold_results['mirna_nt_fold_counts'][24] == (record_count / 2)
-    assert fold_results['mirna_nt_fold_props'][4] == 1.0
-    assert fold_results['mirna_nt_fold_props'][24] == 0.5
+    assert fold_results['mirna_nt_fold_props'][4] == ONE
+    assert fold_results['mirna_nt_fold_props'][24] == HALF
 
     # Execution-only writing tests
     # Get Analysis Delim String:
     out_analysis_file_name = os.path.join(tmp_path, 'analysis_delim_str.csv')
-    fold_analysis_delim_str = hyb_analysis.get_analysis_delim_str(analysis='fold')
-    analysis_delim_str = hyb_analysis.get_analysis_delim_str()
+    fold_analysis_delim_str = hyb_analysis.get_analysis_delim_str(analysis='fold')  # noqa: F841
+    analysis_delim_str = hyb_analysis.get_analysis_delim_str()  # noqa: F841
     hyb_analysis.write_analysis_delim_str(analysis='fold', out_file_name=out_analysis_file_name)
     hyb_analysis.write_analysis_delim_str(analysis=['fold'], out_file_name=out_analysis_file_name)
     hyb_analysis.write_analysis_delim_str(out_file_name=out_analysis_file_name)
diff --git a/auto_tests/test_CtFile.py b/auto_tests/test_CtFile.py
index 845c420..4bd49db 100755
--- a/auto_tests/test_CtFile.py
+++ b/auto_tests/test_CtFile.py
@@ -7,29 +7,14 @@
 Automatic testing of hybkit CtFile Class.
 """
 
-import os
-import sys
-import copy
-from contextlib import nullcontext as does_not_raise
-import argparse
-import pytest
-import hybkit
-
-
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, Ct_autotest_file_name, hyb_file_name
-
-
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+# from contextlib import nullcontext as does_not_raise
+# import pytest
+# import hybkit
+# from auto_tests.test_helper_data import ART_HYB_Ct_PROPS_1, ART_HYB_Ct_PROPS_2
+# from auto_tests.test_helper_functions import ()
+
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 # ----- Start CtFile Test IO of Ct Strings / FoldRecords -----
 # TODO: Implement CT File Tests
diff --git a/auto_tests/test_FoldFile.py b/auto_tests/test_FoldFile.py
index bf9e852..5338c5f 100755
--- a/auto_tests/test_FoldFile.py
+++ b/auto_tests/test_FoldFile.py
@@ -8,28 +8,17 @@
 """
 
 import os
-import sys
-import copy
-from contextlib import nullcontext as does_not_raise
-import argparse
-import pytest
-import hybkit
-
 
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, vienna_autotest_file_name, hyb_file_name
+# import sys
+# import copy
+# from contextlib import nullcontext as does_not_raise
+# import argparse
+import pytest
 
+import hybkit
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+# from auto_tests.test_helper_data import ()
+# from auto_tests.test_helper_functions import ()
 
 hybkit.util.set_setting('error_mode', 'raise')
 hybkit.util.set_setting('iter_error_mode', 'raise')
@@ -38,6 +27,7 @@
 # ----- Begin FoldFile Tests -----
 # ----- Test FoldFile Base Class Misc -----
 def test_foldfile_misc(tmp_path):
+    """Test misc methods of FoldFile base class."""
     fold_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     with pytest.raises(NotImplementedError):
-        fold_file = hybkit.FoldFile.open(fold_autotest_file_name, 'w')
+        _fold_file = hybkit.FoldFile.open(fold_autotest_file_name, 'w')
diff --git a/auto_tests/test_FoldRecord.py b/auto_tests/test_FoldRecord.py
index 56d1be1..e028fbd 100755
--- a/auto_tests/test_FoldRecord.py
+++ b/auto_tests/test_FoldRecord.py
@@ -7,29 +7,37 @@
 Automatic testing of hybkit code.
 """
 
-import os
-import sys
+# import argparse
 import copy
-from contextlib import nullcontext as does_not_raise
-import argparse
-import pytest
-import hybkit
-
-
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, hyb_file_name
 
+# import os
+# import sys
+# from contextlib import nullcontext as does_not_raise
+import pytest
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+import hybkit
+from auto_tests.test_helper_data import (
+    ART_HYB_VIENNA_PROPS_1,
+    ART_HYB_VIENNA_PROPS_2,
+    EMPTY_SEG_PROPS,
+    ENERGY_ALLOWED_TYPES,
+    FOLD_ALLOWED_TYPES,
+    ID_ALLOWED_TYPES,
+    SEQ_ALLOWED_TYPES,
+    TEST_ENERGY_STR,
+    TEST_FOLD_STR,
+    TEST_HYB_ID_STR,
+    TEST_OBJECTS,
+    TEST_SEQ_STR,
+)
+from auto_tests.test_helper_functions import (
+    get_expected_result_context,
+    get_expected_result_string,
+)
+from hybkit.errors import HybkitConstructorError
+
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 hybkit.util.set_setting('error_mode', 'raise')
 hybkit.util.set_setting('iter_error_mode', 'raise')
@@ -65,7 +73,7 @@ def test_foldrecord_constructor_main():
             seq_type=seq_type
         )
         assert test_record_2.seq_type == seq_type
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitConstructorError):
         test_record_2 = hybkit.FoldRecord(
             id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR,
             fold=TEST_FOLD_STR, energy=TEST_ENERGY_STR,
@@ -73,12 +81,12 @@ def test_foldrecord_constructor_main():
         )
 
     # Test Magicmethods
-    assert test_record == test_record
+    assert test_record == copy.deepcopy(test_record)
     test_record_2.id = 'test2'
     assert test_record != test_record_2
     assert len(test_record) == len(TEST_SEQ_STR)
     assert bool(test_record)
-    print(str(test_record))
+    str(test_record)
     hash(test_record)
 
 
@@ -91,22 +99,23 @@ def test_foldrecord_constructor_vienna_to_line():
         seq_type='static',
     )
     format_energy_str = '(%s)' % TEST_ENERGY_STR
-    assert (
-        test_record.to_vienna_string()
-        == """>%s\n%s\n%s\t%s\n""" % (TEST_HYB_ID_STR, TEST_SEQ_STR,
-                                      TEST_FOLD_STR, format_energy_str)
+    compare_string = (
+            f""">{TEST_HYB_ID_STR}\n{TEST_SEQ_STR}\n"""
+            f"""{TEST_FOLD_STR}\t{format_energy_str}\n"""
     )
+    assert test_record.to_vienna_string() == compare_string
 
     test_record_2 = hybkit.FoldRecord(
         id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR,
         fold=TEST_FOLD_STR, energy=None,
         seq_type='static',
     )
-    assert (
-        test_record_2.to_vienna_string()
-        == """>%s\n%s\n%s\t%s\n""" % (TEST_HYB_ID_STR, TEST_SEQ_STR,
-                                      TEST_FOLD_STR, '(.)')
+    compare_string = (
+        f""">{TEST_HYB_ID_STR}\n{TEST_SEQ_STR}\n"""
+        f"""{TEST_FOLD_STR}\t{"(.)"}\n"""
     )
+    assert test_record_2.to_vienna_string() == compare_string
+
 
 
 # ----- FoldRecord Type Tests -----
@@ -125,7 +134,7 @@ def test_foldrecord_constructor_vienna_to_line():
 }
 test_parameters = []
 # Setup test constructor types for each attribute in default_constructor_args:
-for constructor_field in default_constructor_args.keys():
+for constructor_field in default_constructor_args:
     # Setup testing of each possible data type for each field
     for test_name, test_object in TEST_OBJECTS.items():
         # Get types allowed for this field
@@ -135,7 +144,10 @@ def test_foldrecord_constructor_vienna_to_line():
         # Set test data for this field
         constructor_args[constructor_field] = test_object
         # Determine Error vs. Null Context for this test
-        expect_result = get_expected_result_string(test_name in allowed_types)
+        expect_result = get_expected_result_string(
+            test_name in allowed_types,
+            err_string='HybkitConstructorError'
+            )
         test_param_set = (
             constructor_field,
             test_name,
@@ -145,11 +157,12 @@ def test_foldrecord_constructor_vienna_to_line():
         test_parameters.append(test_param_set)
 
 
-@pytest.mark.parametrize("test_field,test_name,expect_str,test_input", [*test_parameters])
+@pytest.mark.parametrize(('test_field', 'test_name', 'expect_str', 'test_input'),
+                         [*test_parameters])
 def test_foldrecord_obj_types(test_field, test_name, expect_str, test_input):
+    """Test FoldRecord constructor with various data types."""
     expect_context = get_expected_result_context(expect_str)
     with expect_context:
-        print(test_input)
         assert hybkit.FoldRecord(**test_input) is not None
 
 
@@ -161,9 +174,10 @@ def test_foldrecord_obj_types(test_field, test_name, expect_str, test_input):
 ]
 
 
-@pytest.mark.parametrize("test_name,expectation,test_props", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'expectation', 'test_props'), [*test_parameters])
 def test_foldrecord_vienna_io(test_name, expectation, test_props):
-    hyb_str = test_props['hyb_str']
+    """Test FoldRecord reading/writing of vienna-format records."""
+    hyb_str = test_props['hyb_str']  # noqa: F841
     vienna_str = test_props['vienna_str']
     vienna_id = vienna_str.splitlines()[0].split()[0].lstrip('>')
     vienna_lines = vienna_str.splitlines()
@@ -184,18 +198,18 @@ def test_foldrecord_vienna_io(test_name, expectation, test_props):
         assert fold_record.energy == energy
 
     assert fold_record.to_vienna_lines(newline=False) == vienna_lines
-    assert ''.join(fold_record.to_vienna_lines(True)) == '\n'.join(vienna_lines_mod_last)
+    assert ''.join(fold_record.to_vienna_lines(newline=True)) == '\n'.join(vienna_lines_mod_last)
     assert fold_record.to_vienna_string(newline=False) == vienna_str.rstrip()
     assert fold_record.to_vienna_string(newline=True) == (vienna_str)
 
 
 # ----- FoldRecord test reading vienna-format records with errors. -----
 test_parameters = [
-    ('empty3', {'record_lines': ['', '', '']}),
-    ('empty2', {'record_lines': ['', '']}),
-    ('empty1', {'record_lines': ['abc', '123', 'singleval']}),
-    ('empty0', {'record_lines': ['abc', '123', '(99']}),
-    ('bad_error', {
+    ('empty3', 'HybkitConstructorError', {'record_lines': ['', '', '']}),
+    ('empty2', 'HybkitConstructorError', {'record_lines': ['', '']}),
+    ('empty1', 'HybkitConstructorError', {'record_lines': ['abc', '123', 'singleval']}),
+    ('empty0', 'HybkitConstructorError', {'record_lines': ['abc', '123', '(99']}),
+    ('bad_error', 'HybkitArgError', {
         'record_lines': ART_HYB_VIENNA_PROPS_1['vienna_str'].split('\n')[:3],
         'error_mode': 'bad_error_mode',
     }),
@@ -203,9 +217,11 @@ def test_foldrecord_vienna_io(test_name, expectation, test_props):
 ]
 
 
-@pytest.mark.parametrize("test_name,test_kws", [*test_parameters])
-def test_foldrecord_vienna_io_errors(test_name, test_kws):
-    with pytest.raises(RuntimeError):
+@pytest.mark.parametrize(('test_name', 'expect_str', 'test_kws'), [*test_parameters])
+def test_foldrecord_vienna_io_errors(test_name, expect_str, test_kws):
+    """Test FoldRecord reading vienna-format records with errors."""
+    expect_context = get_expected_result_context(expect_str)
+    with expect_context:
         hybkit.FoldRecord.from_vienna_lines(**test_kws)
 
 
@@ -217,8 +233,9 @@ def test_foldrecord_vienna_io_errors(test_name, test_kws):
 ]
 
 
-@pytest.mark.parametrize("test_name,test_kws", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'test_kws'), [*test_parameters])
 def test_foldrecord_vienna_io_warnings(test_name, test_kws):
+    """Test FoldRecord reading vienna-format records with allowed errors."""
     hybkit.FoldRecord.from_vienna_lines(**test_kws)
 
 # ----- Begin CT-format FoldRecord tests -----
@@ -265,7 +282,7 @@ def test_foldrecord_vienna_io_warnings(test_name, test_kws):
 # ]
 # @pytest.mark.parametrize("test_name,test_kws", [*test_parameters])
 # def test_foldrecord_ct_io_errors(test_name, test_kws):
-#     with pytest.raises(RuntimeError):
+#     with pytest.raises(HybkitMiscError):
 #         hybkit.FoldRecord.from_ct_lines(**test_kws)
 
 # # ----- FoldRecord test reading ct-format records with allowed errors. -----
diff --git a/auto_tests/test_HybFile.py b/auto_tests/test_HybFile.py
index 8bac2fa..b775367 100755
--- a/auto_tests/test_HybFile.py
+++ b/auto_tests/test_HybFile.py
@@ -7,57 +7,51 @@
 Automatic testing of the hybkit HybRecord class.
 """
 
+# import copy
 import os
-import sys
-import copy
-from contextlib import nullcontext as does_not_raise
-import pytest
-import hybkit
-
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, hyb_file_name
 
+# import sys
+# from contextlib import nullcontext as does_not_raise
+import pytest
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+import hybkit
+from auto_tests.test_helper_data import ART_BAD_HYB_STRS, ART_HYB_PROPS_1, ART_HYB_PROPS_ALL
+from auto_tests.test_helper_functions import get_expected_result_context
+from hybkit.errors import HybkitMiscError
 
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 # ----- HybFile test misc disallowed commands -----
 def test_hybfile_misc_disallowed(tmp_path):
+    """Test misc methods of HybFile class that should raise errors."""
     hyb_autotest_file_name = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     with hybkit.HybFile(hyb_autotest_file_name, 'w') as hyb_autotest_file:
-        with pytest.raises(RuntimeError):
+        with pytest.raises(NotImplementedError):
             hyb_autotest_file.write('test_str')
-        with pytest.raises(RuntimeError):
-            hyb_autotest_file._ensure_HybRecord(None)
+        with pytest.raises(HybkitMiscError):
+            hyb_autotest_file._ensure_hybrecord(None)
 
 
 # ----- HybFile test reading/writing of hyb records. -----
 test_parameters = [
-    ('One-Record', 'Pass', [ART_HYB_PROPS_1['hyb_str']], ),
+    ('One-Record', 'Pass', [ART_HYB_PROPS_1['hyb_str']]),
     ('All-Records', 'Pass', [props['hyb_str'] for props in ART_HYB_PROPS_ALL]),
     ('All-Records-Multi', 'Pass', [props['hyb_str'] for props in ART_HYB_PROPS_ALL]),
 ]
 for i, bad_hyb_str in enumerate(ART_BAD_HYB_STRS, start=1):
     test_parameters.append(
-        ('Bad-Record-' + str(i), 'Raise', [bad_hyb_str])
+        ('Bad-Record-' + str(i), 'HybkitConstructorError', [bad_hyb_str])
     )
 
 
-@pytest.mark.parametrize("test_name,expectation,hyb_strs", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'expectation', 'hyb_strs'), [*test_parameters])
 def test_hybfile_io(test_name, expectation, hyb_strs, tmp_path):
+    """Test reading/writing of hyb records."""
     hyb_autotest_file_name = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
-    expect_context = get_expected_result_context(expectation)
+    expect_context = get_expected_result_context(expect_str=expectation)
     all_hyb_strs = ''.join(hyb_strs)
-    with open(hyb_autotest_file_name, 'w') as hyb_autotest_file:
+    with open(hyb_autotest_file_name, mode='w') as hyb_autotest_file:
         hyb_autotest_file.write(all_hyb_strs)
 
     assert hybkit.util.hyb_exists(hyb_autotest_file_name)
@@ -75,6 +69,6 @@ def test_hybfile_io(test_name, expectation, hyb_strs, tmp_path):
             all_records = hyb_autotest_file.read_records()
         assert first_record == all_records[0]
         with hybkit.HybFile(hyb_autotest_file_name, 'w') as hyb_autotest_file:
-            hyb_autotest_file.write_records([hyb_record, hyb_record])
-            hyb_autotest_file.write_record(hyb_record)
+            hyb_autotest_file.write_records(write_records=[hyb_record, hyb_record])
+            hyb_autotest_file.write_record(write_record=hyb_record)
             hyb_autotest_file.write_fh(all_hyb_strs)
diff --git a/auto_tests/test_HybFoldIter.py b/auto_tests/test_HybFoldIter.py
index a073830..75f4976 100755
--- a/auto_tests/test_HybFoldIter.py
+++ b/auto_tests/test_HybFoldIter.py
@@ -7,28 +7,33 @@
 Automatic testing of hybkit code.
 """
 
-import os
-import sys
+# import argparse
 import copy
-from contextlib import nullcontext as does_not_raise
-import argparse
-import pytest
-import hybkit
-
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, hyb_file_name
+import os
 
+# import sys
+# from contextlib import nullcontext as does_not_raise
+import pytest
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+import hybkit
+from auto_tests.test_helper_data import (
+    ART_BAD_HYB_VIENNA_PROPS_1,
+    ART_BAD_HYB_VIENNA_PROPS_2,
+    ART_BAD_HYB_VIENNA_PROPS_3,
+    ART_BAD_HYB_VIENNA_PROPS_4,
+    ART_BAD_HYB_VIENNA_PROPS_5,
+    ART_BAD_HYB_VIENNA_PROPS_6,
+    ART_BAD_HYB_VIENNA_PROPS_7,
+    ART_BAD_HYB_VIENNA_PROPS_8,
+    ART_BAD_HYB_VIENNA_PROPS_9,
+    ART_HYB_VIENNA_PROPS_1,
+    ART_HYB_VIENNA_PROPS_2,
+)
+from auto_tests.test_helper_functions import get_expected_result_context
+from hybkit.errors import HybkitConstructorError, HybkitIterError
+
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 hybkit.util.set_setting('error_mode', 'raise')
 hybkit.util.set_setting('iter_error_mode', 'raise')
@@ -37,21 +42,22 @@
 # ----- Begin HybFoldIter Tests -----
 # ----- Constructor Tests -----
 def test_hybfolditer_constructor_misc(tmp_path):
+    """Test misc HybFoldIter constructor properties."""
     hyb_autotest_file_name = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     hyb_file = hybkit.HybFile(hyb_autotest_file_name, 'w')
     fold_file = hybkit.ViennaFile(vienna_autotest_file_name, 'w')
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitIterError):
         use_iter = hybkit.HybFoldIter(hyb_file, None)
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitIterError):
         use_iter = hybkit.HybFoldIter(None, fold_file)
-    with pytest.raises(RuntimeError):
-        use_iter = hybkit.HybFoldIter(hyb_file, fold_file, iter_error_mode='BadMode')
+    with pytest.raises(HybkitIterError):
+        use_iter = hybkit.HybFoldIter(hyb_file, fold_file, iter_error_mode='BadMode')  # noqa: F841
 
 
 # ----- Direct Match Tests -----
 def test_hybfolditer_direct_match_misc():
-    """Test misc HybRecord / FoldRecord Match Properties"""
+    """Test misc HybRecord / FoldRecord Match Properties."""
     use_props = ART_BAD_HYB_VIENNA_PROPS_4
     hyb_record = hybkit.HybRecord.from_line(use_props['hyb_str'])
     fold_record = hybkit.FoldRecord.from_vienna_string(use_props['vienna_str'])
@@ -59,7 +65,7 @@ def test_hybfolditer_direct_match_misc():
         copy.deepcopy(fold_record),
         allow_energy_mismatch=True
     )
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitConstructorError):
         copy.deepcopy(hyb_record).set_fold_record(
             copy.deepcopy(fold_record),
             allow_energy_mismatch=False
@@ -76,27 +82,29 @@ def test_hybfolditer_direct_match_misc():
     test_param_sets.append(
         (test_name, 'Pass', prop_set, False)
     )
-test_param_sets.append(('Mismatch_Seq_Static', 'Raise', ART_BAD_HYB_VIENNA_PROPS_1, False))
-test_param_sets.append(('Disallowed-Overlap', 'Raise', ART_BAD_HYB_VIENNA_PROPS_2, False))
-test_param_sets.append(('Mismatch_Seq_Dynamic', 'Raise', ART_BAD_HYB_VIENNA_PROPS_3, False))
-test_param_sets.append(('Mismatch_Energy', 'Raise', ART_BAD_HYB_VIENNA_PROPS_4, True))
-test_param_sets.append(('Missing_Vienna', 'Raise', ART_BAD_HYB_VIENNA_PROPS_5, False))
-test_param_sets.append(('Bad_Vienna', 'Raise', ART_BAD_HYB_VIENNA_PROPS_6, True))
-test_param_sets.append(('Error99', 'Raise', ART_BAD_HYB_VIENNA_PROPS_7, True))
-test_param_sets.append(('Insertion', 'Raise', ART_BAD_HYB_VIENNA_PROPS_8, True))
-test_param_sets.append(('Deletion', 'Raise', ART_BAD_HYB_VIENNA_PROPS_9, True))
+test_param_sets.append(('Mismatch_Seq_Static', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_1, False))
+test_param_sets.append(('Disallowed-Overlap', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_2, False))
+test_param_sets.append(('Mismatch_Seq_Dynamic', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_3, False))
+test_param_sets.append(('Mismatch_Energy', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_4, True))
+test_param_sets.append(('Missing_Vienna', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_5, False))
+test_param_sets.append(('Bad_Vienna', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_6, True))
+test_param_sets.append(('Error99', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_7, True))
+test_param_sets.append(('Insertion', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_8, True))
+test_param_sets.append(('Deletion', 'HybkitIterError', ART_BAD_HYB_VIENNA_PROPS_9, True))
 
 
 test_parameters = []
 for test_param_set in test_param_sets:
     for combine in ['Combine', 'Separate']:
-        test_parameters.append((*test_param_set, combine))
+        use_params = [*test_param_set]
+        test_parameters.append((*use_params, combine))
 arg_string = 'test_name,expectation,test_props,combine_str,skip_match_check'
 
 
 @pytest.mark.parametrize(arg_string, [*test_parameters])
 def test_hybfolditer_io(test_name, expectation, test_props, combine_str,
                         skip_match_check, tmp_path):
+    """Test reading/writing of hyb records."""
     hyb_autotest_file_name = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     expect_context = get_expected_result_context(expectation)
@@ -116,7 +124,7 @@ def test_hybfolditer_io(test_name, expectation, test_props, combine_str,
     assert hybkit.util.hyb_exists(hyb_autotest_file_name)
     assert hybkit.util.vienna_exists(vienna_autotest_file_name)
 
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitIterError):
         use_iter = hybkit.HybFoldIter(
             hyb_autotest_file_name,
             vienna_autotest_file_name,
@@ -155,7 +163,7 @@ def test_hybfolditer_io(test_name, expectation, test_props, combine_str,
         assert hyb_record
         assert fold_record
 
-    if expectation == 'Raise':
+    if expectation in {'Raise', 'HybkitIterError'}:
         hyb_record = hybkit.HybFile(hyb_autotest_file_name, 'r').read_record()
         fold_file = hybkit.ViennaFile(
             vienna_autotest_file_name,
@@ -164,7 +172,7 @@ def test_hybfolditer_io(test_name, expectation, test_props, combine_str,
         )
         fold_record = fold_file.read_record(override_error_mode='warn_return')
         if not skip_match_check:
-            assert not "made it here"
+            # assert not "made it here"
             assert not fold_record.matches_hyb_record(hyb_record)
             assert fold_record.count_hyb_record_mismatches(hyb_record) >= test_props['mismatches']
             with pytest.raises(RuntimeError):
@@ -241,7 +249,8 @@ def test_hybfolditer_io(test_name, expectation, test_props, combine_str,
         if iter_error_mode in allowed_errors:
             expectation = 'Pass'
         else:
-            expectation = 'Raise'
+            # expectation = 'Raise'
+            expectation = 'HybkitIterError'
         test_parameters.append((test_name, iter_error_mode, expectation, seq_type, test_props))
 arg_string = 'test_name,iter_error_mode,expectation,seq_type,test_props'
 
@@ -249,6 +258,7 @@ def test_hybfolditer_io(test_name, expectation, test_props, combine_str,
 @pytest.mark.parametrize(arg_string, [*test_parameters])
 def test_hybfolditer_iter_error_mode(test_name, iter_error_mode,
                                      expectation, seq_type, test_props, tmp_path):
+    """Test reading/writing of hyb records."""
     hyb_autotest_file_name = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     expect_context = get_expected_result_context(expectation)
@@ -267,14 +277,17 @@ def test_hybfolditer_iter_error_mode(test_name, iter_error_mode,
         iter_error_mode=iter_error_mode,
     )
     with expect_context:
-        for ret_items in use_iter:
+        for _ret_items in use_iter:
             pass
 
-
 default_skips = hybkit.settings.HybFoldIter_settings_info['max_sequential_skips'][0]
 test_parameters = [
-    ('Allowed_Skips', 'warn_skip', 'Pass', ART_BAD_HYB_VIENNA_PROPS_1, (default_skips - 1)),
-    ('Disallowed_Skips', 'warn_skip', 'Raise', ART_BAD_HYB_VIENNA_PROPS_1, (default_skips + 2))
+    ('Allowed_Skips', 'warn_skip', 'Pass',
+     ART_BAD_HYB_VIENNA_PROPS_1, (default_skips - 1)
+     ),
+    ('Disallowed_Skips', 'warn_skip', 'HybkitIterError',
+     ART_BAD_HYB_VIENNA_PROPS_1, (default_skips + 2)
+     )
 ]
 arg_string = 'test_name,iter_error_mode,expectation,test_props,num_skips'
 
@@ -282,16 +295,17 @@ def test_hybfolditer_iter_error_mode(test_name, iter_error_mode,
 @pytest.mark.parametrize(arg_string, [*test_parameters])
 def test_hybfolditer_iter_num_skips(test_name, iter_error_mode,
                                     expectation, test_props, num_skips, tmp_path):
+    """Test reading/writing of hyb records."""
     hyb_autotest_file_name = os.path.join(tmp_path, 'hyb_autotest_file.hyb')
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     expect_context = get_expected_result_context(expectation)
     hyb_str = test_props['hyb_str']
     vienna_str = test_props['vienna_str']
     with open(hyb_autotest_file_name, 'w') as hyb_autotest_file:
-        for i in range(num_skips):
+        for _ in range(num_skips):
             hyb_autotest_file.write(hyb_str)
     with open(vienna_autotest_file_name, 'w') as vienna_autotest_file:
-        for i in range(num_skips):
+        for _ in range(num_skips):
             vienna_autotest_file.write(vienna_str)
 
     hyb_file = hybkit.HybFile(hyb_autotest_file_name, 'r')
@@ -303,7 +317,7 @@ def test_hybfolditer_iter_num_skips(test_name, iter_error_mode,
     )
     with expect_context:
         ret_items = None
-        for ret_items in use_iter:
+        for ret_items in use_iter:  # noqa: B007
             pass
         if 'skip' not in iter_error_mode:
             assert ret_items
diff --git a/auto_tests/test_HybRecord.py b/auto_tests/test_HybRecord.py
index f8d6737..d03818b 100755
--- a/auto_tests/test_HybRecord.py
+++ b/auto_tests/test_HybRecord.py
@@ -7,27 +7,55 @@
 Automatic testing of the hybkit HybRecord class.
 """
 
-import os
-import sys
+
 import copy
-from contextlib import nullcontext as does_not_raise
+
 import pytest
-import hybkit
 
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, hyb_file_name
+import hybkit
+from auto_tests.test_helper_data import (
+    ART_HYB_PROPS_1,
+    ART_HYB_PROPS_2,
+    ART_HYB_PROPS_3,
+    ART_HYB_PROPS_4,
+    ART_HYB_STR_PROPS,
+    EMPTY_SEG_PROPS,
+    ENERGY_ALLOWED_TYPES,
+    EXPECTED_FIELDS_HEADER,
+    FIVE_I,
+    FORTY_I,
+    ID_ALLOWED_TYPES,
+    READ_END_ALLOWED_TYPES,
+    READ_START_ALLOWED_TYPES,
+    REF_END_ALLOWED_TYPES,
+    REF_NAME_ALLOWED_TYPES,
+    REF_START_ALLOWED_TYPES,
+    SCORE_ALLOWED_TYPES,
+    SEG_PROPS_ALLOWED_TYPES,
+    SEQ_ALLOWED_TYPES,
+    TEN_I,
+    TEST_ENERGY_STR,
+    TEST_FLAGS_OBJ,
+    TEST_FLAGS_STR,
+    TEST_HYB_FIELDS,
+    TEST_HYB_ID_STR,
+    TEST_HYB_LINE,
+    TEST_HYB_MINIMAL_STRING,
+    TEST_OBJECTS,
+    TEST_READ_COUNT,
+    TEST_READ_COUNT_STR,
+    TEST_SEG_PROPS_STR,
+    TEST_SEQ_STR,
+)
+from auto_tests.test_helper_functions import get_expected_result_context, get_expected_result_string
+from hybkit.errors import HybkitArgError, HybkitConstructorError, HybkitMiscError
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
 # Includes the following functions:
 # get_expected_result_string(is_allowed=False)
 # get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
 
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 # ----- Begin HybRecord Tests -----
 # ----- HybRecord Constructor Tests - Minimal -----
@@ -50,27 +78,27 @@ def test_hybrecord_constructor_minimal():
     # Test "get_seg1_type" method
     assert test_record.get_seg1_type() is None
     # Test "get_seg1_type" method empty error
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         test_record.get_seg1_type(require=True)
     # Test "get_seg2_type" method
     assert test_record.get_seg2_type() is None
     # Test "get_seg2_type" method empty error
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         test_record.get_seg2_type(require=True)
     # Test "get_seg_types" method
     assert test_record.get_seg_types() == (None, None)
     # Test "get_seg_types" method empty error
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         test_record.get_seg_types(require=True)
     # Test "get_read_count" method
     assert test_record.get_read_count() is None
     # Test "get_read_count" method empty error
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         test_record.get_read_count(require=True)
     # Test "get_record_count" method
     assert test_record.get_record_count() == 1
     # Test "get_record_count" method empty error
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         test_record.get_record_count(require=True)
 
 
@@ -78,18 +106,11 @@ def test_hybrecord_constructor_minimal():
 def test_hybrecord_constructor_details():
     """Test construction of HybRecord class with minimal information."""
     test_record = hybkit.HybRecord(id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR)
-
-    # Test "to_line" method with minimial information
-    assert test_record.to_line(newline=False) == TEST_HYB_MINIMAL_STRING
-    # Test "to_csv" method with minimial information
-    assert (test_record.to_csv(newline=False)
-            == TEST_HYB_MINIMAL_STRING.replace('\t', ','))
-
     # Test HybRecord Constructor with missing id
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitConstructorError):
         hybkit.HybRecord(id=None, seq=TEST_SEQ_STR)
     # Test HybRecord Constructor with missing seq
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitConstructorError):
         hybkit.HybRecord(id=TEST_HYB_ID_STR, seq=None)
     # Test HybRecord Constructor with allowed bad_flag
     hybkit.HybRecord(
@@ -99,7 +120,7 @@ def test_hybrecord_constructor_details():
         flags={'bad_flag': 'bad_val'},
     )
     # Test HybRecord Constructor with bad_flag
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         hybkit.HybRecord(
             id=TEST_HYB_ID_STR,
             seq=TEST_SEQ_STR,
@@ -108,19 +129,59 @@ def test_hybrecord_constructor_details():
         )
     # Test HybRecord Constructor with read count
     test_record = hybkit.HybRecord(id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR, read_count=5)
-    assert test_record.get_read_count() == 5
+    assert test_record.get_read_count() == FIVE_I
     # Set Read Count, and retest
-    test_record.set_flag('read_count', 10)
-    assert test_record.get_read_count() == 10
+    test_record.set_flag('read_count', TEN_I)
+    assert test_record.get_read_count() == TEN_I
     # Test HybRecord Constructor with mismatched read count
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitConstructorError):
         hybkit.HybRecord(
             id=TEST_HYB_ID_STR,
             seq=TEST_SEQ_STR,
             read_count=5,
-            flags={'read_count': 10},
+            flags={'read_count': TEN_I},
         )
 
+# ----- HybRecord Formatter Tests - Details -----
+def test_hybrecord_formatter_details():
+    """Test formatting methods of HybRecord class."""
+    test_record = hybkit.HybRecord(id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR)
+    # Test "to_fields_header" method
+    assert test_record.to_fields_header() == EXPECTED_FIELDS_HEADER
+    # Test "to_csv_header" method
+    assert test_record.to_csv_header() == ','.join(EXPECTED_FIELDS_HEADER)
+    # Test "to_line" method with minimal information
+    assert test_record.to_line(newline=False) == TEST_HYB_MINIMAL_STRING
+    # Test "to_csv" method with minimal information
+    assert (test_record.to_csv(newline=False)
+            == TEST_HYB_MINIMAL_STRING.replace('\t', ','))
+    #Test "to_fields" method with minimal information
+    expected_fields = {
+        'id': TEST_HYB_ID_STR,
+        'seq': TEST_SEQ_STR,
+    }
+    for field in EXPECTED_FIELDS_HEADER:
+        if field not in expected_fields:
+            expected_fields[field] = None
+    assert test_record.to_fields() == expected_fields
+
+    # Test with more information
+    test_record = hybkit.HybRecord(
+        id=TEST_HYB_ID_STR,
+        seq=TEST_SEQ_STR,
+        energy=TEST_ENERGY_STR,
+        seg1_props=TEST_SEG_PROPS_STR,
+        seg2_props=TEST_SEG_PROPS_STR,
+        flags=TEST_FLAGS_OBJ,
+    )
+
+    # Test "to_line" method with detailed information
+    assert test_record.to_line(newline=False) == TEST_HYB_LINE
+    # Test "to_csv" method with detailed information
+    assert test_record.to_csv(newline=False) == TEST_HYB_LINE.replace('\t', ',')
+    # Test "to_fields" method with detailed information
+    assert test_record.to_fields() == TEST_HYB_FIELDS
+
 
 # ----- HybRecord Method Tests -----
 def test_hybrecord_methods_misc():
@@ -128,7 +189,7 @@ def test_hybrecord_methods_misc():
     test_record = hybkit.HybRecord(id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR)
     hybkit.HybRecord._flagset = None
     test_record.set_flag('seg1_type', 'microRNA')
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         test_record.set_flag('BadFlag', 'BadVal')
 
 
@@ -152,17 +213,18 @@ def test_hybrecord_methods_misc():
 # error_params[6] = {'seg1_type': 'badtype'}
 # test_parameters.append(tuple(error_params))
 
-arg_string = "test_name,test_id,test_seq,test_energy,test_seg1_props,"
-arg_string += "test_seg2_props,test_flags,test_read_count"
+arg_string = 'test_name,test_id,test_seq,test_energy,test_seg1_props,'
+arg_string += 'test_seg2_props,test_flags,test_read_count'
 
 
 @pytest.mark.parametrize(arg_string, [*test_parameters])
-def test_hybrecord_constructor_errors(test_name, test_id, test_seq, test_energy, test_seg1_props,
+def test_hybrecord_constructor_errors(test_name, test_id, test_seq,
+                                      test_energy, test_seg1_props,
                                       test_seg2_props, test_flags, test_read_count):
     """Test construction of HybRecord class with full complement of information."""
     # Test HybRecord Minimal Constructor:
-    with pytest.raises((RuntimeError, TypeError)):
-        test_record = hybkit.HybRecord(
+    with pytest.raises((HybkitMiscError, HybkitConstructorError)):
+        _test_record = hybkit.HybRecord(
             id=test_id,
             seq=test_seq,
             energy=test_energy,
@@ -191,7 +253,7 @@ def test_hybrecord_constructor_errors(test_name, test_id, test_seq, test_energy,
 }
 test_parameters = []
 # Setup test constructor types for each attribute in default_constructor_args:
-for constructor_field in default_constructor_args.keys():
+for constructor_field in default_constructor_args:
     # Setup testing of each possible data type for each field
     for test_name, test_object in TEST_OBJECTS.items():
         # Get types allowed for this field
@@ -201,7 +263,10 @@ def test_hybrecord_constructor_errors(test_name, test_id, test_seq, test_energy,
         # Set test data for this field
         constructor_args[constructor_field] = test_object
         # Determine Error vs. Null Context for this test
-        expect_result = get_expected_result_string(test_name in allowed_types)
+        expect_result = get_expected_result_string(
+            (test_name in allowed_types),
+            'HybkitConstructorError'
+            )
         test_param_set = (
             constructor_field,
             test_name,
@@ -210,12 +275,12 @@ def test_hybrecord_constructor_errors(test_name, test_id, test_seq, test_energy,
         )
         test_parameters.append(test_param_set)
 
-
-@pytest.mark.parametrize("test_field,test_name,expect_str,test_input", [*test_parameters])
+use_fields = ('test_field', 'test_name', 'expect_str', 'test_input')
+@pytest.mark.parametrize(use_fields, [*test_parameters])
 def test_hybrecord_obj_types(test_field, test_name, expect_str, test_input):
+    """Test HybRecord object types for each attribute in default_constructor_args."""
     expect_context = get_expected_result_context(expect_str)
     with expect_context:
-        print(test_input)
         assert hybkit.HybRecord(**test_input) is not None
 
 
@@ -247,7 +312,7 @@ def test_hybrecord_obj_types(test_field, test_name, expect_str, test_input):
 test_parameters = []
 # Setup test constructor types for each segN_props dict
 for prop_set in ['seg1_props', 'seg2_props']:
-    for prop_field in props_allowed_types.keys():
+    for prop_field in props_allowed_types:
         # Setup testing of each possible data type for each field
         for test_name, test_object in TEST_OBJECTS.items():
             # Get types allowed for this field
@@ -258,7 +323,10 @@ def test_hybrecord_obj_types(test_field, test_name, expect_str, test_input):
             seg_args[prop_field] = test_object
             constructor_args[prop_set] = seg_args
             # Determine Error vs. Null Context for this test
-            expect_result = get_expected_result_string(test_name in allowed_types)
+            expect_result = get_expected_result_string(
+                (test_name in allowed_types),
+                err_string='HybkitConstructorError'
+                )
             test_param_set = (
                 prop_set,
                 prop_field,
@@ -269,11 +337,13 @@ def test_hybrecord_obj_types(test_field, test_name, expect_str, test_input):
             test_parameters.append(test_param_set)
 
 
-@pytest.mark.parametrize("prop_set,prop_field,test_name,expect_str,test_input", [*test_parameters])
+use_parameters = ('prop_set', 'prop_field', 'test_name', 'expect_str', 'test_input')
+
+@pytest.mark.parametrize(use_parameters, [*test_parameters])
 def test_hybrecord_obj_types_seg_props(prop_set, prop_field, test_name, expect_str, test_input):
+    """Test HybRecord object types for each attribute in default_constructor_args."""
     expect_context = get_expected_result_context(expect_str)
     with expect_context:
-        print(test_input)
         assert hybkit.HybRecord(**test_input) is not None
 
 
@@ -286,9 +356,9 @@ def test_hybrecord_obj_types_seg_props(prop_set, prop_field, test_name, expect_s
 ]
 
 
-@pytest.mark.parametrize("test_name,test_params", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'test_params'), [*test_parameters])
 def test_hybrecord_type_mirna(test_name, test_params):
-    """Test Hybrecord type_eval(), eval_mirna(), mirna-associated prop(), and mirna_detail()"""
+    """Test Hybrecord type_eval(), eval_mirna(), mirna-associated prop(), and mirna_detail()."""
     test_record = hybkit.HybRecord.from_line(
         line=test_params['hyb_str'],
     )
@@ -302,9 +372,9 @@ def test_hybrecord_type_mirna(test_name, test_params):
         line=test_params['hyb_str'],
         hybformat_id=True,
     )
-    with pytest.raises((ValueError, RuntimeError)):
+    with pytest.raises(HybkitMiscError):
         test_record.mirna_details(detail='all', allow_mirna_dimers=True)
-    with pytest.raises((ValueError, RuntimeError)):
+    with pytest.raises(HybkitMiscError):
         test_record.to_fasta_record(mode='mirna')
 
     test_record.eval_types()
@@ -352,18 +422,18 @@ def test_hybrecord_type_mirna(test_name, test_params):
     # Test 0-mirna cases
     if not test_params['has_mirna']:
         # Check error on miRNA detail and FASTA calls
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.mirna_details()
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.to_fasta_record(mode='miRNA')
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.to_fasta_record(mode='target')
         # Check miRNA / target prop dict fetching error
         assert test_record.get_mirna_props(require=False) is None
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.get_mirna_props(require=True)
         assert test_record.get_target_props(require=False) is None
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.get_target_props(require=True)
 
     # Test 1-mirna or 2-mirna cases
@@ -375,7 +445,7 @@ def test_hybrecord_type_mirna(test_name, test_params):
         assert mirna_detail_dict['target_seg_type'] == test_params['target_seg_type']
         assert mirna_detail_dict['mirna_seq'] == test_params['mirna_seq']
         assert mirna_detail_dict['target_seq'] == test_params['target_seq']
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises((HybkitArgError, HybkitMiscError)):
             test_record.mirna_details(detail='bad_detail')
 
         mirna_fasta = test_record.to_fasta_record('miRNA', annotate=False,
@@ -402,18 +472,18 @@ def test_hybrecord_type_mirna(test_name, test_params):
     # Test 2-mirna cases only
     if test_params['has_mirna'] and not test_params['has_one_mirna']:
         # Check miRNA Detail Props
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.mirna_details(detail='all', allow_mirna_dimers=False)
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.to_fasta_record('miRNA', allow_mirna_dimers=False)
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.to_fasta_record('target', allow_mirna_dimers=False)
         # Check miRNA / target prop dict fetching based on setting
         assert test_record.get_mirna_props(require=False, allow_mirna_dimers=False) is None
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.get_mirna_props(require=True, allow_mirna_dimers=False)
         assert test_record.get_target_props(require=False, allow_mirna_dimers=False) is None
-        with pytest.raises((ValueError, RuntimeError)):
+        with pytest.raises(HybkitMiscError):
             test_record.get_target_props(require=True, allow_mirna_dimers=False)
         assert (test_record.get_mirna_props(require=True, allow_mirna_dimers=True)
                 == getattr(test_record, test_params['mirna_seg_props']))
@@ -427,9 +497,9 @@ def test_hybrecord_type_mirna(test_name, test_params):
 ]
 
 
-@pytest.mark.parametrize("test_name,test_params", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'test_params'), [*test_parameters])
 def test_hybrecord_props(test_name, test_params):
-    """Test Hybrecord type_eval(), eval_mirna(), mirna-associated prop(), and mirna_detail()"""
+    """Test Hybrecord type_eval(), eval_mirna(), mirna-associated prop(), and mirna_detail()."""
     test_record = hybkit.HybRecord.from_line(
         line=test_params['hyb_str'],
         hybformat_id=True,
@@ -451,6 +521,7 @@ def test_hybrecord_props(test_name, test_params):
 
 # ----- HybRecord Magic Methods tests -----
 def test_hybrecord_magic_methods():
+    """Test HybRecord magic methods."""
     test_record_1 = hybkit.HybRecord.from_line(
         ART_HYB_PROPS_1['hyb_str'],
         hybformat_id=True,
@@ -462,43 +533,45 @@ def test_hybrecord_magic_methods():
         hybformat_ref=True
     )
     test_record_2.id = 'NewID'
-    print(str(test_record_1))
-    assert test_record_1 == test_record_1
-    assert not (test_record_1 != test_record_1)
+    str(test_record_1)
+    assert test_record_1 == copy.deepcopy(test_record_1)
+    assert not (test_record_1 != copy.deepcopy(test_record_1))  # noqa: SIM202
     assert test_record_1 != test_record_2
     assert bool(test_record_1)
     assert str(test_record_1)
     assert hash(test_record_1)
-    assert len(test_record_1) == 40
+    assert len(test_record_1) == FORTY_I
 
 
 # ----- HybRecord misc disallowed option tests -----
 test_parameters = [
-    ('to_fasta_record', ('notallowed',)),
-    ('is_set', ('badprop',)),
-    ('prop', ('badprop',)),
-    ('prop', ('any_seg_type_contains', None)),
-    ('prop', ('target_none',)),  # NotImplmented
-    ('set_fold_record', (None,)),
-    ('set_fold_record', ('not_fold_record',)),
-    ('mirna_detail', ('disallowed_detail',)),
-    ('_get_flag', ('fake_flag', True)),
-    ('_make_flags_dict', ('not_dict',)),
-    ('_make_flags_dict', ({'bad_flag': True},)),
-    ('_parse_hybformat_id', ('bad_id_name_continues_on',)),
-    ('_parse_hybformat_ref', ('bad_ref_name_continues_on',)),
-    ('_read_flags', ('bad_flag=B;bad_flag2=C;',)),
+    ('to_fasta_record', 'HybkitMiscError', ('notallowed',)),
+    ('is_set', 'HybkitMiscError', ('badprop',)),
+    ('prop', 'HybkitMiscError', ('badprop',)),
+    ('prop', 'HybkitMiscError', ('any_seg_type_contains', None)),
+    ('prop', 'HybkitMiscError', ('target_none',)),  # NotImplemented
+    ('set_fold_record', 'HybkitMiscError', (None,)),
+    ('set_fold_record', 'HybkitConstructorError', ('not_fold_record',)),
+    ('mirna_detail', 'HybkitMiscError', ('disallowed_detail',)),
+    ('_get_flag', 'HybkitMiscError', ('fake_flag', True)),
+    ('_make_flags_dict', 'HybkitMiscError', ('not_dict',)),
+    ('_make_flags_dict', 'HybkitMiscError', ({'bad_flag': True},)),
+    ('_parse_hybformat_id', 'HybkitConstructorError', ('bad_id_name_continues_on',)),
+    ('_parse_hybformat_ref', 'HybkitConstructorError', ('bad_ref_name_continues_on',)),
+    ('_read_flags', 'HybkitConstructorError', ('bad_flag=B;bad_flag2=C;',)),
 ]
 
 
-@pytest.mark.parametrize("method,badval", [*test_parameters])
-def test_hybrecord_misc_disallowed_1(method, badval):
+@pytest.mark.parametrize(('method', 'expectation', 'badval'), [*test_parameters])
+def test_hybrecord_misc_disallowed_1(method, expectation, badval):
+    """Test HybRecord misc disallowed option tests."""
     test_record = hybkit.HybRecord.from_line(
         ART_HYB_PROPS_1['hyb_str'],
         hybformat_id=True,
         hybformat_ref=True,
     )
-    with pytest.raises((RuntimeError, NotImplementedError)):
+    expect_context = get_expected_result_context(expectation)
+    with expect_context:
         getattr(test_record, method)(*badval)
 
 
@@ -509,34 +582,37 @@ def test_hybrecord_misc_disallowed_1(method, badval):
 ]
 
 
-@pytest.mark.parametrize("method,badval", [*test_parameters])
+@pytest.mark.parametrize(('method', 'badval'), [*test_parameters])
 def test_hybrecord_misc_disallowed_2(method, badval):
+    """Test HybRecord misc disallowed option tests."""
     test_record = hybkit.HybRecord(id=TEST_HYB_ID_STR, seq=TEST_SEQ_STR)
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitMiscError):
         getattr(test_record, method)(*badval)
 
 
 test_parameters = [
-    ('_ensure_props_read_start_end', tuple()),
+    ('_ensure_props_read_start_end', ()),
     ('to_fasta_record', ('seg1',)),
     ('_get_seg_seq', (EMPTY_SEG_PROPS,)),
 ]
 
 
-@pytest.mark.parametrize("method,badval", [*test_parameters])
+@pytest.mark.parametrize(('method', 'badval'), [*test_parameters])
 def test_hybrecord_bad_seg_props(method, badval):
+    """Test HybRecord misc disallowed option tests."""
     test_record = hybkit.HybRecord.from_line(
         ART_HYB_PROPS_1['hyb_str'],
         hybformat_id=True,
         hybformat_ref=True
     )
     test_record.seg1_props['read_start'] = None
-    with pytest.raises(RuntimeError):
+    with pytest.raises(HybkitConstructorError):
         getattr(test_record, method)(*badval)
 
 
 # ----- HybRecord misc private_function_tests -----
 def test_hybrecord_misc_private():
+    """Test HybRecord misc private function tests."""
     test_record = hybkit.HybRecord.from_line(
         ART_HYB_PROPS_1['hyb_str'],
         hybformat_id=True,
@@ -544,7 +620,10 @@ def test_hybrecord_misc_private():
     )
     read_flags = test_record._read_flags('bad_flag=B;', allow_undefined_flags=True)
     assert read_flags['bad_flag'] == 'B'
-    test_record._flagset = None
+    # If you change test_record.flagset, it treats it as an instance variable and breaks
+    #   class-level assignment. So have to use type() to change it.
+    type(test_record)._flagset = None
+    assert test_record.flags
     assert (test_record._get_flag_keys(reorder_flags=True)
             == ['read_count', 'seg1_type', 'seg2_type', 'dataset'])
     assert (test_record._get_flag_keys(reorder_flags=False)
diff --git a/auto_tests/test_TypeFinder.py b/auto_tests/test_TypeFinder.py
index 4d04eb3..e763d28 100755
--- a/auto_tests/test_TypeFinder.py
+++ b/auto_tests/test_TypeFinder.py
@@ -7,38 +7,40 @@
 Automatic testing of the hybkit TypeFinder class.
 """
 
-import os
-import sys
 import copy
-from contextlib import nullcontext as does_not_raise
-import pytest
-import hybkit
-
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, hyb_file_name
+import os
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+import pytest
 
+import hybkit
+from auto_tests.test_helper_data import (
+    ART_HYB_MATCHTYPE_PROPS,
+    ART_HYB_NOTYPE_PROPS,
+    ART_HYB_PROPS_ALL,
+    BAD_ID_MAP_PARAMS_1,
+    BAD_ID_MAP_PARAMS_2,
+    BAD_STRING_MATCH_PARAMS_1,
+    BAD_STRING_MATCH_PARAMS_2,
+    HYB_STR_1,
+    ID_MAP_PARAMS_1,
+    STRING_MATCH_PARAMS_1,
+)
+from auto_tests.test_helper_functions import get_expected_result_context
+from hybkit.errors import HybkitArgError, HybkitMiscError
+
+# ----- Linting Directives -----
+# ruff: noqa: SLF001 ARG001
 
 # ----- TypeFinder Misc - Minimal -----
 def test_typefinder_misc():
     """Test construction of HybRecord class with minimal information."""
-    Type_Finder = hybkit.type_finder.TypeFinder
+    type_finder = hybkit.type_finder.TypeFinder
     with pytest.raises((RuntimeError, TypeError)):
-        Type_Finder()
-    Type_Finder.set_custom_method(print)
-    Type_Finder._reset()
+        type_finder()
+    type_finder.set_custom_method(print)
+    type_finder._reset()
     with pytest.raises((RuntimeError, TypeError)):
-        Type_Finder.find({})
+        type_finder.find({})
 
 
 test_parameters = [
@@ -50,19 +52,20 @@ def test_typefinder_misc():
      BAD_STRING_MATCH_PARAMS_2['params_str'], BAD_STRING_MATCH_PARAMS_2['params_dict']),
     ('make_id_map_params', 'Pass',
      ID_MAP_PARAMS_1['params_str'], ID_MAP_PARAMS_1['params_dict']),
-    ('make_id_map_params', 'Raise',
+    ('make_id_map_params', 'HybkitMiscError',
      BAD_ID_MAP_PARAMS_1['params_str'], BAD_ID_MAP_PARAMS_1['params_dict']),
-    ('make_id_map_params', 'Raise',
+    ('make_id_map_params', 'HybkitMiscError',
      BAD_ID_MAP_PARAMS_2['params_str'], BAD_ID_MAP_PARAMS_2['params_dict']),
 ]
-arg_string = "method,expect_str,params_str,params_dict"
+arg_string = 'method,expect_str,params_str,params_dict'
 
 
 @pytest.mark.parametrize(arg_string, [*test_parameters])
 def test_typefinder_make_params(method, expect_str, params_str, params_dict, tmp_path):
+    """Test TypeFinder.make_params methods."""
     make_params_autotest_file_name = os.path.join(tmp_path, 'make_params_autotest_file.txt')
     expect_context = get_expected_result_context(expect_str)
-    with pytest.raises(TypeError):
+    with pytest.raises((HybkitArgError, TypeError)):
         gen_params = getattr(hybkit.type_finder.TypeFinder, method)(23)
     with pytest.raises(FileNotFoundError):
         gen_params = getattr(hybkit.type_finder.TypeFinder, method)(make_params_autotest_file_name)
@@ -74,11 +77,11 @@ def test_typefinder_make_params(method, expect_str, params_str, params_dict, tmp
 
 
 test_parameter_sets = [
-    ('badmethod', 'Raise', None, {}),
+    ('badmethod', 'HybkitArgError', None, {}),
     ('hybformat', 'Pass', None, {}),
-    ('hybformat', 'Raise', {'badparam': True}, {}),
+    ('hybformat', 'HybkitArgError', {'badparam': True}, {}),
     ('string_match', 'Pass', STRING_MATCH_PARAMS_1['params_dict'], {}),
-    ('string_match', 'Raise', {}, {}),
+    ('string_match', 'HybkitArgError', {}, {}),
     # ('string_match', 'Raise', BAD_STRING_MATCH_PARAMS_1['params_dict'], {}),
     # ('string_match', 'Raise', BAD_STRING_MATCH_PARAMS_2['params_dict'], {}),
     ('id_map', 'Pass', ID_MAP_PARAMS_1['params_dict'], {}),
@@ -99,11 +102,12 @@ def test_typefinder_make_params(method, expect_str, params_str, params_dict, tmp
 for test_parameter_set in test_parameter_sets:
     for test_record_set in test_records:
         test_parameters.append((*test_parameter_set, test_record_set))
-arg_string = "method,expect_str,method_params,test_params,hyb_record_params"
+arg_string = 'method,expect_str,method_params,test_params,hyb_record_params'
 
 
 @pytest.mark.parametrize(arg_string, [*test_parameters])
 def test_typefinder_methods(method, expect_str, method_params, test_params, hyb_record_params):
+    """Test TypeFinder methods."""
     expect_context = get_expected_result_context(expect_str)
     hybkit.type_finder.TypeFinder._reset()
     with expect_context:
@@ -131,5 +135,5 @@ def test_typefinder_methods(method, expect_str, method_params, test_params, hyb_
 
             if test_seg1_type is None:
                 use_test_hyb_record_2 = copy.deepcopy(test_hyb_record)
-                with pytest.raises(RuntimeError):
+                with pytest.raises(HybkitMiscError):
                     use_test_hyb_record_2.eval_types()
diff --git a/auto_tests/test_ViennaFile.py b/auto_tests/test_ViennaFile.py
index a71a646..df4fc12 100755
--- a/auto_tests/test_ViennaFile.py
+++ b/auto_tests/test_ViennaFile.py
@@ -8,27 +8,16 @@
 """
 
 import os
-import sys
-import copy
-from contextlib import nullcontext as does_not_raise
-import argparse
-import pytest
-import hybkit
 
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, vienna_autotest_file_name, hyb_file_name
+import pytest
 
+import hybkit
+from auto_tests.test_helper_data import ART_HYB_VIENNA_PROPS_1, ART_HYB_VIENNA_PROPS_2, test_out_dir
+from auto_tests.test_helper_functions import get_expected_result_context
+from hybkit.errors import HybkitMiscError
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001
 
 hybkit.util.set_setting('error_mode', 'raise')
 hybkit.util.set_setting('iter_error_mode', 'raise')
@@ -36,18 +25,19 @@
 
 # ----- Begin ViennaFile Tests -----
 test_parameters = [
-    ('bad_type', 'Raise', {'seq_type': 'badtype'}),
-    ('bad_error_mode', 'Raise', {'error_mode': 'badmode'}),
+    ('bad_type', 'HybkitArgError', {'seq_type': 'badtype'}),
+    ('bad_error_mode', 'HybkitArgError', {'error_mode': 'badmode'}),
 ]
 
 
-@pytest.mark.parametrize("test_name,expectation,test_kwargs", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'expectation', 'test_kwargs'), [*test_parameters])
 # ----- Test Misc Properties of ViennaFiles -----
 def test_viennafile_constructor_misc(test_name, expectation, test_kwargs, tmp_path):
+    """Test miscellaneous properties of ViennaFiles."""
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     expect_context = get_expected_result_context(expectation)
     with expect_context:
-        vienna_file = hybkit.ViennaFile.open(
+        _vienna_file = hybkit.ViennaFile.open(
             vienna_autotest_file_name, 'w',
             **test_kwargs
         )
@@ -65,8 +55,9 @@ def test_viennafile_constructor_misc(test_name, expectation, test_kwargs, tmp_pa
     )
 
 
-@pytest.mark.parametrize("test_name,expectation,test_props", [*test_parameters])
+@pytest.mark.parametrize(('test_name', 'expectation', 'test_props'), [*test_parameters])
 def test_viennafile_io(test_name, expectation, test_props, tmp_path):
+    """Test reading and writing of ViennaFile objects."""
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     if not os.path.isdir(test_out_dir):
         os.mkdir(test_out_dir)
@@ -97,7 +88,8 @@ def test_viennafile_io(test_name, expectation, test_props, tmp_path):
 
 # ----- Test ViennaFile Misc -----
 def test_viennafile_misc(tmp_path):
+    """Test miscellaneous properties of ViennaFiles."""
     vienna_autotest_file_name = os.path.join(tmp_path, 'vienna_autotest_file.vienna')
     vienna_file = hybkit.ViennaFile.open(vienna_autotest_file_name, 'w')
-    with pytest.raises(RuntimeError):
-        vienna_file._ensure_FoldRecord(None)
+    with pytest.raises(HybkitMiscError):
+        vienna_file._ensure_foldrecord(None)
diff --git a/auto_tests/test_helper_data.py b/auto_tests/test_helper_data.py
index b4ff8bb..8642cbb 100755
--- a/auto_tests/test_helper_data.py
+++ b/auto_tests/test_helper_data.py
@@ -9,7 +9,60 @@
 
 import copy
 import os
-from contextlib import nullcontext as does_not_raise
+
+from hybkit.errors import (
+    HybkitArgError,
+    HybkitConstructorError,
+    HybkitError,
+    HybkitIterError,
+    HybkitMiscError,
+)
+
+# from contextlib import nullcontext as does_not_raise
+
+# ----- Start Data Constants -----
+NEG_10 = -10.0
+ZERO = 0.0
+HALF = 0.5
+ONE = 1.0
+TWO = 2.0
+TWO_I = 2
+FIVE = 5.0
+FIVE_I = 5
+TEN = 10.0
+TEN_I = 10
+FORTY_I = 40
+
+# ----- Start Error Constants -----
+ERROR_TYPE_STRINGS = {
+    'HybkitError': HybkitError,
+    'HybkitMiscError': HybkitMiscError,
+    'HybkitArgError': HybkitArgError,
+    'HybkitConstructorError': HybkitConstructorError,
+    'HybkitIterError': HybkitIterError,
+}
+for init_str in list(ERROR_TYPE_STRINGS):
+    ERROR_TYPE_STRINGS[init_str.lower()] = ERROR_TYPE_STRINGS[init_str]
+
+# ----- Start Expected Fields Header -----
+EXPECTED_FIELDS_HEADER = (
+    'id',
+    'seq',
+    'energy',
+    'seg1_ref_name',
+    'seg1_read_start',
+    'seg1_read_end',
+    'seg1_ref_start',
+    'seg1_ref_end',
+    'seg1_score',
+    'seg2_ref_name',
+    'seg2_read_start',
+    'seg2_read_end',
+    'seg2_ref_start',
+    'seg2_ref_end',
+    'seg2_score',
+    'flags',
+)
 
 # ----- Start Real Hyb/Vienna Data Examples -----
 # Example hyb record string for testing.
@@ -310,10 +363,10 @@
 
 ART_HYB_STR_PROPS['false_prop_argsets'] = [
     (vals[0], (vals[1] + 'XXX')) for vals
-    in ART_HYB_STR_PROPS['true_prop_argsets'] if len(vals) == 2
+    in ART_HYB_STR_PROPS['true_prop_argsets'] if len(vals) == TWO
 ]
 
-# Add bad hyb stirngs with missing name or seq
+# Add bad hyb strings with missing name or seq
 ART_BAD_HYB_STRS = []
 for i in range(2):
     source_str = ART_HYB_PROPS_1['hyb_str']
@@ -467,7 +520,7 @@
     'ref_end': 21,
     'score': 'e-10'
 }
-TEST_SEG_PROPS_STR = {p: str(TEST_SEG_PROPS[p]) for p in TEST_SEG_PROPS.keys()}
+TEST_SEG_PROPS_STR = {p: str(TEST_SEG_PROPS[p]) for p in TEST_SEG_PROPS}
 TEST_FLAGS_OBJ = {
     'count_total': 10,
     'count_last_clustering': 11,
@@ -490,7 +543,7 @@
 TEST_RECORD_COUNT = TEST_FLAGS_OBJ['count_total']
 TEST_RECORD_COUNT_STR = str(TEST_RECORD_COUNT)
 TEST_FLAGS_STR = {flag: str(TEST_FLAGS_OBJ[flag])
-                  for flag in TEST_FLAGS_OBJ.keys()}
+                  for flag in TEST_FLAGS_OBJ}
 TEST_FLAGS_STR_LINE = (
     'count_total=10;count_last_clustering=11;two_way_merged=TRUE;'
     + 'seq_IDs_in_cluster=test_id_1,test_id_2;'
@@ -500,6 +553,36 @@
 )
 TEST_FLAGS_STR_REVERSED = {flag: TEST_FLAGS_STR[flag] for flag
                            in reversed([*TEST_FLAGS_STR.keys()])}
+TEST_HYB_FIELDS = {
+    'id': TEST_HYB_ID_STR,
+    'seq': TEST_SEQ_STR,
+    'energy': TEST_ENERGY_STR,
+    'seg1_ref_name': TEST_SEG_PROPS['ref_name'],
+    'seg1_read_start': TEST_SEG_PROPS['read_start'],
+    'seg1_read_end': TEST_SEG_PROPS['read_end'],
+    'seg1_ref_start': TEST_SEG_PROPS['ref_start'],
+    'seg1_ref_end': TEST_SEG_PROPS['ref_end'],
+    'seg1_score': TEST_SEG_PROPS['score'],
+    'seg2_ref_name': TEST_SEG_PROPS['ref_name'],
+    'seg2_read_start': TEST_SEG_PROPS['read_start'],
+    'seg2_read_end': TEST_SEG_PROPS['read_end'],
+    'seg2_ref_start': TEST_SEG_PROPS['ref_start'],
+    'seg2_ref_end': TEST_SEG_PROPS['ref_end'],
+    'seg2_score': TEST_SEG_PROPS['score'],
+    'flags': TEST_FLAGS_STR,
+}
+
+TEST_HYB_LINE = (
+    f'{TEST_HYB_ID_STR}\t{TEST_SEQ_STR}\t{TEST_ENERGY_STR}\t'
+    f'{TEST_SEG_PROPS_STR["ref_name"]}\t{TEST_SEG_PROPS_STR["read_start"]}\t'
+    f'{TEST_SEG_PROPS_STR["read_end"]}\t{TEST_SEG_PROPS_STR["ref_start"]}\t'
+    f'{TEST_SEG_PROPS_STR["ref_end"]}\t{TEST_SEG_PROPS_STR["score"]}\t'
+    f'{TEST_SEG_PROPS_STR["ref_name"]}\t{TEST_SEG_PROPS_STR["read_start"]}\t'
+    f'{TEST_SEG_PROPS_STR["read_end"]}\t{TEST_SEG_PROPS_STR["ref_start"]}\t'
+    f'{TEST_SEG_PROPS_STR["ref_end"]}\t{TEST_SEG_PROPS_STR["score"]}\t'
+    f'{TEST_FLAGS_STR_LINE}'
+)
+
 TEST_OBJECTS = {
     'None': None,
     'id_str': TEST_HYB_ID_STR,
diff --git a/auto_tests/test_helper_functions.py b/auto_tests/test_helper_functions.py
index d6985b9..05a574d 100755
--- a/auto_tests/test_helper_functions.py
+++ b/auto_tests/test_helper_functions.py
@@ -7,44 +7,22 @@
 Helper functions for automatic testing of hybkit code.
 """
 
-import copy
-import os
-import hybkit
-import pytest
 from contextlib import nullcontext as does_not_raise
 
-import test_helper_data
-
-# ----- Test Assistance Functions -----
-# Generate HybRecord and FoldRecord objects for testing.
-
-# def default_hyb_records():
-#     """Generate two HybRecord objects for tests"""
-#     hyb_record_1 = hybkit.HybRecord.from_line(
-#         HYB_STR_1,
-#         hybformat_id=True,
-#         hybformat_ref=True,
-#     )
-#     fold_record = None
-#     #fold_record = hybkit.DynamicFoldRecord.from_vienna_string(VIENNA_STR_1)
+import pytest
+from test_helper_data import ERROR_TYPE_STRINGS
 
-#     hyb_record_2 = hybkit.HybRecord(
-#         id=hyb_record_1.id,
-#         seq=hyb_record_1.seq,
-#         seg1_props=copy.deepcopy(hyb_record_1.seg1_props),
-#         seg2_props=copy.deepcopy(hyb_record_1.seg2_props),
-#         fold_record=fold_record,
-#     )
-#     return hyb_record_1, hyb_record_2, fold_record
+# import test_helper_data
 
+# import hybkit
 
 # Get expected result string for exception testing.
-def get_expected_result_string(is_allowed=False):
-    """Return string identifying expected pass/error result"""
+def get_expected_result_string(is_allowed=False, err_string='Raise'):
+    """Return string identifying expected pass/error result."""
     if is_allowed:
         return 'Pass'
     else:
-        return 'Raise'
+        return err_string
 
 
 # Get expected result context for exception testing.
@@ -58,3 +36,9 @@ def get_expected_result_context(expect_str, error_types=(TypeError, RuntimeError
         elif not isinstance(error_types, tuple):
             error_types = (error_types,)
         return pytest.raises(error_types)
+    elif expect_str.lower() in ERROR_TYPE_STRINGS:
+        return pytest.raises(ERROR_TYPE_STRINGS[expect_str.lower()])
+    else:
+        message = 'Expected result string must be "Pass", "Raise", or one of: '
+        message += f'{ERROR_TYPE_STRINGS.keys()}'
+        raise ValueError(message)
diff --git a/auto_tests/test_util.py b/auto_tests/test_util.py
index fb9919b..a82578f 100755
--- a/auto_tests/test_util.py
+++ b/auto_tests/test_util.py
@@ -7,38 +7,34 @@
 Automatic testing of hybkit code.
 """
 
-import os
-import sys
-import copy
-from contextlib import nullcontext as does_not_raise
+# import sys
+# import copy
+# from contextlib import nullcontext as does_not_raise
 import argparse
-import pytest
-import hybkit
-
-
-# ----- Import Testing Helper Data -----
-from auto_tests.test_helper_data import *
-# Includes the following variables:
-# TEST_HYBID_STR, TEST_SEQ_STR, TEST_FOLD_STR, TEST_ENERGY_STR
-# ART_HYB_PROPS_1, ART_HYB_PROPS_ALL, ART_BAD_HYB_STRS
-# ID_ALLOWED_TYPES, SEQ_ALLOWED_TYPES, FOLD_ALLOWED_TYPES, ENERGY_ALLOWED_TYPES
-# test_out_dir, hyb_autotest_file_name, test_hyb_file_name
+import os
 
+import pytest
 
-# ----- Import Testing Helper Functions -----
-from auto_tests.test_helper_functions import *
-# Includes the following functions:
-# get_expected_result_string(is_allowed=False)
-# get_expected_result_context(expect_str, error_types = (TypeError, RuntimeError))
+import hybkit
+from auto_tests.test_helper_data import (
+    test_ct_file_name,
+    test_hyb_file_name,
+    test_out_dir,
+    test_vienna_file_name,
+)
+from auto_tests.test_helper_functions import get_expected_result_context
 
+# ----- Linting Directives:
+# ruff: noqa: SLF001 ARG001 FBT003
 
 # ----- Start Test Util -----
 def test_util_misc():
-    original_abspath = hybkit.settings._USE_ABSPATH
+    """Test miscellaneous functions in hybkit.util."""
+    _original_abspath = hybkit.settings._USE_ABSPATH
     hybkit.settings._USE_ABSPATH = True
 
     # Test get_argparse_doc
-    source_str = ":ref:`|==``::\n::\n"
+    source_str = ':ref:`|==``::\n::\n'
     assert hybkit.util.get_argparse_doc(source_str) == '":\n'
 
     # Test _bool_from_string
@@ -131,25 +127,26 @@ def test_util_misc():
 ]
 test_parameters = []
 for source, settings_info_dict in settings_info_dicts:
-    for setting in settings_info_dict.keys():
+    for setting in settings_info_dict:
         default_value, description, type_str = settings_info_dict[setting][:3]
         short_flag, argparse_fields = settings_info_dict[setting][3:]
         if 'choices' in argparse_fields:
-            good_choice = list(argparse_fields['choices'])[0]
+            good_choice = next(iter(argparse_fields['choices']))
             bad_choice_1, bad_choice_2 = 'invalid', ['invalid']
         else:
             good_choice = default_value
             bad_choice_1, bad_choice_2 = None, None
         test_parameters.append((source, setting, 'Pass', good_choice, settings_info_dict[setting]))
         if bad_choice_1 is not None:
-            test_parameters.append((source, setting, 'Raise',
+            test_parameters.append((source, setting, 'HybkitArgError',
                                     bad_choice_1, settings_info_dict[setting]))
-            test_parameters.append((source, setting, 'Raise',
+            test_parameters.append((source, setting, 'HybkitArgError',
                                     bad_choice_2, settings_info_dict[setting]))
+use_parameters = ('source', 'setting', 'expectation', 'set_val', 'setting_props')
 
-
-@pytest.mark.parametrize("source,setting,expectation,set_val,setting_props", [*test_parameters])
+@pytest.mark.parametrize(use_parameters, [*test_parameters])
 def test_util_set_settings(source, setting, expectation, set_val, setting_props):
+    """Test hybkit.util.set_settings_from_namespace."""
     expect_context = get_expected_result_context(expectation)
     use_namespace = argparse.Namespace()
     setattr(use_namespace, setting, set_val)
@@ -166,6 +163,7 @@ def test_util_set_settings(source, setting, expectation, set_val, setting_props)
 
 # ----- Test hybkit.util parser generation
 def test_util_validate_args():
+    """Test hybkit.util parser generation."""
     original_abspath = hybkit.settings._USE_ABSPATH
     hybkit.settings._USE_ABSPATH = True