Repository: claritychallenge/clarity (language: python)

Issue:
Jpb/220 enable some pylint error checking in run tests GitHub workflow
Merging this branch as it has already fixed some of the more serious pylint issues.


Discussion / hints:

Fix patch:
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 4b7add1b3..af760092a 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1,3 +1,34 @@
+# pylint compliance (https://github.com/claritychallenge/clarity/pull/224)
+1c4f7def50b63e449c1292ebcb1072a333752dc2 # Ignoring commit that fix pylint errors
+bd5f3068d3d149a2c169bcf2fe1ba42e00f04c22 # Dealt with F6401 false positives, and final, final missed issues.
+1e4d8d6b634cb08d2c75d8b360bef1a87a03f83c # Mopped up a few missed issues
+072b2ca24c3c8e7e1bd5efd2c2a199440a2184bf # Disabled missing docstrings from .pylintrc.precommit.ini
+e14f2306c6ad98ae06628f249911b4f6b3ecd6a0 # Added the .pylintrc to use for precommit
+77cb687d09721d32dec33df03f9aa260b21b533b # Dealt with W0223 missing abstract method definition warnings
+08f04ac78b576bb254a7f213de7a4b5589223921 # Fixed error introduce when dealing with exceptions
+b80f63be56d4911c1222371d815b232e94eff339 # Fixed W0221, W0222, W0237 overridden signature change issues
+154f398410bdc391b8e84f304ca4112260627434 # Fixed all W0718 and W0719 exception issues
+d2b7285866fad9e5adf140e319ebbce2f4cda374 # Fixing long line wrapping on merged commits
+eea79f3ea082c505ef76ccd699a0e47e5ee6e8ac # Merge branch 'main' into 222-fix-issues-raised-by-pylint
+23d6ef58bfdf3b07089790a2da9de6da7961a9fb # Fixed W0106 expression not assigned issue
+b05d8d7949d3300db3e8a320f4748a7701648e9a # Fixed all R1725 removing default values from super() issues
+885a3b4aa23986b4d91f29b081c3c2dd8207d713 # Fixed all C0201, C0206 - dictionary iteration issues
+4f5de575980cdce4635674986a0424113615d4a3 # Fixed all C0209 f-string issues
+42d3d86565da49bb068c38f9d5cea1696933a6fa # Fixed test that was broken by previous commit
+3a9a3116bb786e432e19c9aa502955d28c797602 # Added torch<2.0 in setup.cfg to stay on old version
+ec57cda3e9be1b5005893926bd0e36b031de9485 # Fixed a bunch of rare pylint issues. See below.
+4e9889b8e6b024fe30e3a9a20579409214eb9630 # Fixed all W0612 and W0613 unused vars and params issues
+5e491d7db7ef76b40826f26eedd2eb623cac4664 # Fixed all W0201: Attribute defined outside __init__
+00c36d1e6e646af0cbbf2f5fcdb19f42b6b7468b # Fixed all R1732 consider using with issues
+7bc62369914dd71d802ff315d7c0d3523bd6808f # Fixed all R1705 Unnecessary else after return issues
+abd4992d14ed357bde180132f80c3772ba65fe3a # Fixed all C0200 use enumerate issues
+626af27bea3ae56f0a7b4bab911d417d098c5d26 # Fixed all W1514 using open without explicit encoding issues
+18ff821ce058be8011d23436126ee0f456c56902 # Fixed all W0621 redefined from outer scoper issues
+73cdab6028d5a8320ae3031c4e28cc933b5f90af # Install pylint-pytest to fix pytest related false positives
+65f263d073f1e6c6d03cafd6e2f2232ecf4cd36b # Fixed all lines over the 88 character limit used in Black
+3ddfdab42496db024610d265120639ac6731c8c6 # Merge pull request #223 from claritychallenge/jpb/220-enable-some-pylint-error-checking-in-run_tests-github-workflow
+
+
 # PEP8 compliance - clarity/evaluators/hasqi
 14e21eff76a1350cabaa19ad26f2556892d51761
 961846ca661b5b97f0d1a75585452aed6f15c5f7
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f2662a4c4..06e1fcf59 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -82,11 +82,12 @@ repos:
   - repo: local
     hooks:
       - id: pylint_warnings
-        args: ["--rcfile=.pylintrc"]
+        args: ["--rcfile=.pylintrc.precommit.ini"]
         name: pylint-warnings
         entry: python -m pylint
         language: system
         files: \.py$
+        exclude: clarity/_version.py # 3rd party code that doesn't conform
 ci:
   autofix_prs: true
   autofix_commit_msg: '[pre-commit.ci] Fixing issues with pre-commit'
diff --git a/.pylintrc b/.pylintrc
index 544e4bd89..9a531134d 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -28,7 +28,7 @@ ignore-paths=
 
 # Files or directories matching the regex patterns are skipped. The regex
 # matches against base names, not paths.
-ignore-patterns=
+ignore-patterns=clarity/_version.py
 
 # Python code to execute, usually for sys.path manipulation such as
 # pygtk.require().
@@ -45,7 +45,7 @@ limit-inference-results=100
 
 # List of plugins (as comma separated values of python module names) to load,
 # usually to register additional checkers.
-load-plugins=
+load-plugins=pylint_pytest
 
 # Pickle collected data for later comparisons.
 persistent=yes
@@ -88,7 +88,7 @@ disable=R0801,
         useless-suppression,
         deprecated-pragma,
         use-symbolic-message-instead,
-        logging-fstring-interpolation
+        # logging-fstring-interpolation
 
 # Enable the message, report, category or checker with the given id(s). You can
 # either give multiple identifier separated by comma (,) or put this option
@@ -173,8 +173,7 @@ spelling-store-unknown-words=no
 
 # List of note tags to take in consideration, separated by a comma.
 notes=FIXME,
-      XXX,
-      TODO
+      XXX
 
 # Regular expression of note tags to take in consideration.
 #notes-rgx=
@@ -289,7 +288,7 @@ indent-after-paren=4
 indent-string='    '
 
 # Maximum number of characters on a single line.
-max-line-length=100
+max-line-length=88
 
 # Maximum number of lines in a module.
 max-module-lines=1000
diff --git a/.pylintrc.precommit.ini b/.pylintrc.precommit.ini
new file mode 100644
index 000000000..a0bd126dc
--- /dev/null
+++ b/.pylintrc.precommit.ini
@@ -0,0 +1,602 @@
+[MASTER]
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code.
+extension-pkg-allow-list=
+
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
+# for backward compatibility.)
+extension-pkg-whitelist=
+
+# Return non-zero exit code if any of these messages/categories are detected,
+# even if score is above --fail-under value. Syntax same as enable. Messages
+# specified are enabled, while categories only check already-enabled messages.
+fail-on=
+
+# Specify a score threshold to be exceeded before program exits with error.
+fail-under=10.0
+
+# Files or directories to be skipped. They should be base names, not paths.
+ignore=CVS
+
+# Add files or directories matching the regex patterns to the ignore-list. The
+# regex matches against paths and can be in Posix or Windows format.
+ignore-paths=
+
+# Files or directories matching the regex patterns are skipped. The regex
+# matches against base names, not paths.
+ignore-patterns=clarity/_version.py
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
+# number of processors available to use.
+jobs=1
+
+# Control the amount of potential inferred values when inferring a single
+# object. This can help the performance when dealing with large functions or
+# complex, nested conditions.
+limit-inference-results=100
+
+# List of plugins (as comma separated values of python module names) to load,
+# usually to register additional checkers.
+load-plugins=pylint_pytest
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Minimum Python version to use for version dependent checks. Will default to
+# the version used to run pylint.
+py-version=3.9
+
+# When enabled, pylint would attempt to guess common misconfiguration and emit
+# user-friendly hints instead of false-positive error messages.
+suggestion-mode=yes
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
+confidence=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once). You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use "--disable=all --enable=classes
+# --disable=W".
+disable=R0801,  # duplicate-code
+        R0901,  # too-many-ancestors
+        R0902,  # too-many-instance-attributes
+        R0903,  # too-few-public-methods
+        R0912,  # too-many-branches
+        R0913,  # too-many-arguments
+        R0914,  # too-many-locals
+        R0915,  # too-many-statements
+        C0103,  # invalid-name
+        C0302,  # too-many-lines
+        W1203,  # logging-fstring-interpolation
+        F6401,  # cannot-enumerate-pytest-fixtures - false positives
+        C0114,  # missing-module-docstring # WILL BE REMOVED IN LATER PR
+        C0115,  # missing-class-docstring # WILL BE REMOVED IN LATER PR
+        C0116,  # missing-function-docstring # WILL BE REMOVED IN LATER PR
+        raw-checker-failed,
+        bad-inline-option,
+        locally-disabled,
+        file-ignored,
+        suppressed-message,
+        useless-suppression,
+        deprecated-pragma,
+        use-symbolic-message-instead,
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+enable=c-extension-no-member
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'error', 'warning', 'refactor', and 'convention'
+# which contain the number of messages in each category, as well as 'statement'
+# which is the total number of statements analyzed. This score is used by the
+# global evaluation report (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+#msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
+
+[REFACTORING]
+
+# Maximum number of nested blocks for function / method body
+max-nested-blocks=5
+
+# Complete name of functions that never returns. When checking for
+# inconsistent-return-statements if a never returning function is called then
+# it will be considered as an explicit return statement and no message will be
+# printed.
+never-returning-functions=sys.exit,argparse.parse_error
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it work,
+# install the 'python-enchant' package.
+spelling-dict=
+
+# List of comma separated words that should be considered directives if they
+# appear and the beginning of a comment and should not be checked.
+spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+# We're allowing TODO notes in the code but not FIXME or XXX.
+# (The former just means not done yet, but FIXME suggests a bug)
+notes=FIXME,
+      XXX
+
+# Regular expression of note tags to take in consideration.
+#notes-rgx=
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=torch.*,device
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# class is considered mixin if its name matches the mixin-class-rgx option.
+ignore-mixin-members=yes
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# Regex pattern to define which classes are considered mixins ignore-mixin-
+# members is set to 'yes'
+mixin-class-rgx=.*[Mm]ixin
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[VARIABLES]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=
+
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
+
+# List of names allowed to shadow builtins
+allowed-redefined-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+          _cb
+
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore.
+ignored-argument-names=_.*|^ignored_|^unused_
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
+
+
+[FORMAT]
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+# Maximum number of characters on a single line.
+max-line-length=88
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[SIMILARITIES]
+
+# Comments are removed from the similarity computation
+ignore-comments=yes
+
+# Docstrings are removed from the similarity computation
+ignore-docstrings=yes
+
+# Imports are removed from the similarity computation
+ignore-imports=no
+
+# Signatures are removed from the similarity computation
+ignore-signatures=no
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+
+[BASIC]
+
+# Naming style matching correct argument names.
+argument-naming-style=snake_case
+
+# Regular expression matching correct argument names. Overrides argument-
+# naming-style.
+#argument-rgx=
+
+# Naming style matching correct attribute names.
+attr-naming-style=snake_case
+
+# Regular expression matching correct attribute names. Overrides attr-naming-
+# style.
+#attr-rgx=
+
+# Bad variable names which should always be refused, separated by a comma.
+bad-names=foo,
+          bar,
+          baz,
+          toto,
+          tutu,
+          tata
+
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
+# Naming style matching correct class attribute names.
+class-attribute-naming-style=any
+
+# Regular expression matching correct class attribute names. Overrides class-
+# attribute-naming-style.
+#class-attribute-rgx=
+
+# Naming style matching correct class constant names.
+class-const-naming-style=UPPER_CASE
+
+# Regular expression matching correct class constant names. Overrides class-
+# const-naming-style.
+#class-const-rgx=
+
+# Naming style matching correct class names.
+class-naming-style=PascalCase
+
+# Regular expression matching correct class names. Overrides class-naming-
+# style.
+#class-rgx=
+
+# Naming style matching correct constant names.
+const-naming-style=UPPER_CASE
+
+# Regular expression matching correct constant names. Overrides const-naming-
+# style.
+#const-rgx=
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+# Naming style matching correct function names.
+function-naming-style=snake_case
+
+# Regular expression matching correct function names. Overrides function-
+# naming-style.
+#function-rgx=
+
+# Good variable names which should always be accepted, separated by a comma.
+good-names=a,
+           b,
+           c,
+           df,  # pandas.DataFrame
+           e,   # useful for exceptions
+           i,
+           j,
+           k,
+           m,
+           n,
+           x,
+           fx,
+           y,
+           fy,
+           ex,
+           fp,  # use for file pointers in "with fp:" blocks
+           Run,
+           _
+
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
+# Include a hint for the correct naming format with invalid-name.
+include-naming-hint=no
+
+# Naming style matching correct inline iteration names.
+inlinevar-naming-style=any
+
+# Regular expression matching correct inline iteration names. Overrides
+# inlinevar-naming-style.
+#inlinevar-rgx=
+
+# Naming style matching correct method names.
+method-naming-style=snake_case
+
+# Regular expression matching correct method names. Overrides method-naming-
+# style.
+#method-rgx=
+
+# Naming style matching correct module names.
+module-naming-style=snake_case
+
+# Regular expression matching correct module names. Overrides module-naming-
+# style.
+#module-rgx=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=^_
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+# These decorators are taken in consideration only for invalid-name.
+property-classes=abc.abstractproperty
+
+# Naming style matching correct variable names.
+variable-naming-style=snake_case
+
+# Regular expression matching correct variable names. Overrides variable-
+# naming-style.
+#variable-rgx=
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=
+
+# Output a graph (.gv or any supported image format) of external dependencies
+# to the given file (report RP0402 must not be disabled).
+ext-import-graph=
+
+# Output a graph (.gv or any supported image format) of all (i.e. internal and
+# external) dependencies to the given file (report RP0402 must not be
+# disabled).
+import-graph=
+
+# Output a graph (.gv or any supported image format) of internal dependencies
+# to the given file (report RP0402 must not be disabled).
+
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[CLASSES]
+
+# Warn about protected attribute access inside special methods
+check-protected-access-in-special-methods=no
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp,
+                      __post_init__
+
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[DESIGN]
+
+# List of regular expressions of class ancestor names to ignore when counting
+# public methods (see R0903)
+exclude-too-few-public-methods=
+
+# List of qualified class names to ignore when counting class parents (see
+# R0901)
+ignored-parents=
+
+# Maximum number of arguments for function / method.
+max-args=7  # Raised this from 5
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Maximum number of boolean expressions in an if statement (see R0916).
+max-bool-expr=5
+
+# Maximum number of branch for function / method body.
+max-branches=12
+
+# Maximum number of locals for function / method body.
+max-locals=15
+
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+# Maximum number of return / yield for function / method body.
+max-returns=6
+
+# Maximum number of statements in function / method body.
+max-statements=50
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "BaseException, Exception".
+overgeneral-exceptions=builtins.BaseException,
+                       builtins.Exception
diff --git a/clarity/_version.py b/clarity/_version.py
index 1355ca481..f59d79b08 100644
--- a/clarity/_version.py
+++ b/clarity/_version.py
@@ -8,6 +8,8 @@
 # Generated by versioneer-0.26
 # https://github.com/python-versioneer/python-versioneer
 
+# pylint: disable=W0201  # allow "attribute defined outside __init__"
+
 """Git implementation of _version.py."""
 
 import errno
@@ -88,7 +90,7 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=
         try:
             dispcmd = str([command] + args)
             # remember shell=False, so use git.cmd on windows, not just git
-            process = subprocess.Popen(
+            process = subprocess.Popen(  # pylint: disable=consider-using-with
                 [command] + args,
                 cwd=cwd,
                 env=env,
@@ -157,7 +159,7 @@ def git_get_keywords(versionfile_abs):
     # _version.py.
     keywords = {}
     try:
-        with open(versionfile_abs, "r") as fobj:
+        with open(versionfile_abs, "r", encoding="utf-8") as fobj:
             for line in fobj:
                 if line.strip().startswith("git_refnames ="):
                     mo = re.search(r'=\s*"(.*)"', line)
diff --git a/clarity/data/HOA_tools_cec2.py b/clarity/data/HOA_tools_cec2.py
index 86105a444..ae565b4c0 100644
--- a/clarity/data/HOA_tools_cec2.py
+++ b/clarity/data/HOA_tools_cec2.py
@@ -93,7 +93,8 @@ def compute_rotation_matrix(n: int, foa_rotmat: np.ndarray) -> np.ndarray:
     sub_matrices[1] = foa_rotmat
 
     typed_sub_matrices = TypedList()
-    [typed_sub_matrices.append(x) for x in sub_matrices]
+    for x in sub_matrices:
+        typed_sub_matrices.append(x)
 
     if n > 1:
         for i in np.arange(2, n + 1):
@@ -194,21 +195,22 @@ def V(degree, n, order, rotation_matrices):
     """
     d = 0
     if degree == 0:
-        return P(1, 1, n, order, rotation_matrices) + P(
+        v_coeff = P(1, 1, n, order, rotation_matrices) + P(
             -1, -1, n, order, rotation_matrices
         )
     elif degree > 0:
         if degree == 1:
             d = 1.0
-        return P(1, degree - 1, n, order, rotation_matrices) * np.sqrt(1 + d) - P(
+        v_coeff = P(1, degree - 1, n, order, rotation_matrices) * np.sqrt(1 + d) - P(
             -1, -degree + 1, n, order, rotation_matrices
         ) * (1 - d)
     else:
         if degree == -1:
             d = 1.0
-        return P(1, degree + 1, n, order, rotation_matrices) * (1 - d) + P(
+        v_coeff = P(1, degree + 1, n, order, rotation_matrices) * (1 - d) + P(
             -1, -degree - 1, n, order, rotation_matrices
         ) * np.sqrt(1 + d)
+    return v_coeff
 
 
 @njit
@@ -225,15 +227,16 @@ def W(degree, n, order, rotation_matrices):
         float: W value
     """
     if degree == 0:
-        return 0.0
+        w_value = 0.0
     elif degree > 0:
-        return P(1, degree + 1, n, order, rotation_matrices) + P(
+        w_value = P(1, degree + 1, n, order, rotation_matrices) + P(
             -1, -degree - 1, n, order, rotation_matrices
         )
     else:
-        return P(1, degree - 1, n, order, rotation_matrices) - P(
+        w_value = P(1, degree - 1, n, order, rotation_matrices) - P(
             -1, -degree + 1, n, order, rotation_matrices
         )
+    return w_value
 
 
 @njit
@@ -531,7 +534,7 @@ def rotation_control_vector(
     return np.array(np.floor(idx * (array_length - 1)), dtype=int)
 
 
-def rotation_vector(
+def compute_rotation_vector(
     start_angle: float,
     end_angle: float,
     signal_length: int,
@@ -552,12 +555,8 @@ def rotation_vector(
     """
     turn_direction = -np.sign(start_angle - end_angle)
     with np.errstate(divide="raise"):
-        try:
-            increment = (
-                np.abs(start_angle - end_angle) / signal_length
-            ) * turn_direction
-        except FloatingPointError:
-            raise
+        increment = (np.abs(start_angle - end_angle) / signal_length) * turn_direction
+
     theta = np.arange(start_angle, end_angle, increment)
     idx = rotation_control_vector(signal_length, start_idx, end_idx)
     return theta[idx]
diff --git a/clarity/data/demo_data.py b/clarity/data/demo_data.py
index 7fcc54b96..3c9575826 100644
--- a/clarity/data/demo_data.py
+++ b/clarity/data/demo_data.py
@@ -12,9 +12,9 @@ def get_demo_data(metadata_url: str, target_dir: str) -> None:
     """Download demo data.
 
     Args:
-        metadata_url (str): URL to download data from (should be a link on Google Drive).
-        target_dir (str): Directory to save to (default 'clarity_data/demo'), will be created
-            if it doesn't exist.
+        metadata_url (str): URL to download data from (should be a link on Google Drive)
+        target_dir (str): Directory to save to (default 'clarity_data/demo'), will be
+            created if it doesn't exist.
 
     Returns:
         None
@@ -29,35 +29,53 @@ def get_demo_data(metadata_url: str, target_dir: str) -> None:
 
 def get_metadata_demo(target_dir: str = TARGET_DIR) -> None:
     """Download metadata."""
-    url = "https://drive.google.com/uc?export=download&id=14KGm2GaRgwlrZvtmMwWTYu7itaRVQV8f"
+    url = (
+        "https://drive.google.com/"
+        "uc?export=download&id=14KGm2GaRgwlrZvtmMwWTYu7itaRVQV8f"
+    )
     get_demo_data(url, target_dir)
 
 
 def get_targets_demo(target_dir: str = TARGET_DIR) -> None:
     """Download targets."""
-    url = "https://drive.google.com/uc?export=download&id=1uu2Hes1fzqNaZSCiFNhxZM3bE_fAVKsD"
+    url = (
+        "https://drive.google.com/"
+        "uc?export=download&id=1uu2Hes1fzqNaZSCiFNhxZM3bE_fAVKsD"
+    )
     get_demo_data(url, target_dir)
 
 
 def get_interferers_demo(target_dir: str = TARGET_DIR) -> None:
     """Download interferers."""
-    url = "https://drive.google.com/uc?export=download&id=1_ssD238Qv-EETzC0hJze7JhLE7bHyqwG"
+    url = (
+        "https://drive.google.com/"
+        "uc?export=download&id=1_ssD238Qv-EETzC0hJze7JhLE7bHyqwG"
+    )
     get_demo_data(url, target_dir)
 
 
 def get_rooms_demo(target_dir: str = TARGET_DIR) -> None:
     """Download rooms."""
-    url = "https://drive.google.com/uc?export=download&id=1FBC8DI4Ru-g3Set0fDzoKmXTqHqNXV8n"
+    url = (
+        "https://drive.google.com/"
+        "uc?export=download&id=1FBC8DI4Ru-g3Set0fDzoKmXTqHqNXV8n"
+    )
     get_demo_data(url, target_dir)
 
 
 def get_scenes_demo(target_dir: str = TARGET_DIR) -> None:
     """Download secnes."""
-    url = "https://drive.google.com/uc?export=download&id=1PB0CfGXhpkYNk8HbE5lTWowm2016x6Hl"
+    url = (
+        "https://drive.google.com/"
+        "uc?export=download&id=1PB0CfGXhpkYNk8HbE5lTWowm2016x6Hl"
+    )
     get_demo_data(url, target_dir)
 
 
 def get_hrirs_demo(target_dir: str = TARGET_DIR) -> None:
     """Download hiris."""
-    url = "https://drive.google.com/uc?export=download&id=1USrHLFhOE_jdAQcEKqumG3M5dEZVcwSd"
+    url = (
+        "https://drive.google.com/"
+        "uc?export=download&id=1USrHLFhOE_jdAQcEKqumG3M5dEZVcwSd"
+    )
     get_demo_data(url, target_dir)
diff --git a/clarity/data/scene_builder_cec2.py b/clarity/data/scene_builder_cec2.py
index 886c4dbf2..050bd9d36 100644
--- a/clarity/data/scene_builder_cec2.py
+++ b/clarity/data/scene_builder_cec2.py
@@ -62,8 +62,8 @@ def get_room_dims(text: str) -> List:
     """Find the room dimensions in the rpf file.
 
     Args:
-        text (str): String to be searched for room dimensions (string to be searched for is of the form 'ProjectName =
-    CuboidRoom_5.9x3.4186x2.9').
+        text (str): String to be searched for room dimensions (string to be searched
+            for is of the form 'ProjectName = CuboidRoom_5.9x3.4186x2.9').
 
 
     Returns:
@@ -94,7 +94,8 @@ def read_rpf_file(rpf_filename: str) -> Dict:
         rpf_filename (str): Path to an rpf file to be read.
 
     Returns:
-        dict: dictionary of rpf file contents {"position": sourcePositions, "view_vector": sourceViewVectors}
+        dict: dictionary of rpf file contents
+            {"position": sourcePositions, "view_vector": sourceViewVectors}
     """
     with open(rpf_filename, "r", encoding="utf-8") as f:
         text = f.read()
@@ -190,7 +191,7 @@ def get_num_pre_samples(pre_samples_range: list) -> int:
     """Number of samples prior to target onset.
 
     Args:
-        pre_samples_range (list): parameters for number of samples priot to target onset.
+        pre_samples_range (list): parameters for number of samples prior to target onset
 
     Returns:
     """
@@ -214,13 +215,13 @@ def add_this_target_to_scene(
 ):
     """Add the target details to the scene dict.
 
-    Adds given target to given scene. Target details will be taken from the target dict but
-    the start time will be according to the CEC2 target start time specification.
+    Adds given target to given scene. Target details will be taken from the target dict
+    but the start time will be according to the CEC2 target start time specification.
 
     Args:
         target (dict): target dict read from target metadata file.
         scene (dict): complete scene dictionary.
-        pre_samples_range (list): parameters for number of samples prior to target onset.
+        pre_samples_range (list): parameters for number of samples prior to target onset
         post_samples_range (list): parameters for number of samples to continue
             player after target offsets.
     """
@@ -236,11 +237,10 @@ def add_this_target_to_scene(
 
 # SNR handling
 def generate_snr(snr_range: List[int]) -> float:
-    """Generate a random Signal Noise Ratio.
+    """Generate a random Signal Noise Ratio (SNR).
 
     Args:
-        snr_range (list): Range of values for a Uniform distribution from which to sample
-            Signal Noise Ratio.
+        snr_range (list): Range from which to uniformly sample SNR.
 
     Returns:
         float: random number from uniform distribution in given range.
@@ -367,8 +367,10 @@ def add_interferer_to_scene_inner(
         scene (dict): the scene description
         interferers (dict): the interferer metadata
         number (list): number of interferers
-        start_time_range (list): range of starting points as integers, a random number is selected between these.
-        end_early_time_range (list): range of end points as integers, a random number is selected between these.
+        start_time_range (list): range of starting points as integers, a random number
+            is selected between these.
+        end_early_time_range (list): range of end points as integers, a random number
+            is selected between these.
     """
     dataset = scene["dataset"]
     selected_interferer_types = select_interferer_types(number)
@@ -429,7 +431,8 @@ def generate_rotation(
 
     Args:
         scene (dict): the scene description
-        relative_start_time_range (list): Range from which start time is selected at random.
+        relative_start_time_range (list): Range from which start time is
+            uniformly selected at random.
         duration_mean (float): mean of the time offset for start of turn
         duration_sd (float): standard deviation of the time offset for start of turn
         angle_initial_mean (float):
@@ -594,7 +597,7 @@ def save_scenes(self, filename: str) -> None:
         Returns:
             None
         """
-        scenes = [s for s in self.scenes]
+        scenes = self.scenes.copy()
         # Replace the room structure with the room ID
         for scene in scenes:
             scene["room"] = scene["room"]["name"]
@@ -602,7 +605,7 @@ def save_scenes(self, filename: str) -> None:
             json.dump(self.scenes, f, indent=2)
 
     def instantiate_scenes(self, dataset) -> None:
-        """Instantiate scenes with targets, interferers, signal noise ratio and listeners.
+        """Instantiate scenes with targets, interferers, SNR and listeners.
 
         Args:
             dataset:
@@ -665,15 +668,18 @@ def add_target_to_scene(
         post_samples_range: list,
     ):
         """Add target info to the scenes.
-        Target speaker file set via config.
+
+        Uses target speaker file set via config.
 
         Args:
             dataset (str): dataset to be added.
             target_speakers (str):
-            target_selection (str): Type of target to be added, valid values are 'SEQUENTIAL' and 'RANDOM'.
-            pre_samples_range (list): Parameters for number of samples prior to target onset.
-            post_samples_range (list): Parameters for number osamples to continue player after target offsets.
-
+            target_selection (str): Type of target to be added, valid values are
+                'SEQUENTIAL' and 'RANDOM'.
+            pre_samples_range (list): Parameters for number of samples prior to target
+                onset.
+            post_samples_range (list): Parameters for number of samples to continue
+                player after target offsets.
 
         Raises: TypeError if room_selection is not SEQUENTIAL or RANDOM
         """
@@ -704,10 +710,10 @@ def add_target_to_scene(
             raise TypeError(f"Invalid room selection mode: {target_selection}")
 
     def add_SNR_to_scene(self, snr_range: list):
-        """Add the Signal Noise Ratio info to the scenes.
+        """Add the Signal Noise Ratio (SNR) info to the scenes.
 
         Args:
-            snr_range (list): Range of values from which Signal Noise Ratio will be sampled.
+            snr_range (list): Range of values from which SNR will be sampled.
 
         Returns:
         """
@@ -792,7 +798,8 @@ def add_listener_details_to_scene(
         Args:
             heads ():
             channels ():
-            relative_start_time_range (list): Range from which start time is selected at random.
+            relative_start_time_range (list): Range from which start time is selected at
+                random.
             duration_mean (float): mean of the time offset for start of turn
             duration_sd (float): standard deviation of the time offset for start of turn
             angle_initial_mean (float):
diff --git a/clarity/data/scene_renderer_cec1.py b/clarity/data/scene_renderer_cec1.py
index eae0df73b..c3ae323cf 100644
--- a/clarity/data/scene_renderer_cec1.py
+++ b/clarity/data/scene_renderer_cec1.py
@@ -13,8 +13,8 @@
 
 class Renderer:
     """
-    SceneGenerator of CEC1 training and development sets. The render() function generates all
-    simulated signals for each scene given the parameters specified in the
+    SceneGenerator of CEC1 training and development sets. The render() function
+    generates all simulated signals for each scene given the parameters specified in the
     metadata/scenes.train.json or metadata/scenes.dev.json file.
     """
 
@@ -40,11 +40,11 @@ def __init__(
         self.test_nbits = test_nbits
 
         if num_channels == 0:
-            # This will only generate the initial target, masker and anechoic target signal
+            # Generate just the initial target, masker and anechoic target signal
             self.channels = []
         else:
-            # ... as above plus N hearing aid input channels plus 'channel 0' (the eardrum signal).
-            # e.g. num_channel = 2  => channels [1, 2, 0]
+            # ... as above plus N hearing aid input channels plus 'channel 0'
+            # (the eardrum signal). e.g. num_channel = 2  => channels [1, 2, 0]
             self.channels = list(range(1, num_channels + 1)) + [0]
 
     def read_signal(
@@ -54,26 +54,23 @@ def read_signal(
 
         Args:
             filename (string): Name of file to read
-            offset (int, optional): Offset in samples or seconds (from start). Defaults to 0.
+            offset (int, optional): Offset in samples or seconds from start.
+                Defaults to 0.
             nchannels: expected number of channel (default: 0 = any number OK)
             offset_is_samples (bool): measurement units for offset (default: False)
 
         Returns:
             np.ndarray: audio signal
         """
-        try:
-            wave_file = SoundFile(filename)
-        except Exception as e:
-            # Ensure incorrect error (24 bit) is not generated
-            raise Exception(f"Unable to read {filename}.") from e
+        wave_file = SoundFile(filename)
 
         if nchannels not in (0, wave_file.channels):
-            raise Exception(
+            raise ValueError(
                 f"Wav file ({filename}) was expected to have {nchannels} channels."
             )
 
         if wave_file.samplerate != self.sample_rate:
-            raise Exception(
+            raise ValueError(
                 f"Sampling rate is not {self.sample_rate} for filename {filename}."
             )
 
@@ -145,11 +142,11 @@ def apply_ramp(self, signal, ramp_duration):
         return signal_ramped
 
     def apply_brir(self, signal, brir):
-        """Convolve a signal with a BRIR.
+        """Convolve a signal with a binaural room impulse response (BRIR).
 
         Args:
             signal (ndarray): The mono or stereo signal stored as array of floats
-            brir (ndarray): The binaural room impulse response stored a 2xN array of floats
+            brir (ndarray): The BRIR stored a 2xN array of floats
             n_tail (int): Truncate output to input signal length + n_tail
 
         Returns:
@@ -195,7 +192,8 @@ def compute_snr(
             assert len(segment_target) == len(segment_noise)
         except AssertionError as e:
             raise ValueError(
-                f"Target ({len(segment_target)}) differs in length from Noise ({len(segment_noise)})"
+                f"Target ({len(segment_target)}) "
+                f"differs in length from Noise ({len(segment_noise)})"
             ) from e
 
         snr = better_ear_speechweighted_snr(segment_target, segment_noise)
@@ -268,7 +266,7 @@ def render(
                 )
                 logging.debug("Using channel %s as reference.", channel)
 
-            # Apply snr_ref reference scaling to get 0 dB and then scale to target snr_dB
+            # Apply snr_ref reference scaling to get 0 dB, then scale to target snr_dB
             interferer_at_ear = interferer_at_ear * snr_ref
             interferer_at_ear = interferer_at_ear * 10 ** ((-snr_dB) / 20)
 
@@ -320,8 +318,8 @@ def check_scene_exists(scene: dict, output_path: str, num_channels: int) -> bool
         # This will only generate the initial target, masker and anechoic target signal
         pass
     else:
-        # ... as above plus N hearing aid input channels plus 'channel 0' (the eardrum signal).
-        # e.g. num_channel = 2  => channels [1, 2, 0]
+        # ... as above plus N hearing aid input channels plus 'channel 0' (the eardrum
+        # signal), e.g., num_channel = 2  => channels [1, 2, 0]
         channels = list(range(1, num_channels + 1)) + [0]
 
     pattern = f"{output_path}/{scene['scene']}"
diff --git a/clarity/data/scene_renderer_cec2.py b/clarity/data/scene_renderer_cec2.py
index 8765c1cd3..584cbfe16 100644
--- a/clarity/data/scene_renderer_cec2.py
+++ b/clarity/data/scene_renderer_cec2.py
@@ -38,7 +38,7 @@ def two_point_rotation(rotation: dict, origin: np.ndarray, duration: int) -> np.
     angle_origin = np.arctan2(origin[1], origin[0])
     angles = [math.radians(r["angle"]) - angle_origin for r in rotation]
     logger.info("angles=%s", angles)
-    theta = hoa.rotation_vector(
+    theta = hoa.compute_rotation_vector(
         angles[0], angles[1], duration, rotation[0]["sample"], rotation[1]["sample"]
     )
     return theta
@@ -193,8 +193,8 @@ def load_interferer_signals(self, scene):
     def make_hoa_target_anechoic(self, target, room):
         """Make the HOA anechoic target.
 
-        Applies an anechoic HOA IR that models a source straight in front of the listener.
-        The signal is delayed to match the propagation delay of the room.
+        Applies an anechoic HOA IR that models a source straight in front of the
+        listener. The signal is delayed to match the propagation delay of the room.
 
         Args:
             target ():
@@ -371,7 +371,8 @@ def generate_binaural_signals(
         ]
         hrirs = [loadmat(hrir_filename) for hrir_filename in hrir_filenames]
 
-        # Target and (flattened) interferer mixed down to binaural using each set of hrirs
+        # Target and (flattened) interferer mixed down to binaural using each
+        # set of hrirs
         targets = [
             hoa.binaural_mixdown(hoa_target, hrir, self.metadata.hrir_metadata)
             for hrir in hrirs
diff --git a/clarity/data/utils.py b/clarity/data/utils.py
index 15682e29c..1c93e88ef 100644
--- a/clarity/data/utils.py
+++ b/clarity/data/utils.py
@@ -1,4 +1,5 @@
 """Utilities for data generation."""
+# pylint: disable=wrong-import-position
 import os
 import sys
 
@@ -35,11 +36,8 @@ def better_ear_speechweighted_snr(target: np.ndarray, noise: np.ndarray) -> floa
     """
     if np.ndim(target) == 1:
         # analysis left ear and right ear for single channel target
-        try:
-            left_snr = speechweighted_snr(target, noise[:, 0])
-            right_snr = speechweighted_snr(target, noise[:, 1])
-        except IndexError:
-            raise
+        left_snr = speechweighted_snr(target, noise[:, 0])
+        right_snr = speechweighted_snr(target, noise[:, 1])
     else:
         # analysis left ear and right ear for two channel target
         left_snr = speechweighted_snr(target[:, 0], noise[:, 0])
@@ -60,15 +58,12 @@ def speechweighted_snr(target: np.ndarray, noise: np.ndarray) -> float:
         (float):
     Signal Noise Ratio
     """
-    try:
-        target_filt = scipy.signal.convolve(
-            target, SPEECH_FILTER, mode="full", method="fft"
-        )
-        noise_filt = scipy.signal.convolve(
-            noise, SPEECH_FILTER, mode="full", method="fft"
-        )
-    except ValueError:
-        raise
+
+    target_filt = scipy.signal.convolve(
+        target, SPEECH_FILTER, mode="full", method="fft"
+    )
+    noise_filt = scipy.signal.convolve(noise, SPEECH_FILTER, mode="full", method="fft")
+
     # rms of the target after speech weighted filter
     targ_rms = np.sqrt(np.mean(target_filt**2))
 
@@ -108,10 +103,10 @@ def pad(signal: np.ndarray, length: int) -> np.ndarray:
     Returns:
         np.array:
     """
-    try:
-        assert length >= signal.shape[0]
-    except AssertionError:
-        raise
+
+    if length < signal.shape[0]:
+        raise ValueError("Length must be greater than signal length")
+
     return np.pad(
         signal, ([(0, length - signal.shape[0])] + ([(0, 0)] * (len(signal.shape) - 1)))
     )
diff --git a/clarity/dataset/cec1_dataset.py b/clarity/dataset/cec1_dataset.py
index 0eaea2b0f..b4a8e190c 100644
--- a/clarity/dataset/cec1_dataset.py
+++ b/clarity/dataset/cec1_dataset.py
@@ -37,15 +37,14 @@ def __init__(
         self.testing = testing
 
         self.scene_list = []
-        with open(scenes_file, "r") as f:
-            scene_json = json.load(f)
+        with open(scenes_file, "r", encoding="utf-8") as fp:
+            scene_json = json.load(fp)
             if not testing:
-                for i in range(len(scene_json)):
-                    self.scene_list.append(scene_json[i]["scene"])
+                for scene in scene_json:
+                    self.scene_list.append(scene["scene"])
             else:
                 for scene in scene_json.keys():
                     self.scene_list.append(scene)
-            f.close()
 
         if self.num_channels == 2:
             self.mixed_suffix = "_mixed_CH1.wav"
@@ -80,7 +79,6 @@ def wav_sample(self, x, y):
             end = start + sample_len
             x = x[:, start:end]
             y = y[:, start:end]
-            return x, y
         elif wav_len < sample_len:
             x = np.append(
                 x, np.zeros([x.shape[1], sample_len - wav_len], dtype=np.float32)
@@ -88,9 +86,8 @@ def wav_sample(self, x, y):
             y = np.append(
                 y, np.zeros([x.shape[1], sample_len - wav_len], dtype=np.float32)
             )
-            return x, y
-        else:
-            return x, y
+
+        return x, y
 
     def lowpass_filtering(self, x):
         return lfilter(self.lowpass_filter, 1, x)
@@ -143,12 +140,17 @@ def __getitem__(self, item):
             target = target / mixed_max
 
         if not self.testing:
-            return (
+            return_data = (
                 torch.tensor(mixed, dtype=torch.float32),
                 torch.tensor(target, dtype=torch.float32),
             )
         else:
-            return torch.tensor(mixed, dtype=torch.float32), self.scene_list[item]
+            return_data = (
+                torch.tensor(mixed, dtype=torch.float32),
+                self.scene_list[item],
+            )
+
+        return return_data
 
     def __len__(self):
         return len(self.scene_list)
diff --git a/clarity/engine/system.py b/clarity/engine/system.py
index 5550a6c82..c6fdf35a6 100644
--- a/clarity/engine/system.py
+++ b/clarity/engine/system.py
@@ -5,6 +5,8 @@
 import pytorch_lightning as pl
 from torch.optim.lr_scheduler import ReduceLROnPlateau
 
+# pylint: disable=W0221  # false positives
+
 
 class System(pl.LightningModule):
     def __init__(
diff --git a/clarity/enhancer/compressor.py b/clarity/enhancer/compressor.py
index cdf208632..e9371da4e 100644
--- a/clarity/enhancer/compressor.py
+++ b/clarity/enhancer/compressor.py
@@ -16,7 +16,7 @@ def __init__(
         attenuation: float = 0.0001,
         rms_buffer_size: float = 0.2,
         makeup_gain: int = 1,
-        **kwargs,
+        **_kwargs,
     ):
         """Instantiate the Compressor Class.
 
diff --git a/clarity/enhancer/dnn/mc_conv_tasnet.py b/clarity/enhancer/dnn/mc_conv_tasnet.py
index 8d03bb7a4..2bcf71e1a 100644
--- a/clarity/enhancer/dnn/mc_conv_tasnet.py
+++ b/clarity/enhancer/dnn/mc_conv_tasnet.py
@@ -5,8 +5,8 @@
 from typing import Optional
 
 import torch
-import torch.nn as nn
 import torch.nn.functional as F
+from torch import nn
 
 EPS = 1e-8
 
@@ -23,7 +23,8 @@ def overlap_and_add(signal, frame_step, device):
     Args:
         signal: A [..., frames, frame_length] Tensor. All dimensions may be unknown,
             and rank must be at least 2.
-        frame_step: An integer denoting overlap offsets. Must be less than or equal to frame_length.
+        frame_step: An integer denoting overlap offsets. Must be less than or equal to
+            frame_length.
         device: Whether to use 'cpu' or 'cuda' for processing.
 
     Returns:
@@ -31,7 +32,8 @@ def overlap_and_add(signal, frame_step, device):
             signal's inner-most two dimensions.
         output_size = (frames - 1) * frame_step + frame_length
 
-    Based on https://github.com/tensorflow/tensorflow/blob/r1.12/tensorflow/contrib/signal/python/ops/reconstruction_ops.py
+    Based on https://github.com/tensorflow/tensorflow/blob/r1.12/tensorflow/
+        contrib/signal/python/ops/reconstruction_ops.py
     """
     outer_dimensions = signal.size()[:-2]
     frames, frame_length = signal.size()[-2:]
@@ -174,7 +176,7 @@ class SpatialEncoder(nn.Module):
     """Estimation of the nonnegative mixture weight by a 1-D conv layer."""
 
     def __init__(self, L, N, num_channels):
-        super(SpatialEncoder, self).__init__()
+        super().__init__()
         # Hyper-parameter
         self.L, self.N = L, N
         # Components
diff --git a/clarity/enhancer/dsp/filter.py b/clarity/enhancer/dsp/filter.py
index 8d0bf2bd5..f402c069c 100644
--- a/clarity/enhancer/dsp/filter.py
+++ b/clarity/enhancer/dsp/filter.py
@@ -1,7 +1,7 @@
 import numpy as np
 import torch
-import torch.nn as nn
 import torch.nn.functional as F
+from torch import nn
 
 EPS = 1e-8
 
diff --git a/clarity/enhancer/gha/gainrule_camfit.py b/clarity/enhancer/gha/gainrule_camfit.py
index adc33e033..bc2e821c7 100644
--- a/clarity/enhancer/gha/gainrule_camfit.py
+++ b/clarity/enhancer/gha/gainrule_camfit.py
@@ -237,7 +237,7 @@ def gainrule_camfit_linear(
             intercepts = freq_interp_sh(
                 intercept_frequencies, intercepts, sFitmodel["frequencies"][0][0]
             )
-    except Exception:  # noqa E722
+    except TypeError:  # noqa E722
         intercepts = freq_interp_sh(
             intercept_frequencies, intercepts, sFitmodel["frequencies"]
         )
diff --git a/clarity/enhancer/gha/gha_interface.py b/clarity/enhancer/gha/gha_interface.py
index ab4e9e70f..ebf534be8 100644
--- a/clarity/enhancer/gha/gha_interface.py
+++ b/clarity/enhancer/gha/gha_interface.py
@@ -81,7 +81,9 @@ def create_configured_cfgfile(
         output = template.render(
             io_in=input_file,
             io_out=output_file,
-            peaklevel_in=f"[{peaklevel_in} {peaklevel_in} {peaklevel_in} {peaklevel_in}]",
+            peaklevel_in=(
+                f"[{peaklevel_in} {peaklevel_in} {peaklevel_in} {peaklevel_in}]"
+            ),
             peaklevel_out=f"[{peaklevel_out} {peaklevel_out}]",
             gtdata=formatted_sGt,
         )
@@ -182,25 +184,23 @@ def read_signal(
 
         Args:
             filename (string): Name of file to read
-            offset (int, optional): Offset in samples or seconds (from start). Defaults to 0.
+            offset (int, optional): Offset in samples or seconds (from start).
+                Defaults to 0.
             nchannels: expected number of channel (default: 0 = any number OK)
             offset_is_samples (bool): measurement units for offset (default: False)
         Returns:
             ndarray: audio signal
         """
-        try:
-            wave_file = SoundFile(filename)
-        except Exception as e:
-            # Ensure incorrect error (24 bit) is not generated
-            raise Exception(f"Unable to read {filename}.") from e
+
+        wave_file = SoundFile(filename)
 
         if nchannels not in (0, wave_file.channels):
-            raise Exception(
+            raise ValueError(
                 f"Wav file ({filename}) was expected to have {nchannels} channels."
             )
 
         if wave_file.samplerate != self.fs:
-            raise Exception(f"Sampling rate is not {self.fs} for filename {filename}.")
+            raise ValueError(f"Sampling rate is not {self.fs} for filename {filename}.")
 
         if not offset_is_samples:  # Default behaviour
             offset = int(offset * wave_file.samplerate)
@@ -235,7 +235,7 @@ def create_HA_inputs(self, infile_names, merged_filename):
         """Create input signal for baseline hearing aids."""
 
         if (infile_names[0][-5] != "1") or (infile_names[2][-5] != "3"):
-            raise Exception("HA-input signal error: channel mismatch!")
+            raise ValueError("HA-input signal error: channel mismatch!")
 
         signal_CH1 = self.read_signal(infile_names[0])
         signal_CH3 = self.read_signal(infile_names[2])
diff --git a/clarity/enhancer/nalr.py b/clarity/enhancer/nalr.py
index dc4fa8ca0..c285c8ebb 100644
--- a/clarity/enhancer/nalr.py
+++ b/clarity/enhancer/nalr.py
@@ -11,7 +11,7 @@ def firwin2(
     f: np.ndarray,
     a: np.ndarray,
     window: Optional[Union[str, float]] = None,
-    antisymmetric: Optional[bool] = None,
+    antisymmetric: Optional[bool] = None,  # pylint: disable=unused-argument
 ) -> np.ndarray:
     """FIR filter design using the window method.
     Partial implementation of scipy firwin2 but using our own MATLAB-derived fir2.
@@ -20,11 +20,11 @@ def firwin2(
         n (int): The number of taps in the FIR filter.
         f (ndarray): The frequency sampling points. 0.0 to 1.0 with 1.0 being Nyquist.
         a (ndarray): The filter gains at the frequency sampling points.
-        window (string or (string, float), optional): See scipy.firwin2 (default: (None))
-        _antisymmetric (bool, optional): Unused but present to main compatability
+        window (string or (string, float), optional): See scipy.firwin2 (default: None)
+        _antisymmetric (bool, optional): Unused but present to main compatibility
             with scipy firwin2.
     Returns:
-        np.ndarray:  The filter coefficients of the FIR filter, as a 1-D array of length n.
+        np.ndarray:  The FIR filter coefficients as a 1-D array of length n.
     """
     window_shape = None
     window_type: Union[str, float, None]
diff --git a/clarity/evaluator/haaqi/haaqi.py b/clarity/evaluator/haaqi/haaqi.py
index 5641bb404..5efcd3ad7 100644
--- a/clarity/evaluator/haaqi/haaqi.py
+++ b/clarity/evaluator/haaqi/haaqi.py
@@ -29,22 +29,26 @@ def haaqi_v1(
     impaired hearing.
 
     Arguments:
-    reference (ndarray):  Clear input reference speech signal with no noise or distortion.
+        reference (ndarray):  Input reference speech signal with no noise or distortion.
             If a hearing loss is specified, NAL-R equalization is optional
-    reference_freq (int): Sampling rate in Hz for reference signal.
-    processed (np.ndarray):  Output signal with noise, distortion, HA gain, and/or processing.
-    processed_freq (int): Sampling rate in Hz for processed signal.
-    hearling_loss (np.ndarray): (1,6) vector of hearing loss at the 6 audiometric frequencies
-        [250, 500, 1000, 2000, 4000, 6000] Hz.
-    equalisation (int): Flag to provide equalization for the hearing loss to signal processed:
+        reference_freq (int): Sampling rate in Hz for reference signal.
+        processed (np.ndarray):  Output signal with noise, distortion, HA gain,
+            and/or processing.
+        processed_freq (int): Sampling rate in Hz for processed signal.
+        hearing_loss (np.ndarray): (1,6) vector of hearing loss at the 6 audiometric
+            frequencies [250, 500, 1000, 2000, 4000, 6000] Hz.
+        equalisation (int): hearing loss equalization mode for reference signal:
             1 = no EQ has been provided, the function will add NAL-R
             2 = NAL-R EQ has already been added to the reference signal
-    level1 (int): Optional input specifying level in dB SPL that corresponds to a
+        level1 (int): Optional input specifying level in dB SPL that corresponds to a
            signal RMS = 1. Default is 65 dB SPL if argument not provided.
            Default: 65
-    silence_threshold (float): Silence threshold sum across bands, dB above aud threshold. Default : 2.5
-    add_noise (float): Additive noise in dB SL to condition cross-covariances. Default: 0.0
-    segment_covariance (int): Segment size for the covariance calculation. Default: 16
+        silence_threshold (float): Silence threshold sum across bands,
+            dB above auditory threshold. Default : 2.5
+        add_noise (float): Additive noise dB SL to condition cross-covariances.
+            Defaults to 0.0
+        segment_covariance (int): Segment size for the covariance calculation.
+            Defaults to 16
 
     Returns:
         combined : Quality is the polynomial sum of the nonlinear and linear terms
diff --git a/clarity/evaluator/haspi/eb.py b/clarity/evaluator/haspi/eb.py
index 6ad6c3bf4..d69a0d32c 100644
--- a/clarity/evaluator/haspi/eb.py
+++ b/clarity/evaluator/haspi/eb.py
@@ -18,13 +18,6 @@
 
 logger = logging.getLogger(__name__)
 
-# pylint: disable=line-too-long
-# pylint: disable=too-many-arguments
-# pylint: disable=too-many-branches
-# pylint: disable=too-many-lines
-# pylint: disable=too-many-locals
-# pylint: disable=too-many-statements
-
 
 def ear_model(
     reference,
@@ -53,31 +46,33 @@ def ear_model(
     and IHC loss attenuation.
 
     Arguments:
-    reference (np.ndarray): reference signal: should be adjusted to 65 dB SPL (itype=0 or 1)
-               or to 65 dB SPL plus NAL-R gain (itype=2)
-    reference_freq (int): sampling rate for the reference signal, Hz
-    processed (np.ndarray): processed signal (e.g. hearing-aid output) includes HA gain
-    processed_freq (int): sampling rate for the processed signal, Hz
-    hearing_loss (np.ndarray): audiogram giving the hearing loss in dB at six audiometric
-               frequencies: [250, 500, 1000, 2000, 4000, 6000] Hz
-    itype (int): purpose for the calculation:
-             0=intelligibility: reference is nornal hearing and must not
+        reference (np.ndarray): reference signal: should be adjusted to 65 dB SPL
+            (itype=0 or 1) or to 65 dB SPL plus NAL-R gain (itype=2)
+        reference_freq (int): sampling rate for the reference signal, Hz
+        processed (np.ndarray): processed signal (e.g. hearing-aid output) includes
+            HA gain
+        processed_freq (int): sampling rate for the processed signal, Hz
+        hearing_loss (np.ndarray): audiogram giving the hearing loss in dB at 6
+            audiometric frequencies: [250, 500, 1000, 2000, 4000, 6000] Hz
+        itype (int): purpose for the calculation:
+             0=intelligibility: reference is normal hearing and must not
                include NAL-R EQ
              1=quality: reference does not include NAL-R EQ
              2=quality: reference already has NAL-R EQ applied
-    level1   level calibration: signal RMS=1 corresponds to Level1 dB SPL
-    nchan (int): auditory frequency bands
-    m_delay (int): Compensate for the gammatone group delay.
-    shift (float): Basal shift of the basilar membrane length
+        level1:   level calibration: signal RMS=1 corresponds to Level1 dB SPL
+        nchan (int): auditory frequency bands
+        m_delay (int): Compensate for the gammatone group delay.
+        shift (float): Basal shift of the basilar membrane length
 
     Returns:
-    reference_db (): envelope for the reference in each band
-    reference_basilar_membrane (): BM motion for the reference in each band
-    processed_db (): envelope for the processed signal in each band
-    processed_basilar_membrane (): BM motion for the processed signal in each band
-    reference_sl (): compressed RMS average reference in each band converted to dB SL
-    processed_sl (): compressed RMS average output in each band converted to dB SL
-    freq_sample (): sampling rate in Hz for the model outputs
+        reference_db (): envelope for the reference in each band
+        reference_basilar_membrane (): BM motion for the reference in each band
+        processed_db (): envelope for the processed signal in each band
+        processed_basilar_membrane (): BM motion for the processed signal in each band
+        reference_sl (): compressed RMS average reference in each band converted
+            to dB SL
+        processed_sl (): compressed RMS average output in each band converted to dB SL
+        freq_sample (): sampling rate in Hz for the model outputs
 
     Updates:
     James M. Kates, 27 October 2011.
@@ -106,7 +101,8 @@ def ear_model(
     ) = loss_parameters(hearing_loss, _center_freq)
 
     # The cochlear model parameters for the reference are the same as for the hearing
-    # loss if calculating quality, but are for normal hearing if calculating intelligibility.
+    # loss if calculating quality, but are for normal hearing if calculating
+    # intelligibility.
     if itype == 0:
         hearing_loss_x = [0] * len(hearing_loss)
     else:
@@ -143,7 +139,8 @@ def ear_model(
     reference_24hz, processed_24hz = input_align(reference_24hz, processed_24hz)
     nsamp = len(reference_24hz)
 
-    # For HASQI, here add NAL-R equalization if the quality reference doesn't already have it.
+    # For HASQI, here add NAL-R equalization if the quality reference doesn't
+    # already have it.
     if itype == 1:
         nfir = 140  # Length in samples of the FIR NAL-R EQ filter (24-kHz rate)
         enhancer = NALR(nfir, freq_sample)
@@ -322,21 +319,20 @@ def center_frequency(
     min_bw=24.7,
 ):
     """
-    Compute the Equivalent Rectangular Bandwidth_[1] frequency spacing for the gammatone filter bank. The equation
-    comes from Malcolm Slaney[2]_ .
+    Compute the Equivalent Rectangular Bandwidth_[1] frequency spacing for the
+    gammatone filter bank. The equation comes from Malcolm Slaney[2].
 
     Arguments:
-    nchan (int): number of filters in the filter bank
-    low_freq (int): Low Frequency level.
-    high_freq (int): High Frequency level.
-    shift (): optional frequency shift of the filter bank specified as a
-              fractional shift in distance along the BM. A positive shift
-              is an increase in frequency (basal shift), and negative is
-              a decrease in frequency (apical shift). The total length of
-              the BM is normalized to 1. The frequency-to-distance map is
-              from D.D. Greenwood[3]_.
-    ear_q (float):
-    min_bw (float):
+        nchan (int): number of filters in the filter bank
+        low_freq (int): Low Frequency level.
+        high_freq (int): High Frequency level.
+        shift (): optional frequency shift of the filter bank specified as a fractional
+            shift in distance along the BM. A positive shift is an increase in frequency
+            (basal shift), and negative is a decrease in frequency (apical shift). The
+            total length of the BM is normalized to 1. The frequency-to-distance map is
+            from D.D. Greenwood[3].
+        ear_q (float):
+        min_bw (float):
 
     Returns:
 
@@ -380,7 +376,8 @@ def center_frequency(
 
     # All of the following expressions are derived in Apple TR #35,
     # "An Efficient Implementation of the Patterson-Holdsworth Cochlear
-    # Filter Bank" by Malcolm Slaney. https://engineering.purdue.edu/~malcolm/apple/tr35/PattersonsEar.pdf
+    # Filter Bank" by Malcolm Slaney.
+    # https://engineering.purdue.edu/~malcolm/apple/tr35/PattersonsEar.pdf
     _center_freq = -(ear_q * min_bw) + np.exp(
         np.arange(1, nchan)
         * (-np.log(high_freq + ear_q * min_bw) + np.log(low_freq + ear_q * min_bw))
@@ -400,18 +397,18 @@ def loss_parameters(hearing_loss, center_freq, audiometric_freq=None):
     in proportion to the OHC fraction of the total loss.
 
     Arguments:
-    hearing_loss (np.ndarray): hearing loss at the 6 audiometric frequencies
-    center_freq (np.ndarray): array containing the center frequencies of the
-        gammatone filters arranged from low to high
-    audiometric_freq (list):
+        hearing_loss (np.ndarray): hearing loss at the 6 audiometric frequencies
+        center_freq (np.ndarray): array containing the center frequencies of the
+            gammatone filters arranged from low to high
+        audiometric_freq (list):
 
     Returns:
-    attenuated_ohc (): attenuation in dB for the OHC gammatone filters
-    bandwidth (): OHC filter bandwidth expressed in terms of normal
-    low_knee (): Lower kneepoint for the low-level linear amplification
-    compression_ratio (): Ranges from 1.4:1 at 150 Hz to 3.5:1 at 8 kHz for normal
-        hearing. Reduced in proportion to the OHC loss to 1:1.
-    attenuated_ihc ():	attenuation in dB for the input to the IHC synapse
+        attenuated_ohc (): attenuation in dB for the OHC gammatone filters
+        bandwidth (): OHC filter bandwidth expressed in terms of normal
+        low_knee (): Lower kneepoint for the low-level linear amplification
+        compression_ratio (): Ranges from 1.4:1 at 150 Hz to 3.5:1 at 8 kHz for normal
+            hearing. Reduced in proportion to the OHC loss to 1:1.
+        attenuated_ihc ():	attenuation in dB for the input to the IHC synapse
 
     Updates:
     James M. Kates, 25 January 2007.
@@ -447,8 +444,8 @@ def loss_parameters(hearing_loss, center_freq, audiometric_freq=None):
     # frequency band to 3.5:1 in the 8-kHz frequency band
     compression_ratio = 1.25 + 2.25 * np.arange(nfilt) / (nfilt - 1)
 
-    # Maximum OHC sensitivity loss depends on the compression ratio.
-    # The compression I/O curves assume linear below 30 and above 100 dB SPL in normal ears.
+    # Maximum OHC sensitivity loss depends on the compression ratio. The compression
+    # I/O curves assume linear below 30 and above 100 dB SPL in normal ears.
     max_ohc = 70 * (
         1 - (1 / compression_ratio)
     )  # HC loss that results in 1:1 compression
@@ -667,33 +664,33 @@ def gammatone_basilar_membrane(
     min_bandwidth=24.7,
 ):
     """
-    4th-order gammatone auditory filter. This implementation is based
-    on the c program published on-line by Ning Ma, U. Sheffield, UK[1]_
-    that gives an implementation of the Martin Cooke filters[2]_:
-    an impulse-invariant transformation of the gammatone filter. The
-    signal is demodulated down to baseband using a complex exponential,
+    4th-order gammatone auditory filter. This implementation is based on the c program
+    published on-line by Ning Ma, U. Sheffield, UK[1]_ that gives an implementation of
+    the Martin Cooke filters[2]_: an impulse-invariant transformation of the gammatone
+    filter. The signal is demodulated down to baseband using a complex exponential,
     and then passed through a cascade of four one-pole low-pass filters.
 
-    This version filters two signals that have the same sampling rate and the
-    same gammatone filter center frequencies. The lengths of the two signals
-    should match; if they don't, the signals are truncated to the shorter of
-    the two lengths.
+    This version filters two signals that have the same sampling rate and the same
+    gammatone filter center frequencies. The lengths of the two signals should match;
+    if they don't, the signals are truncated to the shorter of the two lengths.
 
     Arguments:
-    reference (): first sequence to be filtered
-    reference_bandwidth: bandwidth for x relative to that of a normal ear
-    processed (): second sequence to be filtered
-    processed_bandwidth (): bandwidth for x relative to that of a normal ear
-    freq_sample (): sampling rate in Hz
-    center_frequency (int): filter center frequency in Hz
-    ear_q: (float): ???
-    min_bandwidth (float): ???
+        reference (): first sequence to be filtered
+        reference_bandwidth: bandwidth for x relative to that of a normal ear
+        processed (): second sequence to be filtered
+        processed_bandwidth (): bandwidth for x relative to that of a normal ear
+        freq_sample (): sampling rate in Hz
+        center_frequency (int): filter center frequency in Hz
+        ear_q: (float): ???
+        min_bandwidth (float): ???
 
     Returns:
-    reference_envelope (): filter envelope output (modulated down to baseband) 1st signal
-    reference_basilar_membrane (): Basilar Membrane for the first signal
-    processed_envelope (): filter envelope output (modulated down to baseband) 2nd signal
-    processed_basilar_membrane (): Basilar Membrane for the second signal
+        reference_envelope (): filter envelope output (modulated down to baseband)
+            1st signal
+        reference_basilar_membrane (): Basilar Membrane for the first signal
+        processed_envelope (): filter envelope output (modulated down to baseband)
+            2nd signal
+        processed_basilar_membrane (): Basilar Membrane for the second signal
 
     References:
     .. [1] Ma N, Green P, Barker J, Coy A (2007) Exploiting correlogram
@@ -712,7 +709,8 @@ def gammatone_basilar_membrane(
     Cosine/sine loop speed increased, 9 August 2013.
     Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
-    # Filter Equivalent Rectangular Bandwidth from Moore and Glasberg (1983) doi: 10.1121/1.389861
+    # Filter Equivalent Rectangular Bandwidth from Moore and Glasberg (1983)
+    # doi: 10.1121/1.389861
     erb = min_bandwidth + (center_freq / ear_q)
 
     # Check the lengths of the two signals and trim to shortest
@@ -779,15 +777,15 @@ def gammatone_bandwidth_demodulation(
     """Gamma tone bandwidth demodulation
 
     Arguments:
-    npts (): ???
-    tpt (): ???
-    center_freq (): ???
-    center_freq_cos (): ???
-    sincf (): ???
+        npts (): ???
+        tpt (): ???
+        center_freq (): ???
+        center_freq_cos (): ???
+        sincf (): ???
 
     Returns:
-    sincf (): ???
-    coscf (): ???
+        sincf (): ???
+        coscf (): ???
     """
     cos_n = np.cos(tpt * center_freq)
     sin_n = np.sin(tpt * center_freq)
@@ -807,17 +805,16 @@ def gammatone_bandwidth_demodulation(
 
 def bandwidth_adjust(control, bandwidth_min, bandwidth_max, level1):
     """
-    Compute the increase in auditory filter bandwidth in response to high
-    signal levels.
+    Compute the increase in auditory filter bandwidth in response to high signal levels.
 
     Arguments:
-    control (): envelope output in the control filter band
-    bandwidth_min (): auditory filter bandwidth computed for the loss (or NH)
-    bandwidth_max (): auditory filter bandwidth at maximum OHC damage
-    level1 ():     RMS=1 corresponds to Level1 dB SPL
+        control (): envelope output in the control filter band
+        bandwidth_min (): auditory filter bandwidth computed for the loss (or NH)
+        bandwidth_max (): auditory filter bandwidth at maximum OHC damage
+        level1 ():     RMS=1 corresponds to Level1 dB SPL
 
     Returns:
-    bandwidth (): filter bandwidth increased for high signal levels
+        bandwidth (): filter bandwidth increased for high signal levels
 
     Updates:
     James M. Kates, 21 June 2011.
@@ -851,30 +848,29 @@ def env_compress_basilar_membrane(
     threshold_high=100,
 ):
     """
-    Compute the cochlear compression in one auditory filter band. The
-    gain is linear below the lower threshold, compressive with a
-    compression ratio of CR:1 between the lower and upper thresholds,
-    and reverts to linear above the upper threshold. The compressor
-    assumes that auditory thresold is 0 dB SPL.
+    Compute the cochlear compression in one auditory filter band. The gain is linear
+    below the lower threshold, compressive with a compression ratio of CR:1 between the
+    lower and upper thresholds, and reverts to linear above the upper threshold. The
+    compressor assumes that auditory threshold is 0 dB SPL.
 
     Arguments:
-    envsig (): analytic signal envelope (magnitude) returned by the
+        envsig (): analytic signal envelope (magnitude) returned by the
                 gammatone filter bank
-    bm (): BM motion output by the filter bank
-    control (): analytic control envelope returned by the wide control
+        bm (): BM motion output by the filter bank
+        control (): analytic control envelope returned by the wide control
                 path filter bank
-    attn_ohc (): OHC attenuation at the input to the compressor
-    threshold_Low (): kneepoint for the low-level linear amplification
-    compression_ratio (): compression ratio
-    fsamp (): sampling rate in Hz
-    level1 (): dB reference level: a signal having an RMS value of 1 is
+        attn_ohc (): OHC attenuation at the input to the compressor
+        threshold_Low (): kneepoint for the low-level linear amplification
+        compression_ratio (): compression ratio
+        fsamp (): sampling rate in Hz
+        level1 (): dB reference level: a signal having an RMS value of 1 is
                 assigned to Level1 dB SPL.
-    small (): ???
-    threshold_high: kneepoint for the high-level linear amplification
+        small (): ???
+        threshold_high: kneepoint for the high-level linear amplification
 
     Returns:
-    compressed_signal (): compressed version of the signal envelope
-    compressed_basilar_membrane (): compressed version of the BM motion
+        compressed_signal (): compressed version of the signal envelope
+        compressed_basilar_membrane (): compressed version of the BM motion
 
     Updates:
     James M. Kates, 19 January 2007.
@@ -913,16 +909,16 @@ def env_compress_basilar_membrane(
 
 def envelope_align(reference, output, freq_sample=24000, corr_range=100):
     """
-    Align the envelope of the processed signal to that of the reference
-    signal.
+    Align the envelope of the processed signal to that of the reference signal.
 
     Arguments:
-    reference (): envelope or BM motion of the reference signal
-    output (): envelope or BM motion of the output signal
-    freq_sample (int): Frequency sample rate in Hz
-    corr_range (int): range in msec for the correlation
+        reference (): envelope or BM motion of the reference signal
+        output (): envelope or BM motion of the output signal
+        freq_sample (int): Frequency sample rate in Hz
+        corr_range (int): range in msec for the correlation
+
     Returns:
-    y (): shifted output envelope to match the input
+        y (): shifted output envelope to match the input
 
     Updates:
     James M. Kates, 28 October 2011.
@@ -931,9 +927,9 @@ def envelope_align(reference, output, freq_sample=24000, corr_range=100):
     Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
 
-    # The MATLAB code limits the range of lags to search (to 100 ms) to save
-    # computation time - no such option exists in numpy, but the code below limits the delay
-    # to the same range as in Matlab, for consistent results
+    # The MATLAB code limits the range of lags to search (to 100 ms) to save computation
+    # time - no such option exists in numpy, but the code below limits the delay to the
+    # same range as in Matlab, for consistent results
     lags = round(0.001 * corr_range * freq_sample)  # Range in samples
     npts = len(reference)
     lags = min(lags, npts)
@@ -956,15 +952,16 @@ def envelope_sl(reference, basilar_membrane, attnenuated_ihc, level1, small=1e-3
     Convert the compressed envelope returned by cochlear_envcomp to dB SL.
 
     Arguments:
-    reference (): linear envelope after compression
-    basilar_membrane (): linear Basilar Membrane vibration after compression
-    attnenuated_ihc (): IHC attenuation at the input to the synapse
-    level1 (): level in dB SPL corresponding to 1 RMS
-    small (float): ???
+        reference (): linear envelope after compression
+        basilar_membrane (): linear Basilar Membrane vibration after compression
+        attenuated_ihc (): IHC attenuation at the input to the synapse
+        level1 (): level in dB SPL corresponding to 1 RMS
+        small (float): ???
 
     Returns:
-    _reference (): reference envelope in dB SL
-    _basilar_membrane (): Basilar Membrane vibration with envelope converted to dB SL
+        _reference (): reference envelope in dB SL
+        _basilar_membrane (): Basilar Membrane vibration withenvelope converted to
+            dB SL
 
     Updates:
     James M. Kates, 20 Feb 07.
@@ -999,15 +996,17 @@ def inner_hair_cell_adaptation(
     auditory threshold are provided by a subsequent call to eb_BMatten.
 
     Arguments:
-    reference_db (np.ndarray): signal envelope in one frequency band in dB SL
+        reference_db (np.ndarray): signal envelope in one frequency band in dB SL
              contains OHC compression and IHC attenuation
-    reference_basilar_membrane (): basilar membrane vibration with OHC compression but no IHC atten
-    delta (): overshoot factor = delta x steady-state
-    freq_sample (int): sampling rate in Hz
+        reference_basilar_membrane (): basilar membrane vibration with OHC compression
+            but no IHC attenuation
+        delta (): overshoot factor = delta x steady-state
+        freq_sample (int): sampling rate in Hz
 
     Returns:
-    output_db ():     envelope in dB SL with IHC adaptation
-    output_basilar_membrane (): Basilar Membrane multiplied by the IHC adaptation gain function
+        output_db (): envelope in dB SL with IHC adaptation
+        output_basilar_membrane (): Basilar Membrane multiplied by the IHC adaptation
+            gain function
 
     Updates:
     James M. Kates, 1 October 2012.
@@ -1074,21 +1073,21 @@ def inner_hair_cell_adaptation(
 
 def basilar_membrane_add_noise(reference, threshold, level1):
     """
-    Apply the IHC attenuation to the BM motion and to add a
-    low-level Gaussian noise to give the auditory threshold.
+    Apply the IHC attenuation to the BM motion and to add a low-level Gaussian noise to
+    give the auditory threshold.
 
     Arguments:
-    reference ():         BM motion to be attenuated
-    threshold (): additive noise level in dB re:auditory threshold
-    level1 (): an input having RMS=1 corresponds to Leve1 dB SPL
+        reference (): BM motion to be attenuated
+        threshold (): additive noise level in dB re:auditory threshold
+        level1 (): an input having RMS=1 corresponds to Leve1 dB SPL
 
     Returns:
-    Attenuated signal with threhsold noise added
+        Attenuated signal with threshold noise added
 
     Updates:
-    James M. Kates, 19 June 2012.
-    Just additive noise, 2 Oct 2012.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 19 June 2012.
+        Just additive noise, 2 Oct 2012.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     gain = 10 ** ((threshold - level1) / 20)  # Linear gain for the noise
 
@@ -1114,19 +1113,19 @@ def group_delay_compensate(
     the same group delay.
 
     Arguments:
-    xenv (np.ndarray): matrix of signal envelopes or BM motion
-    bandwidths (): gammatone filter bandwidths adjusted for loss
-    center_freq (): center frequencies of the bands
-    freq_sample (): sampling rate for the input signal in Hz (e.g. 24,000 Hz)
-    ear_q (float):
-    min_bandwidth (float) :
+        xenv (np.ndarray): matrix of signal envelopes or BM motion
+        bandwidths (): gammatone filter bandwidths adjusted for loss
+        center_freq (): center frequencies of the bands
+        freq_sample (): sampling rate for the input signal in Hz (e.g. 24,000 Hz)
+        ear_q (float):
+        min_bandwidth (float) :
 
     Returns:
-    processed (): envelopes or BM motion compensated for the group delay.
+        processed (): envelopes or BM motion compensated for the group delay.
 
     Updates:
-    James M. Kates, 28 October 2011.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 28 October 2011.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     # Processing parameters
     nchan = len(bandwidths)
@@ -1191,25 +1190,25 @@ def convert_rms_to_sl(
     assumes that auditory thresold is 0 dB SPL.
 
     Arguments:
-    reference (): analytic signal envelope (magnitude) returned by the
-    gammatone filter bank, RMS average level
-    control (): control signal envelope
-    attenuated_ohc (): OHC attenuation at the input to the compressor
-    threshold_low (): kneepoint for the low-level linear amplification
-    compression_ratio (): compression ratio
-    attenuated_ihc (): IHC attenuation at the input to the synapse
-    level1 (): dB reference level: a signal having an RMS value of 1 is
+        reference (): analytic signal envelope (magnitude) returned by the
+        gammatone filter bank, RMS average level
+        control (): control signal envelope
+        attenuated_ohc (): OHC attenuation at the input to the compressor
+        threshold_low (): kneepoint for the low-level linear amplification
+        compression_ratio (): compression ratio
+        attenuated_ihc (): IHC attenuation at the input to the synapse
+        level1 (): dB reference level: a signal having an RMS value of 1 is
                 assigned to Level1 dB SPL.
-    threshold_high (int):
-    small (float):
+        threshold_high (int):
+        small (float):
 
     Returns:
-    reference_db (): compressed output in dB above the impaired threshold
+        reference_db (): compressed output in dB above the impaired threshold
 
     Updates:
-    James M. Kates, 6 August 2007.
-    Version for two-tone suppression, 29 August 2008.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 6 August 2007.
+        Version for two-tone suppression, 29 August 2008.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
 
     # Initialize the compression parameters
@@ -1246,17 +1245,17 @@ def env_smooth(envelopes: np.ndarray, segment_size, freq_sample):
     2*(1000/segsize).
 
     Arguments:
-    envelopes (np.ndarray): matrix of envelopes in each of the auditory bands
-    segment_size: averaging segment size in msec
-    freq_sample (int): input envelope sampling rate in Hz
+        envelopes (np.ndarray): matrix of envelopes in each of the auditory bands
+        segment_size: averaging segment size in msec
+        freq_sample (int): input envelope sampling rate in Hz
 
     Returns:
-    smooth: matrix of subsampled windowed averages in each band
+        smooth: matrix of subsampled windowed averages in each band
 
     Updates:
-    James M. Kates, 26 January 2007.
-    Final half segment added 27 August 2012.
-    Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
+        James M. Kates, 26 January 2007.
+        Final half segment added 27 August 2012.
+        Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
     """
 
     # Compute the window
@@ -1321,21 +1320,22 @@ def mel_cepstrum_correlation(reference, distorted, threshold, addnoise):
     then computed.
 
     Arguments:
-    reference (): subsampled input signal envelope in dB SL in each critical band
-    distorted (): subsampled distorted output signal envelope
-    threshold (): threshold in dB SPL to include segment in calculation
-    addnoise (): additive Gaussian noise to ensure 0 cross-corr at low levels
+        reference (): subsampled input signal envelope in dB SL in each critical band
+        distorted (): subsampled distorted output signal envelope
+        threshold (): threshold in dB SPL to include segment in calculation
+        addnoise (): additive Gaussian noise to ensure 0 cross-corr at low levels
 
     Returns:
-    average_cepstral_correlation : average cepstral correlation 2-6, input vs output
-    individual_cepstral_correlations : individual cepstral correlations, input vs output
+        average_cepstral_correlation : average cepstral correlation 2-6, input vs output
+        individual_cepstral_correlations : individual cepstral correlations,
+            input vs output
 
     Updates:
-    James M. Kates, 24 October 2006.
-    Difference signal removed for cochlear model, 31 January 2007.
-    Absolute value added 13 May 2011.
-    Changed to loudness criterion for silence threhsold, 28 August 2012.
-    Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
+        James M. Kates, 24 October 2006.
+        Difference signal removed for cochlear model, 31 January 2007.
+        Absolute value added 13 May 2011.
+        Changed to loudness criterion for silence threhsold, 28 August 2012.
+        Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
     """
 
     # Processing parameters
@@ -1405,7 +1405,6 @@ def mel_cepstrum_correlation(reference, distorted, threshold, addnoise):
                 np.sum(reference_cep[k, :] * processed_cep[k, :]) / np.sqrt(xsum * ysum)
             )
 
-    #
     # Figure of merit is the average of the cepstral correlations, ignoring
     # the first (average spectrum level).
     average_cepstral_correlation = np.sum(
@@ -1433,29 +1432,29 @@ def melcor9(
     the 8 modulation frequencies.
 
     Arguments:
-    reference (): subsampled input signal envelope in dB SL in each critical band
-    distorted (): subsampled distorted output signal envelope
-    threshold (): threshold in dB SPL to include segment in calculation
-    add_noise (): additive Gaussian noise to ensure 0 cross-corr at low levels
-    segment_size (): segment size in ms used for the envelope LP filter (8 msec)
-    n_cepstral_coef (int): Number of cepstral ceofficients
+        reference (): subsampled input signal envelope in dB SL in each critical band
+        distorted (): subsampled distorted output signal envelope
+        threshold (): threshold in dB SPL to include segment in calculation
+        add_noise (): additive Gaussian noise to ensure 0 cross-corr at low levels
+        segment_size (): segment size in ms used for the envelope LP filter (8 msec)
+        n_cepstral_coef (int): Number of cepstral ceofficients
 
     Returns:
-    mel_cepstral_average (): average of the modulation correlations across analysis frequency
-        bands and modulation frequency bands, basis functions 2 -6
-    mel_cepstral_low (): average over the four lower mod freq bands, 0 - 20 Hz
-    mel_cepstral_high (): average over the four higher mod freq bands, 20 - 125 Hz
-    mel_cepstral_modulation (): vector of cross-correlations by modulation frequency,
-            averaged over ananlysis frequency band
+        mel_cepstral_average (): average of the modulation correlations across analysis
+            frequency bands and modulation frequency bands, basis functions 2 -6
+        mel_cepstral_low (): average over the four lower mod freq bands, 0 - 20 Hz
+        mel_cepstral_high (): average over the four higher mod freq bands, 20 - 125 Hz
+        mel_cepstral_modulation (): vector of cross-correlations by modulation
+            frequency, averaged over ananlysis frequency band
 
     Updates:
-    James M. Kates, 24 October 2006.
-    Difference signal removed for cochlear model, 31 January 2007.
-    Absolute value added 13 May 2011.
-    Changed to loudness criterion for silence threshold, 28 August 2012.
-    Version using envelope modulation filters, 15 July 2014.
-    Modulation frequency vector output added 27 August 2014.
-    Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
+        James M. Kates, 24 October 2006.
+        Difference signal removed for cochlear model, 31 January 2007.
+        Absolute value added 13 May 2011.
+        Changed to loudness criterion for silence threshold, 28 August 2012.
+        Version using envelope modulation filters, 15 July 2014.
+        Modulation frequency vector output added 27 August 2014.
+        Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
     """
 
     # Processing parameters
@@ -1611,16 +1610,16 @@ def melcor9_crosscovmatrix(b, nmod, nbasis, nsamp, nfir, reference_cep, processe
     """Compute the cross-covariance matrix.
 
     Arguments:
-    b (): ???
-    nmod (): ???
-    nbasis (): ???
-    nsamp (): ???
-    nfir (): ???
-    xcep (): ???
-    ycep (): ???
+        b (): ???
+        nmod (): ???
+        nbasis (): ???
+        nsamp (): ???
+        nfir (): ???
+        xcep (): ???
+        ycep (): ???
 
     Returns:
-    cross_covariance_matrix ():
+        cross_covariance_matrix ():
     """
     small = 1.0e-30
     nfir2 = nfir / 2
@@ -1695,8 +1694,8 @@ def spectrum_diff(reference_sl, processed_sl):
            <http://www.aes.org/e-lib/browse.cfm?elib=13018>.
 
     Updates:
-    James M. Kates, 28 June 2012.
-    Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
+        James M. Kates, 28 June 2012.
+        Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
     """
 
     # Convert the dB SL to linear magnitude values. Because of the auditory
@@ -1758,25 +1757,26 @@ def bm_covary(
     reference_basilar_membrane, processed_basilar_membrane, segment_size, freq_sample
 ):
     """
-    Compute the cross-covariance (normalized cross-correlation) between
-    the reference and processed signals in each auditory band. The
-    signals are divided into segments having 50% overlap.
+    Compute the cross-covariance (normalized cross-correlation) between  the reference
+    and processed signals in each auditory band. The signals are divided into segments
+    having 50% overlap.
 
     Arguments:
-    reference_basilar_membrane (): Basilar Membrane movement, reference signal
-    processed_basilar_membrane (): Basilar Membrane movement, processed signal
-    segment_size (): signal segment size, msec
-    freq_sample (int): sampling rate in Hz
+        reference_basilar_membrane (): Basilar Membrane movement, reference signal
+        processed_basilar_membrane (): Basilar Membrane movement, processed signal
+        segment_size (): signal segment size, msec
+        freq_sample (int): sampling rate in Hz
 
     Returns:
-    signal_cross_covariance (np.array) : [nchan,nseg] of cross-covariance values
-    reference_mean_square (np.array) : [nchan,nseg] of MS input signal energy values
-    processed_mean_square (np.array) : [nchan,nseg] of MS processed signal energy values
+        signal_cross_covariance (np.array) : [nchan,nseg] of cross-covariance values
+        reference_mean_square (np.array) : [nchan,nseg] of MS input signal energy values
+        processed_mean_square (np.array) : [nchan,nseg] of MS processed signal energy
+            values
 
     Updates:
-    James M. Kates, 28 August 2012.
-    Output amplitude adjustment added, 30 october 2012.
-    Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
+        James M. Kates, 28 August 2012.
+        Output amplitude adjustment added, 30 october 2012.
+        Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
     """
 
     # Initialize parameters
@@ -1946,19 +1946,19 @@ def ave_covary2(
     frequencies Johnson[2]_.
 
     Arguments:
-    signal_cross_covariance (np.array): [nchan,nseg] of cross-covariance values
-    reference_signal_mean_square (np.array): [nchan,nseg] of reference signal MS
-        values
-    threshold_db (): threshold in dB SL to include segment ave over freq in
-        average
-    lp_filter (list): LP filter order
-    freq_cutoff (list): Cutoff frequencies in Hz
+        signal_cross_covariance (np.array): [nchan,nseg] of cross-covariance values
+        reference_signal_mean_square (np.array): [nchan,nseg] of reference signal MS
+            values
+        threshold_db (): threshold in dB SL to include segment ave over freq in
+            average
+        lp_filter (list): LP filter order
+        freq_cutoff (list): Cutoff frequencies in Hz
 
     Returns:
-    average_covariance (): cross-covariance in segments averaged over time and
-        frequency
-    ihc_sync_covariance (): cross-coraviance array, 6 different weightings for loss of
-              IHC synchronization at high frequencies:
+        average_covariance (): cross-covariance in segments averaged over time and
+            frequency
+        ihc_sync_covariance (): cross-coraviance array, 6 different weightings for loss
+            of IHC synchronization at high frequencies:
               LP Filter Order     Cutoff Freq, kHz
                 1              1.5
                 3              2.0
@@ -1975,12 +1975,13 @@ def ave_covary2(
            responses of auditory‐nerve fibers to single tones J Acoustocal Soc of Am
            68:1115 Available at.
            <https://doi.org/10.1121/1.384982>
+
     Updates:
-    James M. Kates, 28 August 2012.
-    Adjusted for BM vibration in dB SL, 30 October 2012.
-    Threshold for including time-freq tile modified, 30 January 2013.
-    Version for different sync loss, 15 February 2013.
-    Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
+        James M. Kates, 28 August 2012.
+        Adjusted for BM vibration in dB SL, 30 October 2012.
+        Threshold for including time-freq tile modified, 30 January 2013.
+        Version for different sync loss, 15 February 2013.
+        Translated from MATLAB to Python by Gerardo Roa Dabike, September 2022.
     """
 
     # Array dimensions
diff --git a/clarity/evaluator/haspi/ebm.py b/clarity/evaluator/haspi/ebm.py
index ebe912c59..d5c19cc96 100644
--- a/clarity/evaluator/haspi/ebm.py
+++ b/clarity/evaluator/haspi/ebm.py
@@ -13,21 +13,22 @@ def env_filter(reference_db, processed_db, filter_cutoff, freq_sub_sample, freq_
     the filtering operation.
 
     Args:
-    reference_db (np.ndarray): env in dB SL for the ref signal in each auditory band
-    processed_db (np.ndarray): env in dB SL for the degraded signal in each auditory band
-    filter_cutoff ():  LP filter cutoff frequency for the filtered envelope, Hz
-    freq_sub_samp ():  subsampling frequency in Hz for the LP filtered envelopes
-    freq_samp ():  sampling rate in Hz for the signals xdB and ydB
+        reference_db (np.ndarray): env in dB SL for the ref signal in each auditory band
+        processed_db (np.ndarray): env in dB SL for the degraded signal in each auditory
+            band
+        filter_cutoff ():  LP filter cutoff frequency for the filtered envelope, Hz
+        freq_sub_samp ():  subsampling frequency in Hz for the LP filtered envelopes
+        freq_samp ():  sampling rate in Hz for the signals xdB and ydB
 
     Returns:
-    tuple: reference_env - LP filtered and subsampled reference signal envelope
+        tuple: reference_env - LP filtered and subsampled reference signal envelope
            Each frequency band is a separate column.
            processed_env - LP filtered and subsampled degraded signal envelope
 
 
     Updates:
-    James M. Kates, 12 September 2019.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 12 September 2019.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     # Check the filter design parameters
     assert (
@@ -53,10 +54,11 @@ def env_filter(reference_db, processed_db, filter_cutoff, freq_sub_sample, freq_
     nhalf = floor(nfilt / 2)
     nfilt = int(2 * nhalf)  # Force an even filter length
 
-    # Design the FIR LP filter using a von Hann window to ensure that there are no negative
-    # envelope values. The MATLAB code uses the hanning() function, which returns the Hann
-    # window without the first and last zero-weighted window samples,
-    # unlike np.hann and scipy.signal.windows.hann; the code below replicates this behaviour
+    # Design the FIR LP filter using a von Hann window to ensure that there are no
+    # negative envelope values. The MATLAB code uses the hanning() function, which
+    # returns the Hann window without the first and last zero-weighted window samples,
+    # unlike np.hann and scipy.signal.windows.hann; the code below replicates this
+    # behaviour
     window = 0.5 * (1 - np.cos(2 * np.pi * np.arange(1, nfilt / 2 + 1) / (nfilt + 1)))
     benv = np.concatenate((window, np.flip(window)))
     benv = benv / np.sum(benv)
@@ -87,21 +89,22 @@ def cepstral_correlation_coef(
     calculation.
 
     Args:
-    reference_db ():      subsampled reference signal envelope in dB SL in each band
-    processed_db ():	    subsampled distorted output signal envelope
-    thresh_cep ():   threshold in dB SPL to include sample in calculation
-    thresh_nerve (): additive noise RMS for IHC firing (in dB)
-    nbasis    number of cepstral basis functions to use
+        reference_db (): subsampled reference signal envelope in dB SL in each band
+        processed_db (): subsampled distorted output signal envelope
+        thresh_cep (): threshold in dB SPL to include sample in calculation
+        thresh_nerve (): additive noise RMS for IHC firing (in dB)
+        nbasis: number of cepstral basis functions to use
 
     Returns:
-    tuple: refernce_cep cepstral coefficient matrix for the ref signal (nsamp,nbasis)
-           processed_cep cepstral coefficient matrix for the output signal (nsamp,nbasis) each column is a separate basis function, from low to high
+        tuple: reference_cep cepstral coefficient matrix for the ref signal
+        (nsamp,nbasis) processed_cep cepstral coefficient matrix for the output signal
+        (nsamp,nbasis) each column is a separate basis function, from low to high
 
     Updates:
-    James M. Kates, 23 April 2015.
-    Gammawarp version to fit the basis functions, 11 February 2019.
-    Additive noise for IHC firing rates, 24 April 2019.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 23 April 2015.
+        Gammawarp version to fit the basis functions, 11 February 2019.
+        Additive noise for IHC firing rates, 24 April 2019.
+            Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     # Processing parameters
     nbands = reference_db.shape[1]
@@ -139,8 +142,9 @@ def cepstral_correlation_coef(
     reference_cep = reference_db @ cepm
     processed_cep = processed_db @ cepm
 
-    # Remove the average value from the cepstral coefficients. The cepstral cross-correlation
-    # will thus be a cross-covariance, and there is no effect of the absolute signal level in dB.
+    # Remove the average value from the cepstral coefficients. The cepstral
+    # cross-correlation will thus be a cross-covariance, and there is no effect of the
+    # absolute signal level in dB.
     for n in range(nbasis):
         x = reference_cep[:, n]
         x = x - np.mean(x)
@@ -158,15 +162,15 @@ def add_noise(reference_db, thresh_db):
     in each auditory frequency band.
 
     Args:
-    reference_db (): subsampled envelope in dB re:auditory threshold
-    thresh_db (): additive noise RMS level (in dB)
+        reference_db (): subsampled envelope in dB re:auditory threshold
+        thresh_db (): additive noise RMS level (in dB)
 
     Returns:
       () envelope with threshold noise added, in dB re:auditory threshold
 
-    Updates
-    James M. Kates, 23 April 2019.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+    Updates:
+        James M. Kates, 23 April 2019.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     # Additive noise sequence
     # Gaussian noise with RMS=1, then scaled
@@ -194,30 +198,31 @@ def fir_modulation_filter(
     filter outputs.
 
     Args:
-    reference_envelope (np.ndarray) : matrix containing the subsampled reference envelope values. Each
-             column is a different frequency band or cepstral basis function
-             arranged from low to high.
-    processed_envelope (np.ndarray): matrix containing the subsampled processed envelope values
-    freq_sub_sampling (): envelope sub-sampling rate in Hz
-    center_frequencies (np.ndarray): Center Frequencies
+        reference_envelope (np.ndarray) : matrix containing the subsampled reference
+            envelope values. Each column is a different frequency band or cepstral basis
+            function arranged from low to high.
+        processed_envelope (np.ndarray): matrix containing the subsampled processed
+            envelope values
+        freq_sub_sampling (): envelope sub-sampling rate in Hz
+        center_frequencies (np.ndarray): Center Frequencies
 
     Returns:
-    tuple:  reference_modulation ():  a cell array containing the
-                reference signal output of the modulation filterbank.
-                reference_modulation is of size {nchan,nmodfilt} where
-                nchan is the number of frequency channels or cepstral
-                basis functions in reference_envelope, and nmodfilt is
-                the number of modulation filters used in the analysis. Each cell contains a column vector
-                of length nsamp, where nsamp is the number of samples in each
-                envelope sequence contained in the columns of reference_envelope.
-            processed_modulation (): cell array containing the processed signal output of the
-                modulation filterbank.
-            center_frequencies (): vector of the modulation rate filter center frequencies
+        tuple:
+            reference_modulation ():  a cell array containing the reference signal
+                output of the modulation filterbank. reference_modulation is of size
+                [nchan,nmodfilt] where nchan is the number of frequency channels or
+                cepstral basis functions in reference_envelope, and nmodfilt is the
+                number of modulation filters used in the analysis. Each cell contains a
+                column vector of length nsamp, where nsamp is the number of samples in
+                each envelope sequence contained in the columns of reference_envelope.
+            processed_modulation (): cell array containing the processed signal output
+                of the modulation filterbank.
+            center_frequencies (): vector of modulation rate filter center frequencies
 
     Updates:
-    James M. Kates, 14 February 2019.
-    Two matrix version of gwarp_ModFiltWindow, 19 February 2019.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 14 February 2019.
+        Two matrix version of gwarp_ModFiltWindow, 19 February 2019.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
 
     # Input signal properties
@@ -340,28 +345,28 @@ def fir_modulation_filter(
 
 def modulation_cross_correlation(reference_modulation, processed_modulation):
     """
-     Compute the cross-correlations between the input signal time-frequency
-     envelope and the distortion time-frequency envelope. The cepstral
-     coefficients or envelopes in each frequency band have been passed
-     through the modulation filterbank using function ebm_ModFilt.
-
-     Args:
-     reference_modulation (np.array): cell array containing the reference signal output of the
-              modulation filterbank. Xmod is of size {nchan,nmodfilt} where
-              nchan is the number of frequency channels or cepstral basis
-              functions in Xenv, and nmodfilt is the number of modulation
-              filters used in the analysis. Each cell contains a column vector
-              of length nsamp, where nsamp is the number of samples in each
-              envelope sequence contained in the columns of Xenv.
-    processed_modulation (np.ndarray): subsampled distorted output signal envelope
-
-     Output:
-         float: aveCM modulation correlations averaged over basis functions 2-6
-              vector of size nmodfilt
-
-     Updates:
-     James M. Kates, 21 February 2019.
-     Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+    Compute the cross-correlations between the input signal time-frequency
+    envelope and the distortion time-frequency envelope. The cepstral
+    coefficients or envelopes in each frequency band have been passed
+    through the modulation filterbank using function ebm_ModFilt.
+
+    Args:
+       reference_modulation (np.array): cell array containing the reference signal
+           output of the modulation filterbank. Xmod is of size [nchan,nmodfilt] where
+           nchan is the number of frequency channels or cepstral basis functions in
+           Xenv, and nmodfilt is the number of modulation filters used in the analysis.
+           Each cell contains a column vector of length nsamp, where nsamp is the
+           number of samples in each envelope sequence contained in the columns of
+           Xenv.
+       processed_modulation (np.ndarray): subsampled distorted output signal envelope
+
+    Output:
+        float: aveCM modulation correlations averaged over basis functions 2-6
+             vector of size nmodfilt
+
+    Updates:
+       James M. Kates, 21 February 2019.
+       Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     # Processing parameters
     nchan = reference_modulation.shape[0]  # Number of basis functions
diff --git a/clarity/evaluator/haspi/haspi.py b/clarity/evaluator/haspi/haspi.py
index 8a19c3d4d..e20b05f62 100644
--- a/clarity/evaluator/haspi/haspi.py
+++ b/clarity/evaluator/haspi/haspi.py
@@ -31,28 +31,31 @@ def haspi_v2(  # pylint: disable=too-many-arguments too-many-locals
     neural networks to compute the estimated intelligibility.
 
     Args:
-    reference (np.ndarray): Clear input reference speech signal with no noise or distortion.
-              If a hearing loss is specified, no amplification should be provided.
-    reference_freq (int): Sampling rate in Hz for signal x
-    processed (np.ndarray): Output signal with noise, distortion, HA gain, and/or processing.
-    processed_freq (int): Sampling rate in Hz for signal y.
-    hearing_loss (np.ndarray): (1,6) vector of hearing loss at the 6 audiometric frequencies
-                    [250, 500, 1000, 2000, 4000, 6000] Hz.
-    level1 (int): Optional input specifying level in dB SPL that corresponds to a
-              signal RMS = 1. Default is 65 dB SPL if argument not provided.
-    f_lp (int):
-    itype (int): Intelligibility model
+        reference (np.ndarray): Clear input reference speech signal with no noise or
+            distortion. If a hearing loss is specified, no amplification should be
+            provided.
+        reference_freq (int): Sampling rate in Hz for signal x
+        processed (np.ndarray): Output signal with noise, distortion, HA gain, and/or
+            processing.
+        processed_freq (int): Sampling rate in Hz for signal y.
+        hearing_loss (np.ndarray): (1,6) vector of hearing loss at the 6 audiometric
+            frequencies [250, 500, 1000, 2000, 4000, 6000] Hz.
+        level1 (int): Optional input specifying level in dB SPL that corresponds to a
+            signal RMS = 1. Default is 65 dB SPL if argument not provided.
+        f_lp (int):
+        itype (int): Intelligibility model
 
     Returns:
-    Intel     Intelligibility estimated by passing the cepstral coefficients
+        tuple(Intel: float, raw: nd-array)
+        Intel: Intelligibility estimated by passing the cepstral coefficients
               through a modulation filterbank followed by an ensemble of
               neural networks.
-    raw       vector of 10 cep corr modulation filterbank outputs, averaged
+        raw: vector of 10 cep corr modulation filterbank outputs, averaged
               over basis funct 2-6.
 
     Updates:
-    James M. Kates, 5 August 2013.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 5 August 2013.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
 
     # Auditory model for intelligibility
@@ -75,7 +78,7 @@ def haspi_v2(  # pylint: disable=too-many-arguments too-many-locals
         reference_env, processed_env, f_lp, fsub, fsamp
     )
 
-    # Compute the ceptstral coefficients as a function of subsampled time
+    # Compute the cepstral coefficients as a function of subsampled time
     nbasis = 6  # Use 6 basis functions
     thr = 2.5  # Silence threshold in dB SL
     dither = 0.1  # Dither in dB RMS to add to envelope signals
@@ -83,7 +86,7 @@ def haspi_v2(  # pylint: disable=too-many-arguments too-many-locals
         reference_lp, processed_lp, thr, dither, nbasis
     )
 
-    # Cepstral coeffifiencts filtered at each modulation rate
+    # Cepstral coefficients filtered at each modulation rate
     # Band center frequencies [2, 6, 10, 16, 25, 40, 64, 100, 160, 256] Hz
     # Band edges [0, 4, 8, 12.5, 20.5, 30.5, 52.4, 78.1, 128, 200, 328] Hz
     reference_mod, processed_mod, _ = fir_modulation_filter(
@@ -131,21 +134,21 @@ def haspi_v2_be(  # pylint: disable=too-many-arguments
     Calculates HASPI for left and right ear and selects the better result.
 
     Args:
-    ref_left (np.ndarray): left channel of reference signal
-    ref_right (np.ndarray): right channel of reference signal
-    proc_left (np.ndarray): left channel of processed signal
-    proc_right (np.ndarray): right channel of processed signal
-    fs_signal (int): sampling rate for both signal
-    audiogram_left (): left ear audiogram
-    audiogram_right (): right ear audiogram
-    audiogram_cfs: audiogram frequencies
-    Level: level in dB SPL corresponding to RMS=1
+        ref_left (np.ndarray): left channel of reference signal
+        ref_right (np.ndarray): right channel of reference signal
+        proc_left (np.ndarray): left channel of processed signal
+        proc_right (np.ndarray): right channel of processed signal
+        fs_signal (int): sampling rate for both signal
+        audiogram_left (): left ear audiogram
+        audiogram_right (): right ear audiogram
+        audiogram_cfs: audiogram frequencies
+        level: level in dB SPL corresponding to RMS=1
 
     Returns:
-    float: beHASPI score
+        float: beHASPI score
 
     Updates:
-    Zuzanna Podwinska, March 2022
+        Zuzanna Podwinska, March 2022
     """
 
     # HASPI assumes the following audiogram frequencies:
diff --git a/clarity/evaluator/haspi/ip.py b/clarity/evaluator/haspi/ip.py
index ef14182a6..1184e9d68 100644
--- a/clarity/evaluator/haspi/ip.py
+++ b/clarity/evaluator/haspi/ip.py
@@ -14,17 +14,18 @@ def get_neural_net():
     Arguments: None
 
     Returned values:
-    neural_net_params (dict): parameters defining the neural network
-    weights_hidden (): cell array 10 x 1 for the weights linking the input to the hidden
-             layer. Each cell is a 11 x 4 matrix of weights
-    weights_out (): call array 5 x 1 for the weights linking the hidden to the output
-             layer. Each cell is a 5 x 1 vector of weights.
-    normalization_factor (): normalization so that the maximum neural net output is exactly 1.
+        neural_net_params (dict): parameters defining the neural network
+        weights_hidden (): cell array 10 x 1 for the weights linking the input to the
+        hidden layer. Each cell is a 11 x 4 matrix of weights
+        weights_out (): call array 5 x 1 for the weights linking the hidden to the
+            output layer. Each cell is a 5 x 1 vector of weights.
+        normalization_factor (): normalization so that the maximum neural net output is
+            exactly 1.
 
     Updates:
-    James M. Kates, 8 October 2019.
-    Version for new neural network using actual TFS scores, 24 October 2019.
-    Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
+        James M. Kates, 8 October 2019.
+        Version for new neural network using actual TFS scores, 24 October 2019.
+        Translated from MATLAB to Python by Zuzanna Podwinska, March 2022.
     """
     # Set up the neural network parameters
     neural_net_params = {}
@@ -235,7 +236,7 @@ def nn_feed_forward_ensemble(
     ncond = data.shape[0]  # Number of conditions in the input data
     n_networks = len(weights_hidden)  # Number of networks in the ensemble
 
-    # Ensemble average of the predictions over the set of neural networks used for training
+    # Ensemble average of predictions over set of neural networks used for training
     predict = np.zeros((ncond, n_networks))
     for k in range(n_networks):
         for n in range(ncond):
diff --git a/clarity/evaluator/hasqi/hasqi.py b/clarity/evaluator/hasqi/hasqi.py
index 9308a3bc2..f742652c3 100644
--- a/clarity/evaluator/hasqi/hasqi.py
+++ b/clarity/evaluator/hasqi/hasqi.py
@@ -22,27 +22,32 @@ def hasqi_v2(
     impaired hearing.
 
     Arguments:
-    reference (np.ndarray): Clear input reference speech signal with no noise or distortion.
-              If a hearing loss is specified, NAL-R equalization is optional
-    reference_freq (int): Sampling rate in Hz for reference signal.
-    processed (np.ndarray): Output signal with noise, distortion, HA gain, and/or processing.
-    processed_freq (int): Sampling rate in Hz for processed signal.
-    hearing_loss (np.ndarray): vector of hearing loss at the 6 audiometric frequencies
-                  [250, 500, 1000, 2000, 4000, 6000] Hz.
-    equalisation (int): Flag to provide equalization for the hearing loss to reference signal:
+        reference (np.ndarray): Clear input reference speech signal with no noise or
+            distortion. If a hearing loss is specified, NAL-R equalization is optional
+        reference_freq (int): Sampling rate in Hz for reference signal.
+        processed (np.ndarray): Output signal with noise, distortion, HA gain, and/or
+            processing.
+        processed_freq (int): Sampling rate in Hz for processed signal.
+        hearing_loss (np.ndarray): vector of hearing loss at the 6 audiometric
+            frequencies [250, 500, 1000, 2000, 4000, 6000] Hz.
+        equalisation (int): Mode to use when equalising the reference signal:
                 1 = no EQ has been provided, the function will add NAL-R
                 2 = NAL-R EQ has already been added to the reference signal
-    level1    Optional input specifying level in dB SPL that corresponds to a
+        level1: Optional input specifying level in dB SPL that corresponds to a
               signal RMS = 1. Default is 65 dB SPL if argument not provided.
-    silence_threshold (float): Silence threshold sum across bands, dB above audio threshold. Default: 2.5
-    add_noise (float): Additive noise in dB SL to conditiona cross-covariance. Default: 0.0
-    segment_covariance (int): Segment size for the covariance calculation. Default: 16
+        silence_threshold (float): Silence threshold sum across bands, dB above audio
+            threshold. Default: 2.5
+        add_noise (float): Additive noise in dB SL to conditiona cross-covariance.
+            Default is 0.0
+        segment_covariance (int): Segment size for the covariance calculation.
+            Default is 16
 
     Returns:
-    Combined  Quality estimate is the product of the nonlinear and linear terms
-    Nonlin    Nonlinear quality component = (cepstral corr)^2 x seg BM coherence
-    Linear    Linear quality component = std of spectrum and spectrum slope
-    raw       Vector of raw values = [CepCorr, BMsync5, Dloud, Dslope]
+        tuple(Combined, Nonlin, Linear, raw)
+            Combined: Quality estimate is the product of the nonlinear and linear terms
+            Nonlin: Nonlinear quality component = (cepstral corr)^2 x seg BM coherence
+            Linear: Linear quality component = std of spectrum and spectrum slope
+            raw: Vector of raw values = [CepCorr, BMsync5, Dloud, Dslope]
 
     James M. Kates, 5 August 2013.
     Translated from MATLAB to Python by Gerardo Roa Dabike, October 2022.
@@ -76,7 +81,7 @@ def hasqi_v2(
     # Mel cepstrum correlation using smoothed envelopes
     (
         average_cepstral_correlation,
-        individual_cepstral_correlations,
+        _individual_cepstral_correlations,
     ) = eb.mel_cepstrum_correlation(
         reference_smooth, processed_smooth, silence_threshold, add_noise
     )
@@ -85,14 +90,14 @@ def hasqi_v2(
     # dloud  vector: [sum abs diff, std dev diff, max diff] spectra
     # dnorm  vector: [sum abs diff, std dev diff, max diff] norm spectra
     # dslope vector: [sum abs diff, std dev diff, max diff] slope
-    d_loud, d_norm, d_slope = eb.spectrum_diff(reference_sl, processed_sl)
+    d_loud, _d_norm, d_slope = eb.spectrum_diff(reference_sl, processed_sl)
 
     # Temporal fine structure correlation measurements
     # Compute the time-frequency segment covariances
     (
         signal_cross_covariance,
         reference_mean_square,
-        processed_mean_square,
+        _processed_mean_square,
     ) = eb.bm_covary(
         reference_basilar_membrane,
         processed_basilar_membrane,
diff --git a/clarity/evaluator/mbstoi/mbstoi.py b/clarity/evaluator/mbstoi/mbstoi.py
index bc5d4fe98..6b47ce663 100644
--- a/clarity/evaluator/mbstoi/mbstoi.py
+++ b/clarity/evaluator/mbstoi/mbstoi.py
@@ -46,7 +46,7 @@ def mbstoi(
     tau_0: float = 1.6e-3,
     level_shift_deviation: float = 1.6,
 ) -> float:
-    """Implementation of the Modified Binaural Short-Time Objective Intelligibility (mbstoi) measure.
+    """The Modified Binaural Short-Time Objective Intelligibility (mbstoi) measure.
 
     Args:
         left_ear_clean (np.ndarray): Clean speech signal from left ear.
@@ -54,7 +54,8 @@ def mbstoi(
         left_ear_noisy (np.ndarray) : Noisy/processed speech signal from left ear.
         right_ear_noisy (np.ndarray) : Noisy/processed speech signal from right ear.
         fs_signal (int) : Frequency sample rate of signal.
-        gridcoarseness (int) : Grid coarseness as denominator of ntaus and ngammas (default: 1).
+        gridcoarseness (int) : Grid coarseness as denominator of ntaus and ngammas.
+            Defaults to 1.
         sample_rate (int) :  Sample Rate.
         n_frame (int) :  Number of Frames.
         fft_size_in_samples (int) :  ??? size in samples.
@@ -76,14 +77,15 @@ def mbstoi(
         float : mbtsoi index d.
 
     Notes:
-        All title, copyrights and pending patents pertaining to mbtsoi[1]_ in and to the original Matlab software are
-        owned by oticon a/s and/or Aalborg University. please see details at
-        `http://ah-andersen.net/code/<http://ah-andersen.net/code/>`
+        All title, copyrights and pending patents pertaining to mbtsoi[1]_ in and to the
+        original Matlab software are owned by oticon a/s and/or Aalborg University.
+        Please see `http://ah-andersen.net/code/<http://ah-andersen.net/code/>`
 
 
-    .. [1] A. H. Andersen, J. M. de Haan, Z.-H. Tan, and J. Jensen (2018) Refinement and validation of the binaural
-    short time objective intelligibility measure for spatially diverse conditions. Speech Communication vol. 102,
-    pp. 1-13 `doi:10.1016/j.specom.2018.06.001 <https://doi.org/10.1016/j.specom.2018.06.001>`_
+    .. [1] A. H. Andersen, J. M. de Haan, Z.-H. Tan, and J. Jensen (2018) Refinement and
+        validation of the binaural short time objective intelligibility measure for
+        spatially diverse conditions. Speech Communication vol. 102, pp. 1-13
+        doi:10.1016/j.specom.2018.06.001 <https://doi.org/10.1016/j.specom.2018.06.001>
     """
 
     n_taus = math.ceil(100 / gridcoarseness)  # number of tau values to try out
diff --git a/clarity/evaluator/mbstoi/mbstoi_utils.py b/clarity/evaluator/mbstoi/mbstoi_utils.py
index 4f6e3856e..53a1fdba1 100644
--- a/clarity/evaluator/mbstoi/mbstoi_utils.py
+++ b/clarity/evaluator/mbstoi/mbstoi_utils.py
@@ -27,35 +27,40 @@ def equalisation_cancellation(
 ):
     """Run the equalisation-cancellation (EC) stage of the MBSTOI metric.
 
-    The EC loop evaluates one huge equation in every iteration (see referenced notes for details). The left and right
-    ear signals are level adjusted by gamma (in dB) and time shifted by tau relative to one-another and are thereafter
-    subtracted. The processed signals are treated similarly. To obtain performance similar to that of humans,the EC
-    stage adds jitter. We are searching for the level and time adjustments that maximise the intermediate correlation
-    coefficients d. Could add location of source and interferer to this to reduce search space.
+    The EC loop evaluates one huge equation in every iteration (see referenced notes
+    for details). The left and right ear signals are level adjusted by gamma (in dB) and
+    time shifted by tau relative to one-another and are thereafter subtracted. The
+    processed signals are treated similarly. To obtain performance similar to that of
+    humans,the EC stage adds jitter. We are searching for the level and time adjustments
+    that maximise the intermediate correlation coefficients d. Could add location of
+    source and interferer to this to reduce search space.
 
     Args:
-        left_ear_clean_hat (np.ndarray) : Clean left ear short-time DFT coefficients (single-sided) per frequency
-    bin and frame.
-        right_ear_clean_hat (np.ndarray) : Clean right ear short-time DFT coefficients (single-sided) per frequency bin and frame.
-        left_ear_noisy_hat (np.ndarray) : Noisy/processed left ear short-time DFT coefficients (single-sided) per
-    frequency bin and frame.
-        right_ear_noisy_hat (np.ndarray) : Noisy/processed right eat short-time DFT coefficients (single-sided) per
-    frequency bin and frame.
-    n_third_octave_bands (int) : Number of one-third octave bands.
-    n_frames (int) : Number of frames for intermediate intelligibility measure.
-    fids (np.ndarray) : Indices of frequency band edges.
-    cf (np.ndarray) : Centre frequencies.
-    taus (np.ndarray) : Interaural delay (tau) values.
-    n_taus (int) : Number of tau values.
-    gammas (np.ndarray) : Interaural level difference (gamma) values.
-    ngammas (int) : Number of gamma values.
-    intermediate_intelligibility_measure_grid (np.ndarray) : Grid for intermediate intelligibility measure.
-    p_ec_max (np.ndarray) : Empty grid for maximum values.
-    sigma_epsilon (np.ndarray) : Jitter for gammas.
-    sigma_delta (np.ndarray) : Jitter for taus.
+        left_ear_clean_hat (np.ndarray) : Clean left ear short-time DFT coefficients
+            (single-sided) per frequency bin and frame.
+        right_ear_clean_hat (np.ndarray) : Clean right ear short-time DFT coefficients
+            (single-sided) per frequency bin and frame.
+        left_ear_noisy_hat (np.ndarray) : Noisy/processed left ear short-time DFT
+            coefficients (single-sided) per frequency bin and frame.
+        right_ear_noisy_hat (np.ndarray) : Noisy/processed right eat short-time DFT
+            coefficients (single-sided) per frequency bin and frame.
+        n_third_octave_bands (int) : Number of one-third octave bands.
+        n_frames (int) : Number of frames for intermediate intelligibility measure.
+        fids (np.ndarray) : Indices of frequency band edges.
+        cf (np.ndarray) : Centre frequencies.
+        taus (np.ndarray) : Interaural delay (tau) values.
+        n_taus (int) : Number of tau values.
+        gammas (np.ndarray) : Interaural level difference (gamma) values.
+        ngammas (int) : Number of gamma values.
+        intermediate_intelligibility_measure_grid (np.ndarray) : Grid for intermediate
+            intelligibility measure.
+        p_ec_max (np.ndarray) : Empty grid for maximum values.
+        sigma_epsilon (np.ndarray) : Jitter for gammas.
+        sigma_delta (np.ndarray) : Jitter for taus.
 
     Returns:
-        intermediate_intelligibility_measure_grid (np.ndarray) : updated grid for intermediate intelligibility measure
+        intermediate_intelligibility_measure_grid (np.ndarray) : updated grid for
+            intermediate intelligibility measure
         p_ec_max (np.ndarray) : grid containing maximum values.
     """
     taus = np.expand_dims(taus, axis=0)
@@ -382,7 +387,8 @@ def remove_silent_frames(
         right_ear_clean (np.ndarray) : Clean input signal right channel.
         left_ear_noisy (np.ndarray) : Degraded/processed signal left channel.
         right_ear_noisy (np.ndarray) : Degraded/processed signal right channel.
-        dyn_range (np.ndarray) : Range, energy range to determine which frame is silent (default : 40).
+        dyn_range (np.ndarray) : Range, energy range to determine which frame is silent
+            Default is 40.
         framelen (int) : Window size for energy evaluation (default : 256).
         hop (int) : Hop size for energy evaluation (default : 128).
 
@@ -457,7 +463,8 @@ def remove_silent_frames(
 
 
 def thirdoct(frequency_sampling: int, nfft: int, num_bands: int, min_freq: int):
-    """Returns the 1/3 octave band matrix and its center frequencies based on mpariente/pystoi.
+    """Returns the 1/3 octave band matrix and its center frequencies
+    based on mpariente/pystoi.
 
     Args:
         fs (int) : Frequency sampling rate.
@@ -507,14 +514,16 @@ def thirdoct(frequency_sampling: int, nfft: int, num_bands: int, min_freq: int):
 
 
 def find_delay_impulse(ddf: np.ndarray, initial_value: int = 22050):
-    """Find binaural delay in signal ddf given initial location of unit impulse, initial_value.
+    """Find binaural delay in signal ddf.
+
+    Finds delay given initial location of unit impulse, initial_value.
 
     Args:
         ddf (np.ndarray) :
         initial_value: (int) : Initial value (default: 22050)
 
     Returns:
-
+        delay (np.ndarray) : Binaural delay.
     """
     pk0 = find_peaks(ddf[:, 0])
     pk1 = find_peaks(ddf[:, 1])
diff --git a/clarity/evaluator/msbg/cochlea.py b/clarity/evaluator/msbg/cochlea.py
index ba0526fe4..b69f85b64 100644
--- a/clarity/evaluator/msbg/cochlea.py
+++ b/clarity/evaluator/msbg/cochlea.py
@@ -230,7 +230,8 @@ def __init__(
 
         Args:
             audiogram (Audiogram): Audiogram characterising hearing loss
-            catch_up_level (float, optional): loudness catch-up level in dB (default: {105})
+            catch_up_level (float, optional): loudness catch-up level in dB
+                Default is 105 dB
             fs (float, optional): sampling frequency
 
         """
diff --git a/clarity/evaluator/msbg/msbg.py b/clarity/evaluator/msbg/msbg.py
index fb119b56d..fd00425ed 100644
--- a/clarity/evaluator/msbg/msbg.py
+++ b/clarity/evaluator/msbg/msbg.py
@@ -3,7 +3,7 @@
 import math
 
 import numpy as np
-from scipy import interpolate, signal
+import scipy
 from scipy.signal import firwin, lfilter
 
 from clarity.evaluator.msbg.cochlea import Cochlea
@@ -49,8 +49,8 @@ def set_audiogram(self, audiogram):
         """Set the audiogram to be used."""
         if np.max(audiogram.levels[audiogram.levels is not None]) > 80:
             logging.warning(
-                "Impairment too severe: Suggest you limit audiogram max to 80-90 dB HL, \
-                otherwise things go wrong/unrealistic."
+                "Impairment too severe: Suggest you limit audiogram max to"
+                "80-90 dB HL, otherwise things go wrong/unrealistic."
             )
         self.cochlea = Cochlea(audiogram=audiogram)
 
@@ -67,7 +67,7 @@ def get_src_correction(src_pos: str) -> np.array:
         elif src_pos == "df":
             src_correction = DF_ED
         elif src_pos == "ITU":  # transfer to same grid
-            field = interpolate.interp1d(ITU_HZ, ITU_ERP_DRP, kind="linear")
+            field = scipy.interpolate.interp1d(ITU_HZ, ITU_ERP_DRP, kind="linear")
             src_correction = field(HZ)
         else:
             logging.error(
@@ -82,12 +82,12 @@ def src_to_cochlea_filt(
     ) -> np.ndarray:
         """Simulate middle and outer ear transfer functions.
 
-        Made more general, Mar2012, to include diffuse field as well as ITU reference points,
-        that were included in DOS-versions of recruitment simulator, released ca 1999-2001,
-        and on hearing group website, Mar2012 variable [src_pos] takes one of 3
-        values: 'ff', 'df' and 'ITU' free-field to cochlea filter forwards or backward direction,
-        depends on 'backward' switch. NO LONGER via 2 steps. ff to eardrum and then via middle ear:
-        use same length FIR 5-12-97.
+        Made more general, Mar2012, to include diffuse field as well as ITU reference
+        points, that were included in DOS-versions of recruitment simulator, released
+        ca 1999-2001, and on hearing group website, Mar2012 variable [src_pos] takes one
+        of 3 values: 'ff', 'df' and 'ITU' free-field to cochlea filter forwards or
+        backward direction, depends on 'backward' switch. NO LONGER via 2 steps. ff to
+        eardrum and then via middle ear: use same length FIR 5-12-97.
 
         Args:
             ip_sig (ndarray): signal to process
@@ -110,7 +110,7 @@ def src_to_cochlea_filt(
 
         # sig from free field to cochlea: 0 dB at 1kHz
         correction = src_correction - MIDEAR
-        field = interpolate.interp1d(HZ, correction, kind="linear")
+        field = scipy.interpolate.interp1d(HZ, correction, kind="linear")
         last_correction = field(nyquist)  # generate synthetic response at Nyquist
 
         correction_used = np.append(correction[ixf_useful], last_correction)
@@ -120,12 +120,12 @@ def src_to_cochlea_filt(
 
         correction_used = correction_used.flatten()
         # Create filter with 23 msec window to do reasonable job down to about 100 Hz
-        # Scales with fs, falls over with longer windows in fir2 in original MATLAB version
+        # Scales with fs, fails with longer windows in fir2 in original MATLAB version
         n_wdw = 2 * math.floor((sample_frequency / 16e3) * 368 / 2)
         hz_used = hz_used / nyquist
 
         b = firwin2(n_wdw + 1, hz_used.flatten(), correction_used, window=("kaiser", 4))
-        op_sig = signal.lfilter(b, 1, ip_sig)
+        op_sig = scipy.signal.lfilter(b, 1, ip_sig)
 
         return op_sig
 
@@ -255,10 +255,10 @@ def process(self, signal: np.ndarray, add_calibration: bool = False) -> np.ndarr
             for x in signal
         ]
 
-        # Implement low-pass filter at top end of audio range: flat to Cutoff freq, tails
-        # below -80 dB. Suitable lpf for signals later converted to MP3, flat to 15 kHz.
-        # Small window to design low-pass FIR, to cut off high freq processing noise
-        # low-pass to something sensible, prevents exaggeration of > 15 kHz
+        # Implement low-pass filter at top end of audio range: flat to Cutoff freq,
+        # tails below -80 dB. Suitable lpf for signals later converted to MP3, flat to
+        # 15 kHz. Small window to design low-pass FIR, to cut off high freq processing
+        # noise low-pass to something sensible, prevents exaggeration of > 15 kHz
         winlen = 2 * math.floor(0.0015 * sample_frequency) + 1
         lpf44d1 = firwin(
             winlen, UPPER_CUTOFF_HZ / int(sample_frequency / 2), window=("kaiser", 8)
diff --git a/clarity/evaluator/msbg/msbg_utils.py b/clarity/evaluator/msbg/msbg_utils.py
index b93b2027f..bede043ec 100644
--- a/clarity/evaluator/msbg/msbg_utils.py
+++ b/clarity/evaluator/msbg/msbg_utils.py
@@ -126,9 +126,10 @@ def firwin2(
 
     Args:
         n_taps (int): The number of taps in the FIR filter.
-        frequencies (ndarray): The frequency sampling points. 0.0 to 1.0 with 1.0 being Nyquist.
+        frequencies (ndarray): The frequency sampling points. 0.0 to 1.0 with 1.0
+            being Nyquist.
         filter_gains (ndarray): The filter gains at the frequency sampling points.
-        window (string or (string, float), optional): See scipy.firwin2 (default: (None))
+        window (string or (string, float), optional): See scipy.firwin2. Default is None
         antisymmetric (bool, optional): Unused but present to main compatability
             with scipy firwin2.
 
@@ -168,8 +169,8 @@ def fir2(
 
     Args:
         filter_length (int): Order
-        frequencies (ndarray): The frequency sampling points (0 < frequencies < 1) where 1 is Nyquist rate.
-                        First and last elements must be 0 and 1 respectively.
+        frequencies (ndarray): The frequency sampling points (0 < frequencies < 1) where
+            1 is Nyquist rate. First and last elements must be 0 and 1 respectively.
         filter_gains (ndarray): The filter gains at the frequency sampling points.
         n_interpolate (int, optional): Number of points for freq response interpolation
             (default: max(smallest power of 2 greater than nn, 512))
@@ -238,8 +239,9 @@ def gen_tone(
     Args:
         freq (float): Frequency of tone in Hz.
         duration (float): Duration of tone in seconds.
-        sample_frequency (float, optional): Sample rate of generated tone in Hz. (default: 44100)
-        level (float, optional): Level of tone in dB SPL. (default: 0)
+        sample_frequency (float, optional): Sample rate of generated tone in Hz.
+            Default is 44100.
+        level (float, optional): Level of tone in dB SPL. Default is 0.
 
     Returns:
         np.ndarray
@@ -265,7 +267,8 @@ def gen_eh2008_speech_noise(
 ) -> np.ndarray:
     """Generate speech shaped noise.
 
-    Start with white noise and re-shape to ideal SII, ie flat to 500 Hz, and sloping -9db/oct beyond that.
+    Start with white noise and re-shape to ideal SII, ie flat to 500 Hz, and sloping
+        -9db/oct beyond that.
 
     Slightly different shape from SII stylised same as EarHEar 2008 paper, Moore et al.
 
@@ -273,7 +276,8 @@ def gen_eh2008_speech_noise(
         duration (float): Duration of signal in seconds
         sample_frequency (float): Sampling rate
         level (float, optional): Normalise to level dB if present
-        supplied_b (ndarray, optional): High-pass filter (default: uses built-in pre-emphasis filter)
+        supplied_b (ndarray, optional): High-pass filter. Default uses built-in
+            pre-emphasis filter
 
     Returns:
         ndarray: Noise signal
@@ -348,7 +352,8 @@ def generate_key_percent(
         signal (ndarray): The signal to analyse.
         threshold_db (float): fixed energy threshold (dB).
         window_length (int): length of window in samples.
-        percent_to_track (float, optional): Track a percentage of frames (default: {None}).
+        percent_to_track (float, optional): Track a percentage of frames.
+            Default is None
 
     Raises:
         ValueError: percent_to_track is set too high.
@@ -422,7 +427,8 @@ def generate_key_percent(
     # histogram should produce a two-peaked curve: thresh should be set in valley
     # between the two peaks, and set threshold a bit above that,
     # as it heads for main peak
-    # FixMe : Could Otsu's method (from image processing) be used here? https://en.wikipedia.org/wiki/Otsu's_method
+    # TODO : Could Otsu's method (from image processing) be used here?
+    # https://en.wikipedia.org/wiki/Otsu's_method
     frame_index = np.nonzero(every_db >= expected)[0]
     valid_frames = len(frame_index)
     key = np.zeros((1, valid_frames * window_length))[0]
@@ -512,7 +518,7 @@ def read_signal(
 
     Args:
         filename (string): Name of file to read
-        offset (int, optional): Offset in samples or seconds (from start). Defaults to 0.
+        offset (int, optional): Offset in samples or seconds (from start). Default is 0.
         nsamples (int): Number of samples.
         nchannels (int): expected number of channel (default: 0 = any number OK)
         offset_is_samples (bool): measurement units for offset (default: False)
@@ -520,14 +526,11 @@ def read_signal(
     Returns:
         np.ndarray: audio signal
     """
-    try:
-        wave_file = SoundFile(filename)
-    except Exception as e:
-        # Ensure incorrect error (24 bit) is not generated
-        raise Exception(f"Unable to read {filename}.") from e
+
+    wave_file = SoundFile(filename)
 
     if nchannels not in (0, wave_file.channels):
-        raise Exception(
+        raise ValueError(
             f"Wav file ({filename}) was expected to have {nchannels} channels."
         )
 
@@ -564,7 +567,8 @@ def write_signal(
 
     if sample_frequency != MSBG_FS:
         logging.warning(
-            f"Sampling rate mismatch: {filename} with sample frequency = {sample_frequency}."
+            f"Sampling rate mismatch: {filename} with "
+            f"sample frequency = {sample_frequency}."
         )
         # raise ValueError("Sampling rate mismatch")
 
diff --git a/clarity/predictor/torch_msbg.py b/clarity/predictor/torch_msbg.py
index 62798a508..578e02d82 100644
--- a/clarity/predictor/torch_msbg.py
+++ b/clarity/predictor/torch_msbg.py
@@ -6,12 +6,12 @@
 
 import numpy as np
 import torch
-import torch.nn as nn
 import torch.nn.functional as F
 import torchaudio
 from scipy.fftpack import fft
 from scipy.interpolate import interp1d
 from scipy.signal import ellip, firwin, firwin2, freqz
+from torch import nn
 
 from clarity.evaluator.msbg.msbg_utils import (
     DF_ED,
@@ -27,12 +27,13 @@
 # set RMS so that peak of output file so that no clipping occurs, set so that
 # equiv0dBfileSPL > 100dB for LOUD input files
 ref_RMSdB = -31.2
-calib_dBSPL = (
-    65  # what RMS of INPUT speech file translates to in real world (unweighted)
-)
-equiv0dBfileSPL = (
-    calib_dBSPL - ref_RMSdB
-)  # what 0dB file signal would translate to in dB SPL: constant for cochlear_simulate function
+
+# what RMS of INPUT speech file translates to in real world (unweighted)
+calib_dBSPL = 65
+
+# what 0dB file signal would translate to in dB SPL:
+# constant for cochlear_simulate function
+equiv0dBfileSPL = calib_dBSPL - ref_RMSdB
 
 # clarity msbg
 equiv0dBSPL = 100
@@ -76,16 +77,19 @@ def generate_key_percent(signal, thr_dB, winlen):
 
 
 def measure_rms(signal, sr, dB_rel_rms):
-    """Measures toatal power of all 10 msec frams that are above a user-specified threshold
+    """Compute RMS level of a signal.
+
+    Measures total power of all 10 msec frames that are above a user-specified threshold
 
     Args:
         signal: input signal
         sr: sampling rate
-        dB_rel_rms: threshold relative to first-stage rms (if it is made of a 2*1 array, second value over rules.)
-                    only single value supported currently
+        dB_rel_rms: threshold relative to first-stage rms (if it is made of a 2*1 array,
+            second value over rules. only single value supported currently)
+
     Returns:
-        tuple: The percentage of frames that are required to be tracked for measuring RMS (useful when DR compression
-               changes histogram shape)
+        tuple: The percentage of frames that are required to be tracked for measuring
+        RMS (useful when DR compression changes histogram shape)
     """
     win_secs = 0.01
     # first RMS is of all signal
diff --git a/clarity/utils/car_noise_simulator/carnoise_parameters_generator.py b/clarity/utils/car_noise_simulator/carnoise_parameters_generator.py
index ee7303a38..83dc83a92 100644
--- a/clarity/utils/car_noise_simulator/carnoise_parameters_generator.py
+++ b/clarity/utils/car_noise_simulator/carnoise_parameters_generator.py
@@ -72,8 +72,8 @@ def __init__(
                 for Secondary Filter
             secondary_filter_constant_hz (float, optional): The constant cutoff in Hz
                 for Secondary Filter
-            random_flag (bool, optional): Flag to indicate whether some parameters should
-                be randomized or not
+            random_flag (bool, optional): Flag to indicate whether some parameters
+                should be randomized or not
             random_seed (int, optional): Random seed to use for randomization
 
         """
diff --git a/clarity/utils/car_noise_simulator/carnoise_signal_generator.py b/clarity/utils/car_noise_simulator/carnoise_signal_generator.py
index 44907264b..7d87f0f33 100644
--- a/clarity/utils/car_noise_simulator/carnoise_signal_generator.py
+++ b/clarity/utils/car_noise_simulator/carnoise_signal_generator.py
@@ -45,7 +45,8 @@ class CarNoiseSignalGenerator:
         >>> car_noise_parameters = CarNoiseParameters(random_flag=True)
         >>> parameters = car_noise_parameters.gen_parameters(speed_kph=100)
 
-        >>> car_noise = CarNoiseGenerator(sample_rate=44100, duration_secs=5, random_flag=True)
+        >>> car_noise = CarNoiseGenerator(sample_rate=44100, duration_secs=5,
+                random_flag=True)
         >>> car_noise_signal = car_noise.generate_car_noise(parameters, 3, 0.5)
     """
 
@@ -82,13 +83,14 @@ def generate_car_noise(
     ) -> np.ndarray:
         """
         Method that generates the car noise signal.
-        It organizes the parameters and calls the methods to generate the independent parts.
+        It organizes the parameters and calls the methods to generate the independent
+        parts.
 
         Args:
             noise_parameters (dict): Dictionary with the parameters for the noise
                 as generated by the CarNoiseParameters class.
-            number_noise_sources (int): Number of noise sources. First source is the engine noise.
-                Following sources are other noise sources.
+            number_noise_sources (int): Number of noise sources. First source is the
+                engine noise. Following sources are other noise sources.
             commonness_factor (float): Commonness factor
         Returns:
             np.ndarray: Car noise signal
diff --git a/docs/conf.py b/docs/conf.py
index b0e7f6701..27a645b87 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -39,7 +39,7 @@
 # -- Project information -----------------------------------------------------
 
 project = "pyclarity"
-copyright = "2022, pyclarity authors"
+copyright = "2022, pyclarity authors"  # pylint: disable=redefined-builtin
 author = "pyclarity authors"
 
 # The short X.Y version
@@ -123,7 +123,8 @@
 #         {
 #             "rel": "icon",
 #             "sizes": "156x20",
-#             "href": "https://github.com/Mesnage-Org/clarity/actions/workflows/ci-tests.yml/badge.svg",
+#             "href": "https://github.com/Mesnage-Org/clarity/actions/workflows/\
+#                           ci-tests.yml/badge.svg",
 #         },
 #         {
 #             "rel": "icon",
diff --git a/pyproject.toml b/pyproject.toml
index c351283c9..2862389b8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -47,4 +47,4 @@ omit = [
 line-length = 88
 # pyflakes, pycodestyle, isort
 select = ["F", "E", "W", "B", "I001"]
-ignore = ["B905", "E501"]
\ No newline at end of file
+ignore = ["B905"]
\ No newline at end of file
diff --git a/recipes/cad1/task1/baseline/enhance.py b/recipes/cad1/task1/baseline/enhance.py
index d5985a7ea..17a7fd29a 100644
--- a/recipes/cad1/task1/baseline/enhance.py
+++ b/recipes/cad1/task1/baseline/enhance.py
@@ -311,7 +311,8 @@ def enhance(config: DictConfig) -> None:
     for idx, song_listener in enumerate(valid_song_listener_pairs, 1):
         song_name, listener_name = song_listener
         logger.info(
-            f"[{idx:03d}/{num_song_list_pair:03d}] Processing {song_name} for {listener_name}..."
+            f"[{idx:03d}/{num_song_list_pair:03d}] "
+            f"Processing {song_name} for {listener_name}..."
         )
         # Get the listener's audiogram
         listener_info = listener_valid_audiograms[listener_name]
@@ -353,9 +354,9 @@ def enhance(config: DictConfig) -> None:
                 audiogram_right,
             )
 
-        # Baseline applies NALR prescription to each stem
-        # instead of using the listener's audiograms in the decomposition
-        # This stemp can be skipped if the listener's audiograms are used in the decomposition
+        # Baseline applies NALR prescription to each stem instead of using the
+        # listener's audiograms in the decomposition. This stem can be skipped
+        # if the listener's audiograms are used in the decomposition
         processed_stems = process_stems_for_listener(
             stems,
             enhancer,
diff --git a/recipes/cad1/task2/baseline/audio_manager.py b/recipes/cad1/task2/baseline/audio_manager.py
index 8deccd69d..650a2f86c 100644
--- a/recipes/cad1/task2/baseline/audio_manager.py
+++ b/recipes/cad1/task2/baseline/audio_manager.py
@@ -62,7 +62,8 @@ def _save_audio(self, file_name: str, waveform: np.ndarray) -> None:
         n_clipped, waveform = self.clip_audio(waveform)
         if n_clipped > 0:
             logger.warning(
-                f"Writing {self.output_audio_path / file_name}: {n_clipped} samples clipped"
+                f"Writing {self.output_audio_path / file_name}: {n_clipped} "
+                "samples clipped"
             )
 
         waveform = (32768.0 * waveform).astype(np.int16)
@@ -84,7 +85,7 @@ def clip_audio(
             max_val (float): The maximum value to clip to. Defaults to 1.
 
         Returns:
-            Tuple[int, np.ndarray]: The number of samples clipped and the clipped signal.
+            Tuple[int, np.ndarray]: Number of samples clipped and the clipped signal.
         """
         if self.soft_clip:
             signal = np.tanh(signal)
diff --git a/recipes/cad1/task2/baseline/baseline_utils.py b/recipes/cad1/task2/baseline/baseline_utils.py
index 7365ff708..c5b4e293f 100644
--- a/recipes/cad1/task2/baseline/baseline_utils.py
+++ b/recipes/cad1/task2/baseline/baseline_utils.py
@@ -76,11 +76,12 @@ def load_listeners_and_scenes(config: DictConfig) -> Tuple[dict, dict, dict]:
     Args:
         config (DictConfig): A dictionary-like object containing various configuration
             parameters for the evaluation. This includes the path to the scenes file,
-            the path to the listeners train file, and the path to the listeners valid file.
+            the path to the listeners train file, and the path to the listeners valid
+            file.
 
     Returns:
-        Tuple[dict, dict, dict]: A tuple containing the scene data, the listener data and
-            the pair scenes-listeners.
+        Tuple[dict, dict, dict]: A tuple containing the scene data, the listener data
+            and the pair scenes-listeners.
 
     """
     # Load listener data
diff --git a/recipes/cad1/task2/baseline/car_scene_acoustics.py b/recipes/cad1/task2/baseline/car_scene_acoustics.py
index e9690a5b0..fd6f6a3e3 100644
--- a/recipes/cad1/task2/baseline/car_scene_acoustics.py
+++ b/recipes/cad1/task2/baseline/car_scene_acoustics.py
@@ -28,8 +28,8 @@ class CarSceneAcoustics:
     A class for the car acoustic environment.
 
     Constants:
-        ANECHOIC_HRTF_FOR_NOISE (dict): A dictionary containing the names of the anechoic BRIRs
-            for the following directions:
+        ANECHOIC_HRTF_FOR_NOISE (dict): A dictionary containing the names of the
+            anechoic BRIRs for the following directions:
                 0 degrees: front
                     - 000_left: The left channel of the BRIR for 0 degrees.
                     - 000_right: The right channel of the BRIR for 0 degrees.
@@ -182,12 +182,13 @@ def add_hrtf_to_stereo_signal(
         Args:
             signal (np.ndarray): a numpy array of shape (2, n_samples) containing the
                 stereo audio signal.
-            hrir: a dictionary containing the HRIR (head-related impulse response) filenames.
+            hrir: a dictionary containing the HRIR (head-related impulse response)
+                filenames.
             hrtf_type: the type of HRTF to use. Can be either "Anechoic" or "Car".
 
         Returns:
-            A numpy array of shape (2, n_samples) containing the stereo audio signal with the
-                BRIR added.
+            A numpy array of shape (2, n_samples) containing the stereo audio signal
+                with the BRIR added.
 
         """
         car_hrtf_path = Path(self.hrtf_dir) / hrtf_type / "audio"
@@ -315,9 +316,9 @@ def apply_car_acoustics_to_signal(
             enh_signal (np.ndarray): The enhanced signal to apply the car acoustics to.
             scene (dict): The scene dictionary with the acoustics parameters.
             listener (dict): The listener dictionary with the audiograms.
-            hrtf (dict): A dictionary containing the head-related transfer functions (HRTFs)
-                for the listener being evaluated. This includes the left and right HRTFs for
-                the car and the anechoic room.
+            hrtf (dict): A dictionary containing the head-related transfer functions
+                (HRTFs) for the listener being evaluated. This includes the left and
+                right HRTFs for the car and the anechoic room.
             audio_manager (AudioManager): The audio manager object.
             config (DictConfig): The config object.
 
@@ -360,7 +361,8 @@ def apply_car_acoustics_to_signal(
             )
 
         # 4. Add the scaled anechoic car noise to the enhanced signal
-        # processed_signal = (enh_signal * car HRTF) + (car_noise * Anechoic HRTF) * scale_factor
+        # processed_signal = (enh_signal * car HRTF)
+        #   + (car_noise * Anechoic HRTF) * scale_factor
         processed_signal = self.add_two_signals(processed_signal, car_noise_anechoic)
 
         if config.evaluate.save_intermediate_wavs:
@@ -390,7 +392,8 @@ def apply_car_acoustics_to_signal(
 
         if n_clipped > 0:
             logger.warning(
-                f"Scene {scene['scene']}: {n_clipped} samples clipped in evaluation signal."
+                f"Scene {scene['scene']}: {n_clipped}"
+                " samples clipped in evaluation signal."
             )
 
         audio_manager.add_audios_to_save("ha_processed_signal", processed_signal)
diff --git a/recipes/cad1/task2/baseline/evaluate.py b/recipes/cad1/task2/baseline/evaluate.py
index 83bbd5e00..bca2f3c55 100644
--- a/recipes/cad1/task2/baseline/evaluate.py
+++ b/recipes/cad1/task2/baseline/evaluate.py
@@ -127,23 +127,25 @@ def evaluate_scene(
             containing the enhanced signal.
         sample_rate (int): The sampling frequency of the reference and enhanced signals.
         scene_id (str): A string identifier for the scene being evaluated.
-        current_scene (dict): A dictionary containing information about the scene being evaluated,
-            including the song ID, the listener ID, the car noise type, and the split.
-        listener_audiogram (dict): A dictionary containing the listener's audiogram data,
-            including the center frequencies (cfs) and the hearing levels for both ears
-            (audiogram_levels_l and audiogram_levels_r).
-        car_scene_acoustic (CarSceneAcoustics): An instance of the CarSceneAcoustics class,
-            which is used to generate car noise and add binaural room impulse responses (BRIRs)
-            to the enhanced signal.
+        current_scene (dict): A dictionary containing information about the scene being
+            evaluated, including the song ID, the listener ID, the car noise type, and
+            the split.
+        listener_audiogram (dict): A dictionary containing the listener's audiogram
+            data, including the center frequencies (cfs) and the hearing levels for both
+            ears (audiogram_levels_l and audiogram_levels_r).
+        car_scene_acoustic (CarSceneAcoustics): An instance of the CarSceneAcoustics
+            class, which is used to generate car noise and add binaural room impulse
+            responses (BRIRs) to the enhanced signal.
         hrtf (dict): A dictionary containing the head-related transfer functions (HRTFs)
             for the listener being evaluated. This includes the left and right HRTFs for
             the car and the anechoic room.
         config (DictConfig): A dictionary-like object containing various configuration
-            parameters for the evaluation. This includes the path to the enhanced signal folder,
-            the path to the music directory, and a flag indicating whether to set a random seed.
+            parameters for the evaluation. This includes the path to the enhanced signal
+            folder,the path to the music directory, and a flag indicating whether to set
+            a random seed.
 
     Returns:
-        Tuple[float, float]: A tuple containing the HAAQI scores for the left and right ears.
+        Tuple[float, float]: A tuple containing HAAQI scores for left and right ears.
 
     """
     audio_manager = AudioManager(
diff --git a/recipes/cad1/task2/data_preparation/build_scene_metadata.py b/recipes/cad1/task2/data_preparation/build_scene_metadata.py
index a9ee80375..3e7db24cc 100644
--- a/recipes/cad1/task2/data_preparation/build_scene_metadata.py
+++ b/recipes/cad1/task2/data_preparation/build_scene_metadata.py
@@ -26,7 +26,8 @@ def get_random_head_rotation(input_dict: dict) -> Any:
     Selects a random head rotation degree.
 
     Args:
-        input_dict (dict): A dictionary where keys are bird IDs and values are bird data.
+        input_dict (dict): A dictionary where keys are bird IDs and values are bird
+            data.
 
     Returns:
         A random item from the input dictionary.
@@ -45,7 +46,7 @@ def get_random_car_params(min_speed: int = 50, max_speed: int = 120) -> Dict:
         - max_speed (int): The maximum speed that can be returned (default 120).
 
     Returns:
-        A dictionary containing the parameters needed by the CarNoiseSignalGeneration class.
+        A dictionary containing parameters needed by CarNoiseSignalGeneration class.
     """
     speed = np.random.randint(min_speed, max_speed)
     car_params = CarNoiseParametersGenerator().gen_parameters(speed)
@@ -82,7 +83,7 @@ def get_random_snr(min_snr, max_snr, round_to=4) -> float:
 
 @hydra.main(config_path=".", config_name="config")
 def run(cfg: DictConfig) -> None:
-    """Main function to generate the metadata for the scenes in the CAD-1 Task-2 challenge.
+    """Main function to generate metadata for the scenes in the CAD-1 Task-2 challenge.
 
     This function relies on a random seed to generate the metadata.
     The seed is set to 2023 by default and it should always be present.
diff --git a/recipes/cec1/baseline/enhance.py b/recipes/cec1/baseline/enhance.py
index 9c12ddef1..5ae0e339c 100644
--- a/recipes/cec1/baseline/enhance.py
+++ b/recipes/cec1/baseline/enhance.py
@@ -14,8 +14,10 @@
 def enhance(cfg: DictConfig) -> None:
     enhanced_folder = os.path.join(cfg["path"]["exp_folder"], "enhanced_signals")
     os.makedirs(enhanced_folder, exist_ok=True)
-    scenes_listeners = json.load(open(cfg["path"]["scenes_listeners_file"]))
-    listener_audiograms = json.load(open(cfg["path"]["listeners_file"]))
+    with open(cfg["path"]["scenes_listeners_file"], "r", encoding="utf-8") as fp:
+        scenes_listeners = json.load(fp)
+    with open(cfg["path"]["listeners_file"], "r", encoding="utf-8") as fp:
+        listener_audiograms = json.load(fp)
 
     enhancer = GHAHearingAid(**cfg["GHAHearingAid"])
 
diff --git a/recipes/cec1/baseline/evaluate.py b/recipes/cec1/baseline/evaluate.py
index 93bca7acc..2394fe522 100644
--- a/recipes/cec1/baseline/evaluate.py
+++ b/recipes/cec1/baseline/evaluate.py
@@ -41,8 +41,10 @@ def listen(ear, signal, audiogram_l, audiogram_r):
 def run_HL_processing(cfg: DictConfig) -> None:
     output_path = os.path.join(cfg["path"]["exp_folder"], "eval_signals")
     os.makedirs(output_path, exist_ok=True)
-    scenes_listeners = json.load(open(cfg["path"]["scenes_listeners_file"]))
-    listener_audiograms = json.load(open(cfg["path"]["listeners_file"]))
+    with open(cfg["path"]["scenes_listeners_file"], "r", encoding="utf-8") as fp:
+        scenes_listeners = json.load(fp)
+    with open(cfg["path"]["listeners_file"], "r", encoding="utf-8") as fp:
+        listener_audiograms = json.load(fp)
     enhanced_folder = cfg["path"]["enhanced_signals"]
 
     # initialize ear
@@ -69,7 +71,7 @@ def run_HL_processing(cfg: DictConfig) -> None:
                 f"{outfile_stem}_HL-mixoutput.wav",
             ]
             # if all signals to write exist, pass
-            if all([os.path.isfile(f) for f in signal_files_to_write]):
+            if all(os.path.isfile(f) for f in signal_files_to_write):
                 continue
 
             signal = read_signal(signal_file)
@@ -102,10 +104,11 @@ def run_HL_processing(cfg: DictConfig) -> None:
                 listen(ear, ddf_signal, left_audiogram, right_audiogram),
                 listen(ear, mixture_signal, left_audiogram, right_audiogram),
             ]
-            for i in range(len(signals_to_write)):
+
+            for signal, signal_file in zip(signals_to_write, signal_files_to_write):
                 write_signal(
-                    signal_files_to_write[i],
-                    signals_to_write[i],
+                    signal_file,
+                    signal,
                     MSBG_FS,
                     floating_point=True,
                 )
@@ -113,7 +116,8 @@ def run_HL_processing(cfg: DictConfig) -> None:
 
 @hydra.main(config_path=".", config_name="config")
 def run_calculate_SI(cfg: DictConfig) -> None:
-    scenes_listeners = json.load(open(cfg["path"]["scenes_listeners_file"]))
+    with open(cfg["path"]["scenes_listeners_file"], "r", encoding="utf-8") as fp:
+        scenes_listeners = json.load(fp)
     proc_folder = os.path.join(cfg["path"]["exp_folder"], "eval_signals")
     sii_file = os.path.join(cfg["path"]["exp_folder"], "sii.csv")
     csv_lines = [["scene", "listener", "sii"]]
@@ -133,7 +137,8 @@ def run_calculate_SI(cfg: DictConfig) -> None:
                 os.path.join(proc_folder, f"{scene}_{listener}_HLddf-output.wav")
             )
 
-            # Calculate channel-specific unit impulse delay due to HL model and audiograms
+            # Calculate channel-specific unit impulse delay due to HL model
+            # and audiograms
             delay = find_delay_impulse(ddf, initial_value=int(MSBG_FS / 2))
             if delay[0] != delay[1]:
                 logging.info(f"Difference in delay of {delay[0] - delay[1]}.")
@@ -164,7 +169,7 @@ def run_calculate_SI(cfg: DictConfig) -> None:
             )
             csv_lines.append([scene, listener, sii])  # type: ignore
 
-    with open(sii_file, "w") as csv_f:
+    with open(sii_file, "w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
diff --git a/recipes/cec1/data_preparation/prepare_cec1_data.py b/recipes/cec1/data_preparation/prepare_cec1_data.py
index 0a146bfee..3fe6522bf 100644
--- a/recipes/cec1/data_preparation/prepare_cec1_data.py
+++ b/recipes/cec1/data_preparation/prepare_cec1_data.py
@@ -18,7 +18,7 @@ def prepare_data(root_path, metafile_path, scene_folder, num_channels):
         scene_folder: folder containing generated scenes
         num_channels: number of channels
     """
-    with open(metafile_path, "r") as f:
+    with open(metafile_path, "r", encoding="utf-8") as f:
         scenes = json.load(f)
 
     os.makedirs(scene_folder, exist_ok=True)
diff --git a/recipes/cec1/e009_sheffield/train.py b/recipes/cec1/e009_sheffield/train.py
index 1a6007069..8734ff4da 100644
--- a/recipes/cec1/e009_sheffield/train.py
+++ b/recipes/cec1/e009_sheffield/train.py
@@ -22,7 +22,14 @@
 
 
 class DenModule(System):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.ear_idx = None
+        self.down_sample = None
+
     def common_step(self, batch, batch_nb, train=True):
+        if self.down_sample is None:
+            raise RuntimeError("Hearing model not loaded")
         proc, ref = batch
         ref = ref[:, self.ear_idx, :]
         if self.config.downsample_factor != 1:
@@ -34,7 +41,24 @@ def common_step(self, batch, batch_nb, train=True):
 
 
 class AmpModule(System):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.hl_ear = None
+        self.nh_ear = None
+        self.down_sample = None
+        self.up_sample = None
+        self.ear_idx = None
+        self.den_model = None
+
     def common_step(self, batch, batch_nb, train=True):
+        if (
+            self.hl_ear is None
+            or self.nh_ear is None
+            or self.down_sample is None
+            or self.up_sample is None
+            or self.den_model is None
+        ):
+            raise RuntimeError("Hearing model not loaded")
         proc, ref = batch
         ref = ref[:, self.ear_idx, :]
         if self.config.downsample_factor != 1:
@@ -107,8 +131,8 @@ def train_den(cfg, ear):
     trainer.fit(den_module)
 
     best_k = {k: v.item() for k, v in checkpoint.best_k_models.items()}
-    with open(os.path.join(exp_dir, "best_k_models.json"), "w") as f:
-        json.dump(best_k, f, indent=0)
+    with open(os.path.join(exp_dir, "best_k_models.json"), "w", encoding="utf-8") as fp:
+        json.dump(best_k, fp, indent=0)
     state_dict = torch.load(checkpoint.best_model_path)
     den_module.load_state_dict(state_dict=state_dict["state_dict"])
     den_module.cpu()
@@ -163,8 +187,8 @@ def train_amp(cfg, ear):
         )
 
     # build normal hearing and hearing loss ears
-    with open(cfg.listener.metafile, "r") as f:
-        listeners_file = json.load(f)
+    with open(cfg.listener.metafile, "r", encoding="utf-8") as fp:
+        listeners_file = json.load(fp)
         audiogram_cfs = listeners_file[cfg.listener.id]["audiogram_cfs"]
         audiogram_lvl_l = listeners_file[cfg.listener.id]["audiogram_levels_l"]
         audiogram_lvl_r = listeners_file[cfg.listener.id]["audiogram_levels_r"]
@@ -199,8 +223,8 @@ def train_amp(cfg, ear):
     trainer.fit(amp_module)
 
     best_k = {k: v.item() for k, v in checkpoint.best_k_models.items()}
-    with open(os.path.join(exp_dir, "best_k_models.json"), "w") as f:
-        json.dump(best_k, f, indent=0)
+    with open(os.path.join(exp_dir, "best_k_models.json"), "w", encoding="utf-8") as fp:
+        json.dump(best_k, fp, indent=0)
     state_dict = torch.load(checkpoint.best_model_path)
     amp_module.load_state_dict(state_dict=state_dict["state_dict"])
     amp_module.cpu()
diff --git a/recipes/cec2/baseline/data_generation/render_additional_scenes.py b/recipes/cec2/baseline/data_generation/render_additional_scenes.py
index fa404d37a..b409141fa 100644
--- a/recipes/cec2/baseline/data_generation/render_additional_scenes.py
+++ b/recipes/cec2/baseline/data_generation/render_additional_scenes.py
@@ -12,8 +12,9 @@
 def render_scenes(cfg):
     for dataset in cfg.scene_renderer:
         logger.info(f"Beginning scene generation for {dataset} set...")
-        with open(cfg.scene_renderer[dataset].metadata.scene_definitions, "r") as f:
-            scenes = json.load(f)
+        file_path = cfg.scene_renderer[dataset].metadata.scene_definitions
+        with open(file_path, "r", encoding="utf-8") as fp:
+            scenes = json.load(fp)
 
         starting_scene = (
             cfg.scene_renderer[dataset].chunk_size * cfg.render_starting_chunk
diff --git a/recipes/cec2/baseline/enhance.py b/recipes/cec2/baseline/enhance.py
index 10a0841c2..36da45bfc 100644
--- a/recipes/cec2/baseline/enhance.py
+++ b/recipes/cec2/baseline/enhance.py
@@ -18,8 +18,10 @@
 def enhance(cfg: DictConfig) -> None:
     enhanced_folder = os.path.join(cfg.path.exp_folder, "enhanced_signals")
     os.makedirs(enhanced_folder, exist_ok=True)
-    scenes_listeners = json.load(open(cfg.path.scenes_listeners_file))
-    listener_audiograms = json.load(open(cfg.path.listeners_file))
+    with open(cfg.path.scenes_listeners_file, "r", encoding="utf-8") as fp:
+        scenes_listeners = json.load(fp)
+    with open(cfg.path.listeners_file, "r", encoding="utf-8") as fp:
+        listener_audiograms = json.load(fp)
 
     enhancer = NALR(**cfg.nalr)
     compressor = Compressor(**cfg.compressor)
diff --git a/recipes/cec2/baseline/evaluate.py b/recipes/cec2/baseline/evaluate.py
index e71d3387e..e22a9df7b 100644
--- a/recipes/cec2/baseline/evaluate.py
+++ b/recipes/cec2/baseline/evaluate.py
@@ -17,7 +17,7 @@
 
 def read_csv_scores(file):
     score_dict = {}
-    with open(file, "r") as f:
+    with open(file, "r", encoding="utf-8") as f:
         reader = csv.reader(f)
         _ = next(reader)
         for row in reader:
@@ -27,8 +27,10 @@ def read_csv_scores(file):
 
 @hydra.main(config_path=".", config_name="config")
 def run_calculate_SI(cfg: DictConfig) -> None:
-    scenes_listeners = json.load(open(cfg.path.scenes_listeners_file))
-    listener_audiograms = json.load(open(cfg.path.listeners_file))
+    with open(cfg.path.scenes_listeners_file, "r", encoding="utf-8") as fp:
+        scenes_listeners = json.load(fp)
+    with open(cfg.path.listeners_file, "r", encoding="utf-8") as fp:
+        listener_audiograms = json.load(fp)
     os.makedirs(cfg.path.exp_folder, exist_ok=True)
 
     enhanced_folder = os.path.join(cfg.path.exp_folder, "enhanced_signals")
@@ -40,9 +42,9 @@ def run_calculate_SI(cfg: DictConfig) -> None:
             score_dict = read_csv_scores(unproc_si_file)
             ave_score = np.mean(list(score_dict.values()))
             logger.info(
-                "si_unproc.csv exists, and the average HASPI scores for unprocessed scenes is {:.4f}".format(
-                    ave_score
-                )
+                "si_unproc.csv exists, and the average HASPI scores for unprocessed "
+                "scenes is %.4f",
+                ave_score,
             )
 
     si_file = os.path.join(cfg.path.exp_folder, "si.csv")
@@ -50,9 +52,7 @@ def run_calculate_SI(cfg: DictConfig) -> None:
     if os.path.exists(si_file):
         score_dict = read_csv_scores(si_file)
         ave_score = np.mean(list(score_dict.values()))
-        logger.info(
-            "si.csv exists, and the average HASPI scores is {:.4f}".format(ave_score)
-        )
+        logger.info("si.csv exists, and the average HASPI scores is %4f", ave_score)
         return
 
     for scene in tqdm(scenes_listeners):
@@ -116,7 +116,7 @@ def run_calculate_SI(cfg: DictConfig) -> None:
                     ) % (10**8)
                     np.random.seed(scene_md5)
 
-                fs_unproc, unproc = wavfile.read(
+                _fs_unproc, unproc = wavfile.read(
                     os.path.join(cfg.path.scenes_folder, f"{scene}_mix_CH1.wav")
                 )
                 unproc = unproc / 32768.0
@@ -133,7 +133,7 @@ def run_calculate_SI(cfg: DictConfig) -> None:
                 logger.info(f"The unprocessed signal HASPI score is {si_unproc}")
                 unproc_csv_lines.append([scene, listener, str(si_unproc)])
 
-    with open(si_file, "w") as csv_f:
+    with open(si_file, "w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
@@ -141,12 +141,10 @@ def run_calculate_SI(cfg: DictConfig) -> None:
             csv_writer.writerow(line)
     score_dict = read_csv_scores(si_file)
     ave_score = np.mean(list(score_dict.values()))
-    logger.info(
-        "si.csv exists, and the average HASPI scores is {:.4f}".format(ave_score)
-    )
+    logger.info("si.csv exists, and the average HASPI scores is %.4f", ave_score)
 
     if cfg.evaluate.cal_unprocessed_si:
-        with open(unproc_si_file, "w") as csv_f:
+        with open(unproc_si_file, "w", encoding="utf-8") as csv_f:
             csv_writer = csv.writer(
                 csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
             )
@@ -155,9 +153,9 @@ def run_calculate_SI(cfg: DictConfig) -> None:
         score_dict = read_csv_scores(unproc_si_file)
         ave_score = np.mean(list(score_dict.values()))
         logger.info(
-            "si_unproc.csv exists, and the average HASPI scores for unprocessed scenes is {:.4f}".format(
-                ave_score
-            )
+            "si_unproc.csv exists, and the average HASPI scores "
+            "for unprocessed scenes is %.4f",
+            ave_score,
         )
 
 
diff --git a/recipes/cec2/data_preparation/render_scenes.py b/recipes/cec2/data_preparation/render_scenes.py
index ce559ccec..b213a5401 100644
--- a/recipes/cec2/data_preparation/render_scenes.py
+++ b/recipes/cec2/data_preparation/render_scenes.py
@@ -12,8 +12,12 @@
 def render_scenes(cfg):
     for dataset in cfg.scene_renderer:
         logger.info(f"Beginning scene generation for {dataset} set...")
-        with open(cfg.scene_renderer[dataset].metadata.scene_definitions, "r") as f:
-            scenes = json.load(f)
+        with open(
+            cfg.scene_renderer[dataset].metadata.scene_definitions,
+            "r",
+            encoding="utf-8",
+        ) as fp:
+            scenes = json.load(fp)
 
         starting_scene = (
             cfg.scene_renderer[dataset].chunk_size * cfg.render_starting_chunk
diff --git a/recipes/cpc1/baseline/compute_scores.py b/recipes/cpc1/baseline/compute_scores.py
index 61225eb92..8bf76b630 100644
--- a/recipes/cpc1/baseline/compute_scores.py
+++ b/recipes/cpc1/baseline/compute_scores.py
@@ -49,7 +49,7 @@ def _logistic_mapping(self, x, x0, k):
     def fit(self, pred, intel):
         """Fit a mapping betweeen mbstoi scores and intelligibility scores."""
         initial_guess = [0.5, 1.0]  # Initial guess for parameter values
-        self.params, pcov = curve_fit(
+        self.params, *_remaining_returns = curve_fit(
             self._logistic_mapping, pred, intel, initial_guess
         )
 
@@ -113,13 +113,13 @@ def run(cfg: DictConfig) -> None:
     fit_pred = model.predict(data_eval["prediction"].to_numpy())
     open_set_scores = compute_scores(fit_pred, data_eval["label"].to_numpy())
 
-    with open("results.json", "w") as f:
+    with open("results.json", "w", encoding="utf-8") as fp:
         json.dump(
             {
                 "closed_set scores:": closed_set_scores,
                 "open_set scores:": open_set_scores,
             },
-            f,
+            fp,
         )
 
 
diff --git a/recipes/cpc1/baseline/run.py b/recipes/cpc1/baseline/run.py
index 149431f22..1ff44edb2 100644
--- a/recipes/cpc1/baseline/run.py
+++ b/recipes/cpc1/baseline/run.py
@@ -42,8 +42,10 @@ def listen(ear, signal, audiogram_l, audiogram_r):
 def run_HL_processing(cfg, path):
     output_path = os.path.join(path.exp_folder, "eval_signals")
     os.makedirs(output_path, exist_ok=True)
-    scenes = json.load(open(path.scenes_file))
-    listener_audiograms = json.load(open(path.listeners_file))
+    with open(path.scenes_file, "r", encoding="utf-8") as fp:
+        scenes = json.load(fp)
+    with open(path.listeners_file, "r", encoding="utf-8") as fp:
+        listener_audiograms = json.load(fp)
     enhanced_folder = path.scenes_folder
 
     # initialize ear
@@ -64,7 +66,7 @@ def run_HL_processing(cfg, path):
             f"{outfile_stem}_HL-mixoutput.wav",
         ]
         # if all signals to write exist, pass
-        if all([os.path.isfile(f) for f in signal_files_to_write]):
+        if all(os.path.isfile(f) for f in signal_files_to_write):
             continue
 
         signal = read_signal(signal_file)
@@ -93,17 +95,18 @@ def run_HL_processing(cfg, path):
             listen(ear, ddf_signal, left_audiogram, right_audiogram),
             listen(ear, mixture_signal, left_audiogram, right_audiogram),
         ]
-        for i in range(len(signals_to_write)):
+        for signal, signal_file in zip(signals_to_write, signal_files_to_write):
             write_signal(
-                signal_files_to_write[i],
-                signals_to_write[i],
+                signal_file,
+                signal,
                 MSBG_FS,
                 floating_point=True,
             )
 
 
 def run_calculate_SI(cfg, path) -> None:
-    scenes = json.load(open(path.scenes_file))
+    with open(path.scenes_file, "r", encoding="utf-8") as fp:
+        scenes = json.load(fp)
     proc_folder = os.path.join(path.exp_folder, "eval_signals")
     sii_file = os.path.join(path.exp_folder, "sii.csv")
     csv_lines = [["signal_ID", "intelligibility_score"]]
@@ -153,7 +156,7 @@ def run_calculate_SI(cfg, path) -> None:
         )
         csv_lines.append([f"{scene}_{listener}_{system}", sii])  # type: ignore
 
-    with open(sii_file, "w") as csv_f:
+    with open(sii_file, "w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
diff --git a/recipes/cpc1/e029_sheffield/evaluate.py b/recipes/cpc1/e029_sheffield/evaluate.py
index 54d6457b3..abb3323c7 100644
--- a/recipes/cpc1/e029_sheffield/evaluate.py
+++ b/recipes/cpc1/e029_sheffield/evaluate.py
@@ -30,8 +30,8 @@ def std_err(x, y):
 class Model:
     """Class to represent the mapping from mbstoi parameters to intelligibility scores.
     The mapping uses a simple logistic function scaled between 0 and 100.
-    The mapping parameters need to fit first using mbstoi, intelligibility score pairs, using fit().
-    Once the fit has been made predictions can be made by calling predict()
+    The mapping parameters need to fit first using mbstoi, intelligibility score pairs,
+    using fit(). Once the fit has been made predictions can be made by calling predict()
     """
 
     params = None  # The model params
@@ -48,7 +48,7 @@ def _logistic_mapping(self, x, x0, k):
     def fit(self, pred, intel):
         """Fit a mapping betweeen mbstoi scores and intelligibility scores."""
         initial_guess = [0.5, 1.0]  # Initial guess for parameter values
-        self.params, pcov = curve_fit(
+        self.params, *_remaining_returns = curve_fit(
             self._logistic_mapping, pred, intel, initial_guess
         )
 
@@ -74,11 +74,13 @@ def read_data(pred_json, label_json):
 
     # read label_json to dict
     label_dict = {}
-    label_json = json.load(open(label_json))
+    with open(label_json, "r", encoding="utf-8") as fp:
+        label_json = json.load(fp)
     for item in label_json:
         label_dict[item["signal"]] = item["correctness"] / 100.0
 
-    pred_dict = json.load(open(pred_json))
+    with open(pred_json, "r", encoding="utf-8") as fp:
+        pred_dict = json.load(fp)
     for signal, pred in pred_dict.items():
         prediction.append(pred * 100.0)
         label.append(label_dict[signal])
@@ -127,13 +129,15 @@ def run(cfg: DictConfig) -> None:
     fit_pred = model.predict(prediction_test)
     negent_scores = compute_scores(fit_pred * 100, label_test * 100)
 
-    with open(os.path.join(cfg.path.exp_folder, "results.json"), "w") as f:
+    with open(
+        os.path.join(cfg.path.exp_folder, "results.json"), "w", encoding="utf-8"
+    ) as fp:
         json.dump(
             {
                 "confidence_results": conf_scores,
                 "negative_entropy_results": negent_scores,
             },
-            f,
+            fp,
         )
 
 
diff --git a/recipes/cpc1/e029_sheffield/infer.py b/recipes/cpc1/e029_sheffield/infer.py
index a051221c4..5832be503 100644
--- a/recipes/cpc1/e029_sheffield/infer.py
+++ b/recipes/cpc1/e029_sheffield/infer.py
@@ -18,8 +18,19 @@
 
 
 class ASR(sb.core.Brain):
+    # pylint: disable=abstract-method
+    # Note, no implementation of compute_forward() or compute_objectives() provided
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.asr_ensemble = None
+        self.test_search = None
+
     def compute_uncertainty(self, wavs, wav_lens, tokens_bos):
-        """Forward computations from the waveform batches to the output probabilities."""
+        """Forward computations from waveform batches to the output probabilities."""
+        if self.asr_ensemble is None or self.test_search is None:
+            raise RuntimeError("ASR model not loaded")
+
         # batch = batch.to(self.device)
         wavs, wav_lens, tokens_bos = (
             wavs.to(self.device),
@@ -83,8 +94,8 @@ def init_evaluation(self, max_key=None, min_key=None):
 
 def init_asr(asr_config):
     hparams_file, run_opts, overrides = sb.parse_arguments([asr_config])
-    with open(hparams_file) as fin:
-        hparams = load_hyperpyyaml(fin, overrides)
+    with open(hparams_file, "r", encoding="utf-8") as fp:
+        hparams = load_hyperpyyaml(fp, overrides)
 
     tokenizer = hparams["tokenizer"]
     bos_index = hparams["bos_index"]
@@ -106,7 +117,7 @@ def init_asr(asr_config):
     return asr_brain, tokenizer, bos_index
 
 
-def compute_uncertainty(left_proc_path, asr_model, bos_index, tokenizer):
+def compute_uncertainty(left_proc_path, asr_model, bos_index, _tokenizer):
     wav_len = torch.tensor([1], dtype=torch.float32)
     tokens_bos = torch.LongTensor([bos_index]).view(1, -1)
 
@@ -158,10 +169,14 @@ def run(cfg: DictConfig) -> None:
         dev_conf[wav_id] = uncertainty[0].tolist()
         dev_negent[wav_id] = uncertainty[1].tolist()
 
-        with open(os.path.join(cfg.path.exp_folder, "dev_conf.json"), "w") as f:
-            json.dump(dev_conf, f)
-        with open(os.path.join(cfg.path.exp_folder, "dev_negent.json"), "w") as f:
-            json.dump(dev_negent, f)
+        with open(
+            os.path.join(cfg.path.exp_folder, "dev_conf.json"), "w", encoding="utf-8"
+        ) as fp:
+            json.dump(dev_conf, fp)
+        with open(
+            os.path.join(cfg.path.exp_folder, "dev_negent.json"), "w", encoding="utf-8"
+        ) as fp:
+            json.dump(dev_negent, fp)
 
     # test set similarity
     test_conf = {}
@@ -174,10 +189,15 @@ def run(cfg: DictConfig) -> None:
         test_conf[wav_id] = uncertainty[0].tolist()
         test_negent[wav_id] = uncertainty[1].tolist()
 
-        with open(os.path.join(cfg.path.exp_folder, "test_conf.json"), "w") as f:
-            json.dump(test_conf, f)
-        with open(os.path.join(cfg.path.exp_folder, "test_negent.json"), "w") as f:
-            json.dump(test_negent, f)
+        with open(
+            os.path.join(cfg.path.exp_folder, "test_conf.json"), "w", encoding="utf-8"
+        ) as fp:
+            json.dump(test_conf, fp)
+
+        with open(
+            os.path.join(cfg.path.exp_folder, "test_negent.json"), "w", encoding="utf-8"
+        ) as fp:
+            json.dump(test_negent, fp)
 
 
 # pylint: disable=no-value-for-parameter
diff --git a/recipes/cpc1/e029_sheffield/prepare_data.py b/recipes/cpc1/e029_sheffield/prepare_data.py
index b083bf3b9..34e5db155 100644
--- a/recipes/cpc1/e029_sheffield/prepare_data.py
+++ b/recipes/cpc1/e029_sheffield/prepare_data.py
@@ -33,15 +33,13 @@ def run_data_split(cfg, track):
         logger.info("Train set and dev set lists exist...")
         return
 
-    scenes_dict = json.load(
-        open(
-            os.path.join(
-                cfg.path.root,
-                "clarity_CPC1_data_train/metadata",
-                f"CPC1.{'train'+track}.json",
-            )
-        )
+    file_path = os.path.join(
+        cfg.path.root,
+        "clarity_CPC1_data_train/metadata",
+        f"CPC1.{'train'+track}.json",
     )
+    with open(file_path, "r", encoding="utf-8") as fp:
+        scenes_dict = json.load(fp)
     scene_list = []
     for item in scenes_dict:
         scene_list.append(item["scene"])
@@ -51,10 +49,10 @@ def run_data_split(cfg, track):
     )
     scene_train_list = list(set(scene_list) - set(scene_dev_list))
 
-    with open(scene_train_json, "w") as f:
-        json.dump(scene_train_list, f)
-    with open(scene_dev_json, "w") as f:
-        json.dump(scene_dev_list, f)
+    with open(scene_train_json, "w", encoding="utf-8") as fp:
+        json.dump(scene_train_list, fp)
+    with open(scene_dev_json, "w", encoding="utf-8") as fp:
+        json.dump(scene_dev_list, fp)
 
 
 def listen(ear, signal, audiogram_l, audiogram_r):
@@ -83,23 +81,21 @@ def run_msbg_simulation(cfg, track):
         dataset = split + track
         dataset_folder = os.path.join(cfg.path.root, "clarity_CPC1_data_" + split)
         output_path = os.path.join(dataset_folder, "clarity_data/HA_outputs", dataset)
-        scenes = json.load(
-            open(os.path.join(dataset_folder, "metadata", f"CPC1.{dataset}.json"))
-        )
+        file_path = os.path.join(dataset_folder, "metadata", f"CPC1.{dataset}.json")
+        with open(file_path, "r", encoding="utf-8") as fp:
+            scenes = json.load(fp)
         if split == "train":
-            listener_audiograms = json.load(
-                open(
-                    os.path.join(
-                        dataset_folder, "metadata", "listeners.CPC1_train.json"
-                    )
-                )
+            file_path = os.path.join(
+                dataset_folder, "metadata", "listeners.CPC1_train.json"
             )
+            with open(file_path, "r", encoding="utf-8") as fp:
+                listener_audiograms = json.load(fp)
         else:
-            listener_audiograms = json.load(
-                open(
-                    os.path.join(dataset_folder, "metadata", "listeners.CPC1_all.json")
-                )
+            file_path = os.path.join(
+                dataset_folder, "metadata", "listeners.CPC1_all.json"
             )
+            with open(file_path, "r", encoding="utf-8") as fp:
+                listener_audiograms = json.load(fp)
 
         # initialize ear
         ear = Ear(**cfg["MSBGEar"])
@@ -116,7 +112,7 @@ def run_msbg_simulation(cfg, track):
                 f"{outfile_stem}_HL-output.wav",
             ]
             # if all signals to write exist, pass
-            if all([os.path.isfile(f) for f in signal_files_to_write]):
+            if all(os.path.isfile(f) for f in signal_files_to_write):
                 continue
             signal = read_signal(signal_file)
 
@@ -134,10 +130,11 @@ def run_msbg_simulation(cfg, track):
             signals_to_write = [
                 listen(ear, signal, left_audiogram, right_audiogram),
             ]
-            for i in range(len(signals_to_write)):
+
+            for signal, signal_file in zip(signals_to_write, signal_files_to_write):
                 write_signal(
-                    signal_files_to_write[i],
-                    signals_to_write[i],
+                    signal_file,
+                    signal,
                     MSBG_FS,
                     floating_point=True,
                 )
@@ -152,8 +149,8 @@ def generate_data_split(
     if_msbg=False,
     if_ref=False,
 ):
-    with open(orig_data_json, "r") as f:
-        all_data_list = json.load(f)
+    with open(orig_data_json, "r", encoding="utf-8") as fp:
+        all_data_list = json.load(fp)
 
     left_tgt_signal_folder = os.path.join(
         target_data_folder, orig_signal_folder.split("/")[-2] + "_left"
@@ -232,21 +229,21 @@ def generate_data_split(
                 ["right_" + snt_id, str(duration), wav_file_right, spk_id, wrds]
             )
 
-    with open(left_csvfile, mode="w") as csv_f:
+    with open(left_csvfile, mode="w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
         for line in csv_lines_left:
             csv_writer.writerow(line)
 
-    with open(right_csvfile, mode="w") as csv_f:
+    with open(right_csvfile, mode="w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
         for line in csv_lines_right:
             csv_writer.writerow(line)
 
-    with open(binaural_csvfile, mode="w") as csv_f:
+    with open(binaural_csvfile, mode="w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
@@ -270,17 +267,16 @@ def run_signal_generation_train(cfg, track):
     ]
 
     datasets_to_generate = []
-    for i in range(len(lists_to_generate)):
-        with open(lists_to_generate[i], "r") as f:
-            datasets_to_generate.append(json.load(f))
-            f.close()
+    for list_to_generate in lists_to_generate:
+        with open(list_to_generate, "r", encoding="utf-8") as fp:
+            datasets_to_generate.append(json.load(fp))
 
-    for i in range(len(lists_to_generate)):
+    for list_to_generate, dataset in zip(lists_to_generate, datasets_to_generate):
         # generate_data_split(
         #     train_json_path,
         #     train_signal_folder,
         #     target_folder,
-        #     os.path.basename(lists_to_generate[i]).split("_")[1],
+        #     os.path.basename(list_to_generate).split("_")[1],
         #     datasets_to_generate[i],
         # )
 
@@ -288,8 +284,8 @@ def run_signal_generation_train(cfg, track):
             train_json_path,
             train_signal_folder,
             target_folder,
-            os.path.basename(lists_to_generate[i]).split("_")[1],
-            datasets_to_generate[i],
+            os.path.basename(list_to_generate).split("_")[1],
+            dataset,
             if_msbg=True,
         )
 
@@ -297,8 +293,8 @@ def run_signal_generation_train(cfg, track):
             train_json_path,
             train_signal_folder,
             target_folder,
-            os.path.basename(lists_to_generate[i]).split("_")[1],
-            datasets_to_generate[i],
+            os.path.basename(list_to_generate).split("_")[1],
+            dataset,
             if_ref=True,
         )
 
diff --git a/recipes/cpc1/e029_sheffield/train_asr.py b/recipes/cpc1/e029_sheffield/train_asr.py
index c001ee163..0f99a2e8d 100644
--- a/recipes/cpc1/e029_sheffield/train_asr.py
+++ b/recipes/cpc1/e029_sheffield/train_asr.py
@@ -1,10 +1,12 @@
 #!/usr/bin/env python3
 """
-Recipe for training a Transformer ASR system with librispeech, from the SpeechBrain LibriSpeech/ASR recipe.
-The SpeechBrain version used in this work is:
+Recipe for training a Transformer ASR system with librispeech, from the SpeechBrain
+LibriSpeech/ASR recipe. The SpeechBrain version used in this work is:
 https://github.com/speechbrain/speechbrain/tree/1eddf66eea01866d3cf9dfe61b00bb48d2062236
 """
 
+# pylint: disable=W0201  # code requires attributes defined outside __init__
+
 import logging
 import os
 import sys
@@ -17,11 +19,13 @@
 
 logger = logging.getLogger(__name__)
 
+tokenizer = None
+
 
 # Define training procedure
 class ASR(sb.core.Brain):
     def compute_forward(self, batch, stage):
-        """Forward computations from the waveform batches to the output probabilities."""
+        """Forward computations from waveform batches to the output probabilities."""
         batch = batch.to(self.device)
         wavs, wav_lens = batch.sig
         tokens_bos, _ = batch.tokens_bos
@@ -57,8 +61,8 @@ def compute_forward(self, batch, stage):
             hyps = None
             current_epoch = self.hparams.epoch_counter.current
             if current_epoch % self.hparams.valid_search_interval == 0:
-                # for the sake of efficiency, we only perform beamsearch with limited capacity
-                # and no LM to give user some idea of how the AM is doing
+                # for the sake of efficiency, we only perform beamsearch with limited
+                # capacity and no LM to give user some idea of how the AM is doing
                 hyps, _ = self.hparams.valid_search(enc_out.detach(), wav_lens)
         elif stage == sb.Stage.TEST:
             hyps, _ = self.hparams.test_search(enc_out.detach(), wav_lens)
@@ -127,13 +131,13 @@ def evaluate_batch(self, batch, stage):
             loss = self.compute_objectives(predictions, batch, stage=stage)
         return loss.detach()
 
-    def on_stage_start(self, stage, epoch):
+    def on_stage_start(self, stage, epoch=None):
         """Gets called at the beginning of each epoch"""
         if stage != sb.Stage.TRAIN:
             self.acc_metric = self.hparams.acc_computer()
             self.wer_metric = self.hparams.error_rate_computer()
 
-    def on_stage_end(self, stage, stage_loss, epoch):
+    def on_stage_end(self, stage, stage_loss, epoch=None):
         """Gets called at the end of a epoch."""
         # Compute/store important stats
         stage_stats = {"loss": stage_loss}
@@ -181,8 +185,8 @@ def on_stage_end(self, stage, stage_loss, epoch):
                 stats_meta={"Epoch loaded": self.hparams.epoch_counter.current},
                 test_stats=stage_stats,
             )
-            with open(self.hparams.wer_file, "w") as w:
-                self.wer_metric.write_stats(w)
+            with open(self.hparams.wer_file, "w", encoding="utf-8") as fp:
+                self.wer_metric.write_stats(fp)
 
             # save the averaged checkpoint at the end of the evaluation stage
             # delete the rest of the intermediate checkpoints
@@ -223,7 +227,8 @@ def on_fit_start(self):
 
             # Load latest checkpoint to resume training if interrupted
             if self.checkpointer is not None:
-                # do not reload the weights if training is interrupted right before stage 2
+                # do not reload the weights if training is interrupted right before
+                # stage 2
                 group = current_optimizer.param_groups[0]
                 if "momentum" not in group:
                     return
@@ -270,7 +275,8 @@ def dataio_prepare(hparams):
 
     # We get the tokenizer as we need it to encode the labels when creating
     # mini-batches.
-    tokenizer = hparams["tokenizer"]
+    # (Note, tokenizer is also defined in global space. TODO: fix the design)
+    tokenizer = hparams["tokenizer"]  # pylint: disable=redefined-outer-name
 
     # 2. Define audio pipeline:
     @sb.utils.data_pipeline.takes("wav")
@@ -306,11 +312,11 @@ def text_pipeline(wrd):
     return train_data, valid_data, test_datasets, tokenizer
 
 
-if __name__ == "__main__":
+def main():
     # CLI:
     hparams_file, run_opts, overrides = sb.parse_arguments(sys.argv[1:])
-    with open(hparams_file) as fin:
-        hparams = load_hyperpyyaml(fin, overrides)
+    with open(hparams_file, "r", encoding="utf-8") as fp:
+        hparams = load_hyperpyyaml(fp, overrides)
 
     # If distributed_launch=True then
     # create ddp_group with the right communication protocol
@@ -324,7 +330,7 @@ def text_pipeline(wrd):
     )
 
     # here we create the datasets objects as well as tokenization and encoding
-    train_data, valid_data, test_datasets, tokenizer = dataio_prepare(hparams)
+    train_data, valid_data, test_datasets, _tokenizer = dataio_prepare(hparams)
 
     # We download the pretrained LM from HuggingFace (or elsewhere depending on
     # the path given in the YAML file). The tokenizer is loaded at the same time.
@@ -353,12 +359,16 @@ def text_pipeline(wrd):
     )
 
     # Testing
-    for k in test_datasets.keys():  # keys are test_clean, test_other etc
+    for dataset_key, test_dataset in test_datasets.items():
         asr_brain.hparams.wer_file = os.path.join(
-            hparams["output_folder"], "wer_{}.txt".format(k)
+            hparams["output_folder"], f"wer_{dataset_key}.txt"
         )
         asr_brain.evaluate(
-            test_datasets[k],
+            test_dataset,
             max_key="ACC",
             test_loader_kwargs=hparams["test_dataloader_opts"],
         )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/recipes/cpc1/e029_sheffield/transformer_cpc1_ensemble_decoder.py b/recipes/cpc1/e029_sheffield/transformer_cpc1_ensemble_decoder.py
index 9f1d8288d..001235453 100644
--- a/recipes/cpc1/e029_sheffield/transformer_cpc1_ensemble_decoder.py
+++ b/recipes/cpc1/e029_sheffield/transformer_cpc1_ensemble_decoder.py
@@ -40,7 +40,7 @@ class S2SBaseSearcher(torch.nn.Module):
     """
 
     def __init__(self, bos_index, eos_index, min_decode_ratio, max_decode_ratio):
-        super(S2SBaseSearcher, self).__init__()
+        super().__init__()
         self.bos_index = bos_index
         self.eos_index = eos_index
         self.min_decode_ratio = min_decode_ratio
@@ -155,6 +155,9 @@ class S2SGreedySearcher(S2SBaseSearcher):
     greedy decoding approach. See also S2SBaseSearcher().
     """
 
+    # pylint: disable=W0223
+    # This class is intentionally abstract so disable these warnings
+
     def forward(self, enc_states, wav_len):
         enc_lens = torch.round(enc_states.shape[1] * wav_len).int()
         device = enc_states.device
@@ -221,8 +224,11 @@ class S2SRNNGreedySearcher(S2SGreedySearcher):
     >>> hyps, scores = searcher(enc, wav_len)
     """
 
+    # pylint: disable=W0223
+    # Doesn't implement lm_forward_step() or reset_lm_mem()
+
     def __init__(self, embedding, decoder, linear, **kwargs):
-        super(S2SRNNGreedySearcher, self).__init__(**kwargs)
+        super().__init__(**kwargs)
         self.emb = embedding
         self.dec = decoder
         self.fc = linear
@@ -237,7 +243,8 @@ def reset_mem(self, batch_size, device):
         c = torch.zeros(batch_size, self.dec.attn_dim, device=device)
         return hs, c
 
-    def forward_step(self, inp_tokens, memory, enc_states, enc_lens):
+    def forward_step(self, inp_tokens, memory, enc_states, enc_lens, _index=0):
+        # Note, _index is not used here, but it is defined in base class method
         hs, c = memory
         e = self.emb(inp_tokens)
         dec_out, hs, c, w = self.dec.forward_step(e, hs, c, enc_states, enc_lens)
@@ -278,7 +285,8 @@ class S2SBeamSearcher(S2SBaseSearcher):
     coverage_penalty: float
         The coefficient of coverage penalty (η).
         log P(y|x) + λ log P_LM(y) + γ*len(y) + η*coverage(x,y). (default: 0.0)
-        Reference: https://arxiv.org/pdf/1612.02695.pdf, https://arxiv.org/pdf/1808.10792.pdf
+        Reference: https://arxiv.org/pdf/1612.02695.pdf,
+            https://arxiv.org/pdf/1808.10792.pdf
     lm_weight : float
         The weight of LM when performing beam search (λ).
         log P(y|x) + λ log P_LM(y). (default: 0.0)
@@ -292,8 +300,8 @@ class S2SBeamSearcher(S2SBaseSearcher):
         CTC prefix scoring on "partial" token or "full: token.
     ctc_window_size: int
         Default: 0
-        Compute the ctc scores over the time frames using windowing based on attention peaks.
-        If 0, no windowing applied.
+        Compute the ctc scores over the time frames using windowing based on
+        attention peaks. If 0, no windowing applied.
     using_max_attn_shift: bool
         Whether using the max_attn_shift constraint. (default: False)
     max_attn_shift: int
@@ -331,10 +339,9 @@ def __init__(
         max_attn_shift=60,
         minus_inf=-1e20,
     ):
-        super(S2SBeamSearcher, self).__init__(
-            bos_index, eos_index, min_decode_ratio, max_decode_ratio
-        )
+        super().__init__(bos_index, eos_index, min_decode_ratio, max_decode_ratio)
         self.beam_size = beam_size
+        self.beam_offset = None
         self.topk = topk
         self.return_log_probs = return_log_probs
         self.length_normalization = length_normalization
@@ -367,7 +374,8 @@ def __init__(
         if self.ctc_weight > 0.0:
             if len({self.bos_index, self.eos_index, self.blank_index}) < 3:
                 raise ValueError(
-                    "To perform joint ATT/CTC decoding, set blank, eos and bos to different indexes."
+                    "To perform joint ATT/CTC decoding, set blank, eos and bos to "
+                    " different indexes."
                 )
 
         # ctc already initialized
@@ -382,7 +390,8 @@ def _check_full_beams(self, hyps, beam_size):
         ---------
         hyps : List
             This list contains batch_size number.
-            Each inside list contains a list stores all the hypothesis for this sentence.
+            Each inside list contains a list storing all the hypothesis for
+                this sentence.
         beam_size : int
             The number of beam_size.
 
@@ -392,11 +401,8 @@ def _check_full_beams(self, hyps, beam_size):
             Whether the hyps has been full.
         """
         hyps_len = [len(lst) for lst in hyps]
-        beam_size = [self.beam_size for _ in range(len(hyps_len))]
-        if hyps_len == beam_size:
-            return True
-        else:
-            return False
+        beam_size = [self.beam_size] * len(hyps_len)
+        return hyps_len == beam_size
 
     def _check_attn_shift(self, attn, prev_attn_peak):
         """This method checks whether attention shift is more than attn_shift.
@@ -492,15 +498,17 @@ def _update_hyp_and_scores(
                 hyps_and_scores[batch_id].append((hyp, log_probs, final_scores))
         return is_eos
 
-    def _get_top_score_prediction(self, hyps_and_scores, topk):
-        """This method sorts the scores and return corresponding hypothesis and log probs.
+    # pylint: disable=W0613
+    def _get_top_score_prediction(self, all_hyps_and_scores, topk):
+        """Sort the scores and return corresponding hypothesis and log probs.
 
         Arguments
         ---------
-        hyps_and_scores : list
+        all_hyps_and_scores : list
             To store generated hypotheses and scores.
         topk : int
             Number of hypothesis to return.
+            Deprecated, use self.topk instead.
 
         Returns
         -------
@@ -514,11 +522,11 @@ def _get_top_score_prediction(self, hyps_and_scores, topk):
             The log probabilities of each hypotheses.
         """
         top_hyps, top_log_probs, top_scores, top_lengths = [], [], [], []
-        batch_size = len(hyps_and_scores)
+        batch_size = len(all_hyps_and_scores)
 
         # Collect hypotheses
-        for i in range(len(hyps_and_scores)):
-            hyps, log_probs, scores = zip(*hyps_and_scores[i])
+        for hyps_and_scores in all_hyps_and_scores:
+            hyps, log_probs, scores = zip(*hyps_and_scores)
             top_hyps += hyps
             top_scores += scores
             top_log_probs += log_probs
@@ -542,8 +550,9 @@ def _get_top_score_prediction(self, hyps_and_scores, topk):
 
         return topk_hyps, topk_scores, topk_lengths, topk_log_probs
 
-    # def forward(self, enc_states, wav_len):  # noqa: C901
+    # Extra parameters have been added to overriden function
     def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
+        # pylint: disable=arguments-differ
         all_enc_states = []
         all_enc_lens = []
         all_memory = []
@@ -611,7 +620,7 @@ def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
         # keep only the first to make sure no redundancy.
         sequence_scores.index_fill_(0, self.beam_offset, 0.0)
 
-        # keep the hypothesis that reaches eos and their corresponding score and log_probs.
+        # keep the hypothesis that reaches eos and corresponding score and log_probs.
         hyps_and_scores = [[] for _ in range(batch_size)]
 
         # keep the sequences that still not reaches eos.
@@ -726,7 +735,7 @@ def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
             if self.length_normalization:
                 sequence_scores = sequence_scores * (t + 1)
 
-            # The index of which beam the current top-K output came from in (t-1) timesteps.
+            # Index of which beam the current top-K output came from in (t-1) timesteps.
             predecessors = (
                 torch.div(candidates, vocab_size, rounding_mode="floor")
                 + self.beam_offset.unsqueeze(1).expand_as(candidates)
@@ -743,7 +752,7 @@ def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
                         all_ctc_memory[j], candidates
                     )
 
-            # If using_max_attn_shift, then the previous attn peak has to be permuted too.
+            # If using_max_attn_shift, the previous attn peak has to be permuted too.
             if self.using_max_attn_shift:
                 prev_attn_peak = torch.index_select(
                     prev_attn_peak, dim=0, index=predecessors
@@ -758,9 +767,10 @@ def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
                     # Init coverage
                     self.coverage = cur_attn
 
-                # the attn of transformer is [batch_size*beam_size, current_step, source_len]
+                # the attn of transformer is
+                #   [batch_size*beam_size, current_step, source_len]
                 if len(cur_attn.size()) > 2:
-                    self.converage = torch.sum(cur_attn, dim=1)
+                    self.coverage = torch.sum(cur_attn, dim=1)
                 else:
                     # Update coverage
                     self.coverage = torch.index_select(
@@ -845,8 +855,8 @@ def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
                 # for entropy prediction
                 beam_logprobs = log_probs[j * self.topk : (j + 1) * self.topk]
                 beam_seq_probs = []
-                for m in range(len(beam_logprobs)):
-                    seq_prob = torch.sum(beam_logprobs[m])
+                for beam_logprob in beam_logprobs:
+                    seq_prob = torch.sum(beam_logprob)
                     beam_seq_probs.append(seq_prob)
                 beam_seq_probs = torch.stack(beam_seq_probs, dim=0)
                 beam_seq_probs_softmax = torch.nn.functional.softmax(beam_seq_probs)
@@ -864,8 +874,8 @@ def forward(self, src, tokens_bos, wav_lens, pad_idx):  # noqa: C901
                 uncertainty.append({"confidence": confidence, "entropy": entropy})
 
             return predictions, topk_scores, uncertainty
-        else:
-            return predictions, topk_scores
+
+        return predictions, topk_scores
 
     def ctc_forward_step(self, x, model_idx):
         logits = self.ctc_fc[model_idx](x)
@@ -953,10 +963,13 @@ class S2SRNNBeamSearcher(S2SBeamSearcher):
     >>> hyps, scores = searcher(enc, wav_len)
     """
 
+    # pylint: disable=W0223
+    # Doesn't implement lm_forward_step(), permute_lm_mem() or reset_lm_mem()
+
     def __init__(
         self, embedding, decoder, linear, ctc_linear=None, temperature=1.0, **kwargs
     ):
-        super(S2SRNNBeamSearcher, self).__init__(**kwargs)
+        super().__init__(**kwargs)
         self.emb = embedding
         self.dec = decoder
         self.fc = linear
@@ -973,7 +986,8 @@ def reset_mem(self, batch_size, device):
         c = torch.zeros(batch_size, self.dec.attn_dim, device=device)
         return hs, c
 
-    def forward_step(self, inp_tokens, memory, enc_states, enc_lens):
+    def forward_step(self, inp_tokens, memory, enc_states, enc_lens, _index=0):
+        # Note, _index is not used here, but it is defined in base class method
         with torch.no_grad():
             hs, c = memory
             e = self.emb(inp_tokens)
@@ -1054,7 +1068,7 @@ class S2SRNNBeamSearchLM(S2SRNNBeamSearcher):
     def __init__(
         self, embedding, decoder, linear, language_model, temperature_lm=1.0, **kwargs
     ):
-        super(S2SRNNBeamSearchLM, self).__init__(embedding, decoder, linear, **kwargs)
+        super().__init__(embedding, decoder, linear, **kwargs)
 
         self.lm = language_model
         self.lm.eval()
@@ -1140,9 +1154,7 @@ class S2SRNNBeamSearchTransformerLM(S2SRNNBeamSearcher):
     def __init__(
         self, embedding, decoder, linear, language_model, temperature_lm=1.0, **kwargs
     ):
-        super(S2SRNNBeamSearchTransformerLM, self).__init__(
-            embedding, decoder, linear, **kwargs
-        )
+        super().__init__(embedding, decoder, linear, **kwargs)
 
         self.lm = language_model
         self.lm.eval()
@@ -1198,7 +1210,7 @@ def inflate_tensor(tensor, times, dim):
 
 
 def mask_by_condition(tensor, cond, fill_value):
-    """This function will mask some element in the tensor with fill_value, if condition=False.
+    """Mask some element in the tensor with fill_value, if condition=False.
 
     Arguments
     ---------
@@ -1264,7 +1276,7 @@ class S2STransformerBeamSearch(S2SBeamSearcher):
     """
 
     def __init__(self, modules, temperature=1.0, temperature_lm=1.0, **kwargs):
-        super(S2STransformerBeamSearch, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
         # self.model = modules[0]
         # self.fc = modules[1]
@@ -1295,10 +1307,10 @@ def permute_lm_mem(self, memory, index):
         memory = torch.index_select(memory, dim=0, index=index)
         return memory
 
-    def forward_step(self, inp_tokens, memory, enc_states, enc_lens, model_idx):
+    def forward_step(self, inp_tokens, memory, enc_states, enc_lens, index=0):
         memory = _update_mem(inp_tokens, memory)
-        pred, attn = self.model[model_idx].decode(memory, enc_states)
-        prob_dist = self.softmax(self.fc[model_idx](pred) / self.temperature)
+        pred, attn = self.model[index].decode(memory, enc_states)
+        prob_dist = self.softmax(self.fc[index](pred) / self.temperature)
         return prob_dist[:, -1, :], memory, attn
 
     def lm_forward_step(self, inp_tokens, memory):
diff --git a/recipes/cpc1/e032_sheffield/evaluate.py b/recipes/cpc1/e032_sheffield/evaluate.py
index 35974bfaa..ff66b150e 100644
--- a/recipes/cpc1/e032_sheffield/evaluate.py
+++ b/recipes/cpc1/e032_sheffield/evaluate.py
@@ -30,7 +30,8 @@ def std_err(x, y):
 class Model:
     """Class to represent the mapping from mbstoi parameters to intelligibility scores.
     The mapping uses a simple logistic function scaled between 0 and 100.
-    The mapping parameters need to fit first using mbstoi, intelligibility score pairs, using fit().
+    The mapping parameters need to fit first using mbstoi, intelligibility score
+    pairs, using fit().
     Once the fit has been made predictions can be made by calling predict()
     """
 
@@ -48,7 +49,7 @@ def _logistic_mapping(self, x, x0, k):
     def fit(self, pred, intel):
         """Fit a mapping betweeen mbstoi scores and intelligibility scores."""
         initial_guess = [50.0, 1.0]  # Initial guess for parameter values
-        self.params, pcov = curve_fit(
+        self.params, *_remaining_returns = curve_fit(
             self._logistic_mapping, pred, intel, initial_guess
         )
 
@@ -74,11 +75,13 @@ def read_data(pred_json, label_json):
 
     # read label_json to dict
     label_dict = {}
-    label_json = json.load(open(label_json))
+    with open(label_json, "r", encoding="utf-8") as fp:
+        label_json = json.load(fp)
     for item in label_json:
         label_dict[item["signal"]] = item["correctness"]
 
-    pred_dict = json.load(open(pred_json))
+    with open(pred_json, "r", encoding="utf-8") as fp:
+        pred_dict = json.load(fp)
     for signal, pred in pred_dict.items():
         prediction.append(pred * 100.0)
         label.append(label_dict[signal])
@@ -127,7 +130,9 @@ def run(cfg: DictConfig) -> None:
     fit_pred = model.predict(prediction_test)
     dec_scores = compute_scores(fit_pred, label_test)
 
-    with open(os.path.join(cfg.path.exp_folder, "results.json"), "w") as f:
+    with open(
+        os.path.join(cfg.path.exp_folder, "results.json"), "w", encoding="utf-8"
+    ) as f:
         json.dump({"enc_results": enc_scores, "dec_results": dec_scores}, f)
 
 
diff --git a/recipes/cpc1/e032_sheffield/infer.py b/recipes/cpc1/e032_sheffield/infer.py
index 6a81df1fa..56d0edca8 100644
--- a/recipes/cpc1/e032_sheffield/infer.py
+++ b/recipes/cpc1/e032_sheffield/infer.py
@@ -17,9 +17,19 @@
 
 
 class ASR(sb.core.Brain):
+    # pylint: disable=abstract-method
+    # Note, no implementation of compute_forward() or compute_objectives() provided
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.test_search = None
+
     def generate_feats(self, wavs, wav_lens, tokens_bos):
         """Forward computations from the waveform batches to the output probs."""
         # batch = batch.to(self.device)
+        if self.test_search is None:
+            raise ValueError("test_search is not initialized")
+
         wavs, wav_lens, tokens_bos = (
             wavs.to(self.device),
             wav_lens.to(self.device),
@@ -74,8 +84,8 @@ def init_evaluation(self, max_key=None, min_key=None):
 
 def init_asr(asr_config):
     hparams_file, run_opts, overrides = sb.parse_arguments([asr_config])
-    with open(hparams_file) as fin:
-        hparams = load_hyperpyyaml(fin, overrides)
+    with open(hparams_file, "r", encoding="utf-8") as fp:
+        hparams = load_hyperpyyaml(fp, overrides)
 
     tokenizer = hparams["tokenizer"]
     bos_index = hparams["bos_index"]
@@ -103,9 +113,9 @@ def dtw_similarity(x, y):
     )[1]
 
     x_, y_ = [], []
-    for step in range(len(path)):
-        x_.append(x[:, path[step][0], :])
-        y_.append(y[:, path[step][1], :])
+    for step in path:
+        x_.append(x[:, step[0], :])
+        y_.append(y[:, step[1], :])
     x_ = torch.stack(x_, dim=1)
     y_ = torch.stack(y_, dim=1)
     return torch.nn.functional.cosine_similarity(x_, y_, dim=-1)
@@ -131,7 +141,6 @@ def feat2similarity(
             ),
             dim=-1,
         )[0]
-        return sim
     else:
         max_length = torch.max(
             torch.LongTensor(
@@ -173,7 +182,8 @@ def feat2similarity(
             padded_ref_feats_right, padded_proc_feats_right, dim=-1
         )
         sim = torch.stack([ll_sim, lr_sim, rl_sim, rr_sim], dim=-1).max(dim=-1)[0]
-        return torch.mean(sim, dim=-1)
+        sim = torch.mean(sim, dim=-1)
+    return sim
 
 
 def compute_similarity(left_proc_path, wrd, asr_model, bos_index, tokenizer):
@@ -240,13 +250,17 @@ def run(cfg: DictConfig) -> None:
         dev_dec_similarity[wav_id] = similarity[1].tolist()
 
         with open(
-            os.path.join(cfg.path.exp_folder, "dev_enc_similarity.json"), "w"
-        ) as f:
-            json.dump(dev_enc_similarity, f)
+            os.path.join(cfg.path.exp_folder, "dev_enc_similarity.json"),
+            "w",
+            encoding="utf-8",
+        ) as fp:
+            json.dump(dev_enc_similarity, fp)
         with open(
-            os.path.join(cfg.path.exp_folder, "dev_dec_similarity.json"), "w"
-        ) as f:
-            json.dump(dev_dec_similarity, f)
+            os.path.join(cfg.path.exp_folder, "dev_dec_similarity.json"),
+            "w",
+            encoding="utf-8",
+        ) as fp:
+            json.dump(dev_dec_similarity, fp)
 
     # test set similarity
     test_enc_similarity = {}
@@ -261,13 +275,18 @@ def run(cfg: DictConfig) -> None:
         test_dec_similarity[wav_id] = similarity[1].tolist()
 
         with open(
-            os.path.join(cfg.path.exp_folder, "test_enc_similarity.json"), "w"
-        ) as f:
-            json.dump(test_enc_similarity, f)
+            os.path.join(cfg.path.exp_folder, "test_enc_similarity.json"),
+            "w",
+            encoding="utf-8",
+        ) as fp:
+            json.dump(test_enc_similarity, fp)
+
         with open(
-            os.path.join(cfg.path.exp_folder, "test_dec_similarity.json"), "w"
-        ) as f:
-            json.dump(test_dec_similarity, f)
+            os.path.join(cfg.path.exp_folder, "test_dec_similarity.json"),
+            "w",
+            encoding="utf-8",
+        ) as fp:
+            json.dump(test_dec_similarity, fp)
 
 
 # pylint: disable=no-value-for-parameter
diff --git a/recipes/cpc1/e032_sheffield/prepare_data.py b/recipes/cpc1/e032_sheffield/prepare_data.py
index fdf4410cf..6bf2fabb7 100644
--- a/recipes/cpc1/e032_sheffield/prepare_data.py
+++ b/recipes/cpc1/e032_sheffield/prepare_data.py
@@ -33,15 +33,13 @@ def run_data_split(cfg, track):
         logger.info("Train set and dev set lists exist...")
         return
 
-    scenes_dict = json.load(
-        open(
-            os.path.join(
-                cfg.path.root,
-                "clarity_CPC1_data_train/metadata",
-                f"CPC1.{'train'+track}.json",
-            )
-        )
+    filepath = os.path.join(
+        cfg.path.root,
+        "clarity_CPC1_data_train/metadata",
+        f"CPC1.{'train'+track}.json",
     )
+    with open(filepath, "r", encoding="utf-8") as fp:
+        scenes_dict = json.load(fp)
     scene_list = []
     for item in scenes_dict:
         scene_list.append(item["scene"])
@@ -51,10 +49,10 @@ def run_data_split(cfg, track):
     )
     scene_train_list = list(set(scene_list) - set(scene_dev_list))
 
-    with open(scene_train_json, "w") as f:
-        json.dump(scene_train_list, f)
-    with open(scene_dev_json, "w") as f:
-        json.dump(scene_dev_list, f)
+    with open(scene_train_json, "w", encoding="utf-8") as fp:
+        json.dump(scene_train_list, fp)
+    with open(scene_dev_json, "w", encoding="utf-8") as fp:
+        json.dump(scene_dev_list, fp)
 
 
 def listen(ear, signal, audiogram_l, audiogram_r):
@@ -83,23 +81,21 @@ def run_msbg_simulation(cfg, track):
         dataset = split + track
         dataset_folder = os.path.join(cfg.path.root, "clarity_CPC1_data_" + split)
         output_path = os.path.join(dataset_folder, "clarity_data/HA_outputs", dataset)
-        scenes = json.load(
-            open(os.path.join(dataset_folder, "metadata", f"CPC1.{dataset}.json"))
-        )
+        file_path = os.path.join(dataset_folder, "metadata", f"CPC1.{dataset}.json")
+        with open(file_path, "r", encoding="utf-8") as fp:
+            scenes = json.load(fp)
         if split == "train":
-            listener_audiograms = json.load(
-                open(
-                    os.path.join(
-                        dataset_folder, "metadata", "listeners.CPC1_train.json"
-                    )
-                )
+            file_path = os.path.join(
+                dataset_folder, "metadata", "listeners.CPC1_train.json"
             )
+            with open(file_path, "r", encoding="utf-8") as fp:
+                listener_audiograms = json.load(fp)
         else:
-            listener_audiograms = json.load(
-                open(
-                    os.path.join(dataset_folder, "metadata", "listeners.CPC1_all.json")
-                )
+            file_path = os.path.join(
+                dataset_folder, "metadata", "listeners.CPC1_all.json"
             )
+            with open(file_path, "r", encoding="utf-8") as fp:
+                listener_audiograms = json.load(fp)
 
         # initialize ear
         ear = Ear(**cfg["MSBGEar"])
@@ -116,7 +112,7 @@ def run_msbg_simulation(cfg, track):
                 f"{outfile_stem}_HL-output.wav",
             ]
             # if all signals to write exist, pass
-            if all([os.path.isfile(f) for f in signal_files_to_write]):
+            if all(os.path.isfile(f) for f in signal_files_to_write):
                 continue
             signal = read_signal(signal_file)
 
@@ -134,10 +130,11 @@ def run_msbg_simulation(cfg, track):
             signals_to_write = [
                 listen(ear, signal, left_audiogram, right_audiogram),
             ]
-            for i in range(len(signals_to_write)):
+
+            for signal, signal_file in zip(signals_to_write, signal_files_to_write):
                 write_signal(
-                    signal_files_to_write[i],
-                    signals_to_write[i],
+                    signal_file,
+                    signal,
                     MSBG_FS,
                     floating_point=True,
                 )
@@ -152,8 +149,8 @@ def generate_data_split(
     if_msbg=False,
     if_ref=False,
 ):
-    with open(orig_data_json, "r") as f:
-        all_data_list = json.load(f)
+    with open(orig_data_json, "r", encoding="utf-8") as fp:
+        all_data_list = json.load(fp)
 
     left_tgt_signal_folder = os.path.join(
         target_data_folder, orig_signal_folder.split("/")[-2] + "_left"
@@ -232,21 +229,21 @@ def generate_data_split(
                 ["right_" + snt_id, str(duration), wav_file_right, spk_id, wrds]
             )
 
-    with open(left_csvfile, mode="w") as csv_f:
+    with open(left_csvfile, mode="w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
         for line in csv_lines_left:
             csv_writer.writerow(line)
 
-    with open(right_csvfile, mode="w") as csv_f:
+    with open(right_csvfile, mode="w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
         for line in csv_lines_right:
             csv_writer.writerow(line)
 
-    with open(binaural_csvfile, mode="w") as csv_f:
+    with open(binaural_csvfile, mode="w", encoding="utf-8") as csv_f:
         csv_writer = csv.writer(
             csv_f, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
         )
@@ -270,17 +267,16 @@ def run_signal_generation_train(cfg, track):
     ]
 
     datasets_to_generate = []
-    for i in range(len(lists_to_generate)):
-        with open(lists_to_generate[i], "r") as f:
-            datasets_to_generate.append(json.load(f))
-            f.close()
+    for list_to_generate in lists_to_generate:
+        with open(list_to_generate, "r", encoding="utf-8") as fp:
+            datasets_to_generate.append(json.load(fp))
 
-    for i in range(len(lists_to_generate)):
+    for list_to_generate, dataset in zip(lists_to_generate, datasets_to_generate):
         # generate_data_split(
         #     train_json_path,
         #     train_signal_folder,
         #     target_folder,
-        #     os.path.basename(lists_to_generate[i]).split("_")[1],
+        #     os.path.basename(list_to_generate).split("_")[1],
         #     datasets_to_generate[i],
         # )
 
@@ -288,8 +284,8 @@ def run_signal_generation_train(cfg, track):
             train_json_path,
             train_signal_folder,
             target_folder,
-            os.path.basename(lists_to_generate[i]).split("_")[1],
-            datasets_to_generate[i],
+            os.path.basename(list_to_generate).split("_")[1],
+            dataset,
             if_msbg=True,
         )
 
@@ -297,8 +293,8 @@ def run_signal_generation_train(cfg, track):
             train_json_path,
             train_signal_folder,
             target_folder,
-            os.path.basename(lists_to_generate[i]).split("_")[1],
-            datasets_to_generate[i],
+            os.path.basename(list_to_generate).split("_")[1],
+            dataset,
             if_ref=True,
         )
 
diff --git a/recipes/cpc1/e032_sheffield/train_asr.py b/recipes/cpc1/e032_sheffield/train_asr.py
index 896b02df7..eac380b43 100644
--- a/recipes/cpc1/e032_sheffield/train_asr.py
+++ b/recipes/cpc1/e032_sheffield/train_asr.py
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 """
-Recipe for training a Transformer ASR system with librispeech, from the SpeechBrain LibriSpeech/ASR recipe.
-The SpeechBrain version used in this work is:
+Recipe for training a Transformer ASR system with librispeech, from the SpeechBrain
+LibriSpeech/ASR recipe. The SpeechBrain version used in this work is:
 https://github.com/speechbrain/speechbrain/tree/1eddf66eea01866d3cf9dfe61b00bb48d2062236
 """
 
@@ -17,11 +17,22 @@
 
 logger = logging.getLogger(__name__)
 
+tokenizer = None
+
 
 # Define training procedure
 class ASR(sb.core.Brain):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.acc_metric = None
+        self.wer_metric = None
+        self.train_stats = None
+        self.switched = None
+        self.optimizer = None
+        self.tokenizer = None
+
     def compute_forward(self, batch, stage):
-        """Forward computations from the waveform batches to the output probabilities."""
+        """Forward computations from waveform batches to output probabilities."""
         batch = batch.to(self.device)
         wavs, wav_lens = batch.sig
         tokens_bos, _ = batch.tokens_bos
@@ -57,8 +68,8 @@ def compute_forward(self, batch, stage):
             hyps = None
             current_epoch = self.hparams.epoch_counter.current
             if current_epoch % self.hparams.valid_search_interval == 0:
-                # for the sake of efficiency, we only perform beamsearch with limited capacity
-                # and no LM to give user some idea of how the AM is doing
+                # for the sake of efficiency, we only perform beamsearch with limited
+                # capacity and no LM to give user some idea of how the AM is doing
                 hyps, _ = self.hparams.valid_search(enc_out.detach(), wav_lens)
         elif stage == sb.Stage.TEST:
             hyps, _ = self.hparams.test_search(enc_out.detach(), wav_lens)
@@ -67,7 +78,8 @@ def compute_forward(self, batch, stage):
 
     def compute_objectives(self, predictions, batch, stage):
         """Computes the loss (CTC+NLL) given predictions and targets."""
-
+        if self.wer_metric is None or self.acc_metric is None:
+            raise ValueError("wer_metric or acc_metric is None")
         (p_ctc, p_seq, wav_lens, hyps) = predictions
 
         ids = batch.id
@@ -100,6 +112,9 @@ def fit_batch(self, batch):
         """Train the parameters given a single batch in input"""
         # check if we need to switch optimizer
         # if so change the optimizer from Adam to SGD
+        if self.optimizer is None:
+            raise ValueError("optimizer is None")
+
         self.check_and_reset_optimizer()
 
         predictions = self.compute_forward(batch, sb.Stage.TRAIN)
@@ -127,14 +142,16 @@ def evaluate_batch(self, batch, stage):
             loss = self.compute_objectives(predictions, batch, stage=stage)
         return loss.detach()
 
-    def on_stage_start(self, stage, epoch):
+    def on_stage_start(self, stage, epoch=None):
         """Gets called at the beginning of each epoch"""
         if stage != sb.Stage.TRAIN:
             self.acc_metric = self.hparams.acc_computer()
             self.wer_metric = self.hparams.error_rate_computer()
 
-    def on_stage_end(self, stage, stage_loss, epoch):
+    def on_stage_end(self, stage, stage_loss, epoch=None):
         """Gets called at the end of a epoch."""
+        if self.wer_metric is None or self.acc_metric is None:
+            raise ValueError("wer_metric or acc_metric is None")
         # Compute/store important stats
         stage_stats = {"loss": stage_loss}
         if stage == sb.Stage.TRAIN:
@@ -181,8 +198,8 @@ def on_stage_end(self, stage, stage_loss, epoch):
                 stats_meta={"Epoch loaded": self.hparams.epoch_counter.current},
                 test_stats=stage_stats,
             )
-            with open(self.hparams.wer_file, "w") as w:
-                self.wer_metric.write_stats(w)
+            with open(self.hparams.wer_file, "w", encoding="utf-8") as fp:
+                self.wer_metric.write_stats(fp)
 
             # save the averaged checkpoint at the end of the evaluation stage
             # delete the rest of the intermediate checkpoints
@@ -225,7 +242,8 @@ def on_fit_start(self):
 
             # Load latest checkpoint to resume training if interrupted
             if self.checkpointer is not None:
-                # do not reload the weights if training is interrupted right before stage 2
+                # do not reload the weights if training is interrupted right before
+                # stage 2
                 group = current_optimizer.param_groups[0]
                 if "momentum" not in group:
                     return
@@ -272,7 +290,8 @@ def dataio_prepare(hparams):
 
     # We get the tokenizer as we need it to encode the labels when creating
     # mini-batches.
-    tokenizer = hparams["tokenizer"]
+    # (Note, tokenizer is also defined in global space. TODO: fix the design)
+    tokenizer = hparams["tokenizer"]  # pylint: disable=redefined-outer-name
 
     # 2. Define audio pipeline:
     @sb.utils.data_pipeline.takes("wav")
@@ -308,11 +327,11 @@ def text_pipeline(wrd):
     return train_data, valid_data, test_datasets, tokenizer
 
 
-if __name__ == "__main__":
+def main():
     # CLI:
     hparams_file, run_opts, overrides = sb.parse_arguments(sys.argv[1:])
-    with open(hparams_file) as fin:
-        hparams = load_hyperpyyaml(fin, overrides)
+    with open(hparams_file, "r", encoding="utf-8") as fp:
+        hparams = load_hyperpyyaml(fp, overrides)
 
     # If distributed_launch=True then
     # create ddp_group with the right communication protocol
@@ -326,7 +345,7 @@ def text_pipeline(wrd):
     )
 
     # here we create the datasets objects as well as tokenization and encoding
-    train_data, valid_data, test_datasets, tokenizer = dataio_prepare(hparams)
+    train_data, valid_data, test_datasets, _tokenizer = dataio_prepare(hparams)
 
     # We download the pretrained LM from HuggingFace (or elsewhere depending on
     # the path given in the YAML file). The tokenizer is loaded at the same time.
@@ -355,12 +374,17 @@ def text_pipeline(wrd):
     )
 
     # Testing
-    for k in test_datasets.keys():  # keys are test_clean, test_other etc
+    for dataset_key, test_dataset in test_datasets.items():
+        # dataset_keys are test_clean, test_other etc
         asr_brain.hparams.wer_file = os.path.join(
-            hparams["output_folder"], "wer_{}.txt".format(k)
+            hparams["output_folder"], f"wer_{dataset_key}.txt"
         )
         asr_brain.evaluate(
-            test_datasets[k],
+            test_dataset,
             max_key="ACC",
             test_loader_kwargs=hparams["test_dataloader_opts"],
         )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/recipes/cpc1/e032_sheffield/transformer_cpc1_decoder.py b/recipes/cpc1/e032_sheffield/transformer_cpc1_decoder.py
index 0098c0342..1cd80dbae 100644
--- a/recipes/cpc1/e032_sheffield/transformer_cpc1_decoder.py
+++ b/recipes/cpc1/e032_sheffield/transformer_cpc1_decoder.py
@@ -38,7 +38,7 @@ class S2SBaseSearcher(torch.nn.Module):
     """
 
     def __init__(self, bos_index, eos_index, min_decode_ratio, max_decode_ratio):
-        super(S2SBaseSearcher, self).__init__()
+        super().__init__()
         self.bos_index = bos_index
         self.eos_index = eos_index
         self.min_decode_ratio = min_decode_ratio
@@ -151,6 +151,9 @@ class S2SGreedySearcher(S2SBaseSearcher):
     greedy decoding approach. See also S2SBaseSearcher().
     """
 
+    # pylint: disable=W0223
+    # This class is intentionally abstract so disable these warnings
+
     def forward(self, enc_states, wav_len):
         enc_lens = torch.round(enc_states.shape[1] * wav_len).int()
         device = enc_states.device
@@ -217,8 +220,11 @@ class S2SRNNGreedySearcher(S2SGreedySearcher):
     >>> hyps, scores = searcher(enc, wav_len)
     """
 
+    # pylint: disable=W0223
+    # Doesn't implement lm_forward_step() or reset_lm_mem()
+
     def __init__(self, embedding, decoder, linear, **kwargs):
-        super(S2SRNNGreedySearcher, self).__init__(**kwargs)
+        super().__init__(**kwargs)
         self.emb = embedding
         self.dec = decoder
         self.fc = linear
@@ -274,7 +280,8 @@ class S2SBeamSearcher(S2SBaseSearcher):
     coverage_penalty: float
         The coefficient of coverage penalty (η).
         log P(y|x) + λ log P_LM(y) + γ*len(y) + η*coverage(x,y). (default: 0.0)
-        Reference: https://arxiv.org/pdf/1612.02695.pdf, https://arxiv.org/pdf/1808.10792.pdf
+        Reference: https://arxiv.org/pdf/1612.02695.pdf
+            https://arxiv.org/pdf/1808.10792.pdf
     lm_weight : float
         The weight of LM when performing beam search (λ).
         log P(y|x) + λ log P_LM(y). (default: 0.0)
@@ -288,7 +295,8 @@ class S2SBeamSearcher(S2SBaseSearcher):
         CTC prefix scoring on "partial" token or "full: token.
     ctc_window_size: int
         Default: 0
-        Compute the ctc scores over the time frames using windowing based on attention peaks.
+        Compute the ctc scores over the time frames using windowing based on
+            attention peaks.
         If 0, no windowing applied.
     using_max_attn_shift: bool
         Whether using the max_attn_shift constraint. (default: False)
@@ -326,10 +334,9 @@ def __init__(
         max_attn_shift=60,
         minus_inf=-1e20,
     ):
-        super(S2SBeamSearcher, self).__init__(
-            bos_index, eos_index, min_decode_ratio, max_decode_ratio
-        )
+        super().__init__(bos_index, eos_index, min_decode_ratio, max_decode_ratio)
         self.beam_size = beam_size
+        self.beam_offset = None
         self.topk = topk
         self.return_log_probs = return_log_probs
         self.length_normalization = length_normalization
@@ -359,7 +366,8 @@ def __init__(
         if self.ctc_weight > 0.0:
             if len({self.bos_index, self.eos_index, self.blank_index}) < 3:
                 raise ValueError(
-                    "To perform joint ATT/CTC decoding, set blank, eos and bos to different indexes."
+                    "To perform joint ATT/CTC decoding, set blank, eos and bos to "
+                    "different indexes."
                 )
 
         # ctc already initialized
@@ -374,7 +382,8 @@ def _check_full_beams(self, hyps, beam_size):
         ---------
         hyps : List
             This list contains batch_size number.
-            Each inside list contains a list stores all the hypothesis for this sentence.
+            Each inside list contains a list storing all the hypothesis
+                for this sentence.
         beam_size : int
             The number of beam_size.
 
@@ -384,11 +393,8 @@ def _check_full_beams(self, hyps, beam_size):
             Whether the hyps has been full.
         """
         hyps_len = [len(lst) for lst in hyps]
-        beam_size = [self.beam_size for _ in range(len(hyps_len))]
-        if hyps_len == beam_size:
-            return True
-        else:
-            return False
+        beam_size = [self.beam_size] * len(hyps_len)
+        return hyps_len == beam_size
 
     def _check_attn_shift(self, attn, prev_attn_peak):
         """This method checks whether attention shift is more than attn_shift.
@@ -484,15 +490,17 @@ def _update_hyp_and_scores(
                 hyps_and_scores[batch_id].append((hyp, log_probs, final_scores))
         return is_eos
 
-    def _get_top_score_prediction(self, hyps_and_scores, topk):
-        """This method sorts the scores and return corresponding hypothesis and log probs.
+    # pylint: disable=W0613
+    def _get_top_score_prediction(self, all_hyps_and_scores, topk):
+        """Sort the scores and return corresponding hypothesis and log probs.
 
         Arguments
         ---------
-        hyps_and_scores : list
+        all_hyps_and_scores : list
             To store generated hypotheses and scores.
         topk : int
             Number of hypothesis to return.
+            Deprecated, use self.topk instead.
 
         Returns
         -------
@@ -505,12 +513,13 @@ def _get_top_score_prediction(self, hyps_and_scores, topk):
         topk_log_probs : list
             The log probabilities of each hypotheses.
         """
+
         top_hyps, top_log_probs, top_scores, top_lengths = [], [], [], []
-        batch_size = len(hyps_and_scores)
+        batch_size = len(all_hyps_and_scores)
 
         # Collect hypotheses
-        for i in range(len(hyps_and_scores)):
-            hyps, log_probs, scores = zip(*hyps_and_scores[i])
+        for hyps_and_scores in all_hyps_and_scores:
+            hyps, log_probs, scores = zip(*hyps_and_scores)
             top_hyps += hyps
             top_scores += scores
             top_log_probs += log_probs
@@ -579,7 +588,7 @@ def forward(self, enc_states, wav_len):  # noqa: C901
         # keep only the first to make sure no redundancy.
         sequence_scores.index_fill_(0, self.beam_offset, 0.0)
 
-        # keep the hypothesis that reaches eos and their corresponding score and log_probs.
+        # keep the hypothesis that reaches eos and corresponding score and log_probs.
         hyps_and_scores = [[] for _ in range(batch_size)]
 
         # keep the sequences that still not reaches eos.
@@ -673,7 +682,7 @@ def forward(self, enc_states, wav_len):  # noqa: C901
             if self.length_normalization:
                 sequence_scores = sequence_scores * (t + 1)
 
-            # The index of which beam the current top-K output came from in (t-1) timesteps.
+            # The index of beam the current top-K output came from in (t-1) timesteps.
             predecessors = (
                 torch.div(candidates, vocab_size, rounding_mode="floor")
                 + self.beam_offset.unsqueeze(1).expand_as(candidates)
@@ -687,7 +696,7 @@ def forward(self, enc_states, wav_len):  # noqa: C901
             if self.ctc_weight > 0:
                 ctc_memory = ctc_scorer.permute_mem(ctc_memory, candidates)
 
-            # If using_max_attn_shift, then the previous attn peak has to be permuted too.
+            # If using_max_attn_shift, then previous attn peak has to be permuted too.
             if self.using_max_attn_shift:
                 prev_attn_peak = torch.index_select(
                     prev_attn_peak, dim=0, index=predecessors
@@ -702,9 +711,10 @@ def forward(self, enc_states, wav_len):  # noqa: C901
                     # Init coverage
                     self.coverage = cur_attn
 
-                # the attn of transformer is [batch_size*beam_size, current_step, source_len]
+                # the attn of transformer is
+                #   [batch_size*beam_size, current_step, source_len]
                 if len(cur_attn.size()) > 2:
-                    self.converage = torch.sum(cur_attn, dim=1)
+                    self.coverage = torch.sum(cur_attn, dim=1)
                 else:
                     # Update coverage
                     self.coverage = torch.index_select(
@@ -773,7 +783,7 @@ def forward(self, enc_states, wav_len):  # noqa: C901
         (
             topk_hyps,
             topk_scores,
-            topk_lengths,
+            _topk_lengths,
             log_probs,
         ) = self._get_top_score_prediction(hyps_and_scores, topk=self.topk)
         # pick the best hyp
@@ -782,13 +792,13 @@ def forward(self, enc_states, wav_len):  # noqa: C901
 
         if self.return_log_probs:
             return predictions, topk_scores, log_probs
-        else:
-            return (
-                predictions,
-                topk_scores,
-                dec_outputs[0, :, :],
-                torch.exp(log_probs[0]),
-            )
+
+        return (
+            predictions,
+            topk_scores,
+            dec_outputs[0, :, :],
+            torch.exp(log_probs[0]),
+        )
 
     def ctc_forward_step(self, x):
         logits = self.ctc_fc(x)
@@ -876,10 +886,13 @@ class S2SRNNBeamSearcher(S2SBeamSearcher):
     >>> hyps, scores = searcher(enc, wav_len)
     """
 
+    # pylint: disable=W0223
+    # Doesn't implement lm_forward_step(), permute_lm_mem() or reset_lm_mem()
+
     def __init__(
         self, embedding, decoder, linear, ctc_linear=None, temperature=1.0, **kwargs
     ):
-        super(S2SRNNBeamSearcher, self).__init__(**kwargs)
+        super().__init__(**kwargs)
         self.emb = embedding
         self.dec = decoder
         self.fc = linear
@@ -977,7 +990,7 @@ class S2SRNNBeamSearchLM(S2SRNNBeamSearcher):
     def __init__(
         self, embedding, decoder, linear, language_model, temperature_lm=1.0, **kwargs
     ):
-        super(S2SRNNBeamSearchLM, self).__init__(embedding, decoder, linear, **kwargs)
+        super().__init__(embedding, decoder, linear, **kwargs)
 
         self.lm = language_model
         self.lm.eval()
@@ -1063,9 +1076,7 @@ class S2SRNNBeamSearchTransformerLM(S2SRNNBeamSearcher):
     def __init__(
         self, embedding, decoder, linear, language_model, temperature_lm=1.0, **kwargs
     ):
-        super(S2SRNNBeamSearchTransformerLM, self).__init__(
-            embedding, decoder, linear, **kwargs
-        )
+        super().__init__(embedding, decoder, linear, **kwargs)
 
         self.lm = language_model
         self.lm.eval()
@@ -1121,7 +1132,7 @@ def inflate_tensor(tensor, times, dim):
 
 
 def mask_by_condition(tensor, cond, fill_value):
-    """This function will mask some element in the tensor with fill_value, if condition=False.
+    """Mask some element in the tensor with fill_value, if condition=False.
 
     Arguments
     ---------
@@ -1187,7 +1198,7 @@ class S2STransformerBeamSearch(S2SBeamSearcher):
     """
 
     def __init__(self, modules, temperature=1.0, temperature_lm=1.0, **kwargs):
-        super(S2STransformerBeamSearch, self).__init__(**kwargs)
+        super().__init__(**kwargs)
 
         self.model = modules[0]
         self.fc = modules[1]
diff --git a/recipes/cpc2/baseline/predict.py b/recipes/cpc2/baseline/predict.py
index 2e84a368b..5adff89a4 100644
--- a/recipes/cpc2/baseline/predict.py
+++ b/recipes/cpc2/baseline/predict.py
@@ -90,10 +90,10 @@ def predict(cfg: DictConfig):
     for i, _record in records_df.iterrows():
         test_df = records_df.iloc[[i]].copy()
 
-        # The prediction is made using a logistic mapping from HASPI scores to intelligibility
-        # It is important that this mapping is trained using a disjoint set of data
-        # i.e. we define a training data set that does not contain systems, listeners or signals
-        # that appear in the test data sample.
+        # The prediction is made using a logistic mapping from HASPI scores to
+        # intelligibility. It is important that this mapping is trained using a
+        # disjoint set of data, i.e. we define a training data set that does not
+        # contain systems, listeners or signals that appear in the test data sample.
 
         train_df = make_disjoint_train_set(records_df, test_df)
 
diff --git a/recipes/icassp_2023/baseline/enhance.py b/recipes/icassp_2023/baseline/enhance.py
index 697a84060..c8e62d3d5 100644
--- a/recipes/icassp_2023/baseline/enhance.py
+++ b/recipes/icassp_2023/baseline/enhance.py
@@ -23,6 +23,7 @@ def enhance(cfg: DictConfig) -> None:
     with open(cfg.path.scenes_listeners_file, "r", encoding="utf-8") as fp:
         scenes_listeners = json.load(fp)
 
+    # pylint: disable=unused-variable
     with open(cfg.path.listeners_file, "r", encoding="utf-8") as fp:
         listener_audiograms = json.load(fp)  # noqa: F841
 
diff --git a/setup.cfg b/setup.cfg
index a50854830..991e8d987 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -90,6 +90,7 @@ dev =
   pre-commit
   pycodestyle
   pylint
+  pylint-pytest
   pytest
   pytest-cov
   pytest-regtest


Test patch:
diff --git a/tests/data/test_HOA_tools_cec2.py b/tests/data/test_HOA_tools_cec2.py
index 76d66bc1a..e1a5b6983 100644
--- a/tests/data/test_HOA_tools_cec2.py
+++ b/tests/data/test_HOA_tools_cec2.py
@@ -2,18 +2,20 @@
 import numpy as np
 import pytest
 
-from clarity.data.HOA_tools_cec2 import (  # HOARotator,; ambisonic_convolve,; binaural_mixdown,; compute_band_rotation,; compute_rotation_matrix,;  dot,; dot,
+# HOARotator,; ambisonic_convolve,; binaural_mixdown,;;;
+# compute_band_rotation,;; compute_rotation_matrix,;  dot,; dot,
+from clarity.data.HOA_tools_cec2 import (
     P,
     U,
     V,
     W,
     centred_element,
     compute_rms,
+    compute_rotation_vector,
     compute_UVW_coefficients,
     dB_to_gain,
     equalise_rms_levels,
     rotation_control_vector,
-    rotation_vector,
     smoothstep,
 )
 
@@ -39,7 +41,8 @@ def test_centred_element(
 
 
 def test_centred_element_index_error(random_matrix: np.ndarray) -> None:
-    """Test centered_element() raises an IndexError if centering is outside of matrix dimensions."""
+    """Test that centered_element() raises an IndexError if centering is outside
+    of matrix dimensions."""
     with pytest.raises(IndexError):
         centred_element(random_matrix, row=101, col=2)
     with pytest.raises(IndexError):
@@ -58,7 +61,6 @@ def test_P(
     i: int, a: int, b: int, order: int, random_matrix: np.ndarray, expected: float
 ) -> None:
     """Test for P() function."""
-    # FixMe : how long is the list of matrices that is passed in?
     assert (
         P(
             i,
@@ -73,7 +75,6 @@ def test_P(
 
 def test_P_index_error(random_matrix: np.ndarray) -> None:
     """Test P() raises IndexError if invalid indices to r are provided."""
-    # FixMe : how long is the list of matrices that is passed in?
     with pytest.raises(IndexError):
         assert P(i=1, a=5, b=1, order=1, rotation_matrices=[random_matrix])
     with pytest.raises(IndexError):
@@ -158,7 +159,8 @@ def test_W(
         (0, 2, 2, (0.5773502691896257, -0.28867513459481287, -0.0)),
         (1, 2, 2, (0.5, 0.3535533905932738, -0.0)),
         (-1, 2, 3, (1.2649110640673518, 0.7745966692414834, -0.31622776601683794)),
-        # (-1, 2, 1, (-0.0, np.nan, -0.0)),  # FixMe : This is probably going to cause problems how to capture?
+        # (-1, 2, 1, (-0.0, np.nan, -0.0)),
+        # TODO : This is probably going to cause problems how to capture?
     ],
 )
 def test_compute_UVW_coefficients(
@@ -174,24 +176,28 @@ def test_compute_UVW_coefficients_zero_division_error() -> None:
         compute_UVW_coefficients(degree=-1, n=2, order=-2)
 
 
-# FixMe : Not yet working, I don't understand how to get the `output` passed in correctly
+# TODO : Not yet working, I don't understand how to get `output` passed in correctly
 # @pytest.mark.parametrize(
 #     "el, output, expected",
 #     [
 #         (2, np.asarray([0, 0]), np.asarray([[1, 2], [3, 4]])),
 #     ],
 # )
-# def test_compute_band_rotation(el: int, output: np.ndarray, random_matrix: np.ndarray, expected: np.ndarray) -> None:
+# def test_compute_band_rotation(el: int, output: np.ndarray,
+#   random_matrix: np.ndarray, expected: np.ndarray) -> None:
 #     """Test for compute_band_rotation() function."""
-#     np.testing.assert_array_equal(compute_band_rotation(el, [random_matrix, random_matrix], output), expected)
+#     np.testing.assert_array_equal(compute_band_rotation(el,
+# [random_matrix, random_matrix], output), expected)
 
 
-# FixMe : Not working yet, results in a typing error???
+# TODO : Not working yet, results in a typing error???
 # @pytest.mark.parametrize(
 #     "A, B, expected",
 #     [
-#         (np.asarray([[1, 2], [3, 4]]), np.asarray([[4, 3], [2, 1]]), np.asarray([[1, 2], [3, 4]])),
-#         (np.asarray([[5, 6], [7, 8]]), np.asarray([[8, 7], [6, 5]]), np.asarray([[1, 2], [3, 4]])),
+#         (np.asarray([[1, 2], [3, 4]]), np.asarray([[4, 3], [2, 1]]),
+#               np.asarray([[1, 2], [3, 4]])),
+#         (np.asarray([[5, 6], [7, 8]]), np.asarray([[8, 7], [6, 5]]),
+#               np.asarray([[1, 2], [3, 4]])),
 #     ],
 # )
 # def test_dot(A: np.ndarray, B: np.ndarray, expected: np.ndarray) -> None:
@@ -204,7 +210,7 @@ def test_HOARotator() -> None:
     assert True
 
 
-# FixMe : need examples of hrir_metadata dictionary to be able to write a tests for this
+# TODO : need examples of hrir_metadata dictionary to be able to write a tests for this
 def test_binaural_mixdown() -> None:
     """Test for binaural_mixdown() function."""
     assert True
@@ -327,7 +333,8 @@ def test_rotation_control_vector(
 def test_rotation_control_vector_value_error(
     array_length: int, start_idx: int, end_idx: int
 ) -> None:
-    """Test rotation_control_vector() raises ValueError if start_idx > end_idx or end_idx > array_length."""
+    """Test rotation_control_vector() raises ValueError if start_idx > end_idx or
+    end_idx > array_length."""
     with pytest.raises(ValueError):
         rotation_control_vector(array_length, start_idx, end_idx)
 
@@ -363,7 +370,7 @@ def test_rotation_control_vector_value_error(
         ),
     ],
 )
-def test_rotation_vector(
+def test_compute_rotation_vector(
     start_angle: float,
     end_angle: float,
     signal_length: int,
@@ -373,14 +380,17 @@ def test_rotation_vector(
 ) -> None:
     """Test for rotation_vector() function."""
     np.testing.assert_array_equal(
-        rotation_vector(start_angle, end_angle, signal_length, start_idx, end_idx),
+        compute_rotation_vector(
+            start_angle, end_angle, signal_length, start_idx, end_idx
+        ),
         expected,
     )
 
 
-def test_rotation_vector_floating_point_error() -> None:
-    """Test rotation_vector() raises FloatingPointError if signal_length is zero function."""
+def test_compute_rotation_vector_floating_point_error() -> None:
+    """Test rotation_vector() raises FloatingPointError if signal_length is
+    zero function."""
     with pytest.raises(FloatingPointError):
-        rotation_vector(
+        compute_rotation_vector(
             start_angle=10, end_angle=20, signal_length=0, start_idx=2, end_idx=8
         )
diff --git a/tests/data/test_utils.py b/tests/data/test_utils.py
index 1fa629108..54da9597f 100644
--- a/tests/data/test_utils.py
+++ b/tests/data/test_utils.py
@@ -141,5 +141,5 @@ def test_pad(signal: np.ndarray, length: int, expected: np.ndarray) -> None:
 
 def test_pad_invalid_length_assertion_error() -> None:
     """Test pad() raises an exception when length < signal.shape[0]."""
-    with pytest.raises(AssertionError):
+    with pytest.raises(ValueError):
         pad(signal=np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), length=2)
diff --git a/tests/recipes/cad1/task1/test_enhance.py b/tests/recipes/cad1/task1/test_enhance.py
index ef4af5aaf..15008a010 100644
--- a/tests/recipes/cad1/task1/test_enhance.py
+++ b/tests/recipes/cad1/task1/test_enhance.py
@@ -64,6 +64,7 @@ def test_decompose_signal():
         RESOURCES / "test_enhance.test_decompose_signal.npy",
         allow_pickle=True,
     )[()]
+
     for key, item in output.items():
         np.testing.assert_array_almost_equal(item, expected_results[key])
 
@@ -110,7 +111,7 @@ def test_process_stems_for_listener():
         threshold=0.35, attenuation=0.1, attack=50, release=1000, rms_buffer_size=0.064
     )
 
-    # Call the process_stems_for_listener function and check that the output is as expected
+    # Call the process_stems_for_listener function and check output is as expected
     output = process_stems_for_listener(
         stems, enhancer, compressor, audiogram_left, audiogram_right, cfs
     )
@@ -118,6 +119,7 @@ def test_process_stems_for_listener():
         RESOURCES / "test_enhance.test_process_stems_for_listener.npy",
         allow_pickle=True,
     )[()]
+
     for key, item in output.items():
         np.testing.assert_array_almost_equal(item, expected_results[key])
 
diff --git a/tests/recipes/cad1/task1/test_evaluate.py b/tests/recipes/cad1/task1/test_evaluate.py
index f99b510a4..803cf506d 100644
--- a/tests/recipes/cad1/task1/test_evaluate.py
+++ b/tests/recipes/cad1/task1/test_evaluate.py
@@ -116,7 +116,7 @@ def test_make_song_listener_list():
 def test_evaluate_song_listener(
     song, listener, config, split_dir, listener_audiograms, expected_results, tmp_path
 ):
-    """Test the function _evaluate_song_listener returns the correct results given the input"""
+    """Test the function _evaluate_song_listener returns correct results given input"""
     np.random.seed(2023)
 
     # Generate reference and enhanced wav files
diff --git a/tests/regression/test_data_HOA_tools_cec2.py b/tests/regression/test_data_HOA_tools_cec2.py
index 18e5d4022..c9a90f56d 100644
--- a/tests/regression/test_data_HOA_tools_cec2.py
+++ b/tests/regression/test_data_HOA_tools_cec2.py
@@ -85,8 +85,7 @@ def test_compute_band_rotation(regtest):
 
     sub_matrices = [np.eye(i * 2 + 1) for i in np.arange(n + 1)]
     sub_matrices[1] = foa_rotmat
-    typed_sub_matrices = List()
-    [typed_sub_matrices.append(x) for x in sub_matrices]
+    typed_sub_matrices = sub_matrices.copy()
 
     if n > 1:
         for i in np.arange(2, n + 1):
diff --git a/tests/regression/test_enhancers.py b/tests/regression/test_enhancers.py
index ac209788f..ad0071b3e 100644
--- a/tests/regression/test_enhancers.py
+++ b/tests/regression/test_enhancers.py
@@ -4,7 +4,7 @@
 import numpy as np
 import torch
 
-from clarity.enhancer.dsp import filter
+from clarity.enhancer.dsp import filter  # pylint: disable=redefined-builtin
 from clarity.enhancer.gha.audiogram import Audiogram
 from clarity.enhancer.gha.gha_interface import GHAHearingAid as gha
 from clarity.enhancer.gha.gha_utils import format_gaintable, get_gaintable
@@ -41,7 +41,8 @@ def test_gha_audiogram(regtest):
         levels_r = np.round(np.log10(np.random.rand(6) / 20) * (-i), 0)
         ag = Audiogram(levels_l, levels_r, cfs)
         regtest.write(
-            f"Audiogram original: \n{ag.cfs}\n{ag.levels_l}\n{ag.levels_r}\n{ag.severity}\n"
+            "Audiogram original: \n"
+            f"{ag.cfs}\n{ag.levels_l}\n{ag.levels_r}\n{ag.severity}\n"
         )
         ag = ag.select_subset_of_cfs(np.array([500, 1000, 2000]))
         regtest.write(
@@ -55,7 +56,7 @@ def test_GHA_inputs(regtest):
     infile_names = [
         f"tests/test_data/scenes/S06001_mixed_CH{ch}.wav" for ch in range(1, 4)
     ]
-    fd_merged, merged_filename = tempfile.mkstemp(
+    _fd_merged, merged_filename = tempfile.mkstemp(
         prefix="clarity-merged-", suffix=".wav"
     )
     enhancer.create_HA_inputs(infile_names, merged_filename)
@@ -67,7 +68,8 @@ def test_GHA_inputs(regtest):
 
 def test_GHA_config(regtest):
     enhancer = gha(**gha_params)
-    listeners = json.load(open("tests/test_data/metadata/listeners.json"))
+    with open("tests/test_data/metadata/listeners.json", "r", encoding="utf-8") as fp:
+        listeners = json.load(fp)
     listener = listeners["L0001"]
     cfs = np.array(listener["audiogram_cfs"], dtype="int")
     audiogram_left = np.array(listener["audiogram_levels_l"])
diff --git a/tests/regression/test_evaluators_msbg.py b/tests/regression/test_evaluators_msbg.py
index f200685ed..ef106061e 100644
--- a/tests/regression/test_evaluators_msbg.py
+++ b/tests/regression/test_evaluators_msbg.py
@@ -9,9 +9,13 @@
     pad,
 )
 
+# pylint: disable=W0613
+# pylint false positives due to fixtures. pylint-pytest does not seem to work :(
 
-@pytest.fixture
-def use_numpy():
+
+@pytest.fixture(name="use_numpy")
+def fixture_use_numpy():
+    """Set numpy seed and print options for each test"""
     np.random.seed(0)
     np_print_opts = np.get_printoptions()
     np.set_printoptions(precision=5, threshold=1000)
diff --git a/tests/regression/test_mc_conv_tasnet.py b/tests/regression/test_mc_conv_tasnet.py
index 54ef2dec9..5ffa9b9b6 100644
--- a/tests/regression/test_mc_conv_tasnet.py
+++ b/tests/regression/test_mc_conv_tasnet.py
@@ -56,7 +56,7 @@ def test_convtasnet(regtest):
     cfg["test_loader"] = {
         "batch_size": 1,
         "shuffle": False,
-        "num_workers": 0,  # Overhead of multiprocessing not worth it for tiny test dataset
+        "num_workers": 0,  # Overhead of multiprocessing not worth it for tiny dataset
     }
     cfg = OmegaConf.create(cfg)
     device = "cuda" if torch.cuda.is_available() else None
diff --git a/tests/test_compressor.py b/tests/test_compressor.py
index 939091889..1dbff37d7 100644
--- a/tests/test_compressor.py
+++ b/tests/test_compressor.py
@@ -22,7 +22,7 @@ def test_compressor_set_release():
 def test_compressor_process():
     c = Compressor()
     signal = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
-    output, rms, comp_ratios = c.process(signal)
+    output, rms, _comp_ratios = c.process(signal)
 
     assert len(output) == len(signal)
     assert np.all(rms >= 0.0)
diff --git a/tests/test_import.py b/tests/test_import.py
index 54afce011..f0ebf5719 100644
--- a/tests/test_import.py
+++ b/tests/test_import.py
@@ -1,5 +1,7 @@
+# pylint: disable=unused-import
 import clarity.enhancer  # noqa: F401
 
 
 def test_null():
-    assert 1 == 1
+    """Test that should always."""
+    assert True