Repository: cuthbertLab/music21
Pull request #664 (2020-11-15 22:51:53)

Start of a cleanup for ABC parsing
I wanted to add one tiny feature to ABC, but the tokenize method is way too complex as it is.  A start to cleanup methods by making current parsing positions be attributes on the ABCHandler class.

Next step will be to move ABCHandler to two classes:  ABCStringParser and ABCTokenParser.  But will merge in small PRs that make incremental improvements.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/34980821/badge)](https://coveralls.io/builds/34980821)

Coverage increased (+0.008%) to 90.87% when pulling **e0cb1ee3a041fc398cd5fa354f972aa5a38a8125 on abc-cleanup** into **3913500c3fd7bb432cabd6495953d8b1ae4c9484 on master**.


Patch:
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index d4135bb9a..729b82804 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -22,6 +22,7 @@
       <option name="ignoredNames">
         <list>
           <option value="type" />
+          <option value="format" />
         </list>
       </option>
     </inspection_tool>
diff --git a/.pylintrc b/.pylintrc
index c478963ed..1641d26a0 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -97,7 +97,6 @@ disable=
     trailing-newlines,  # is this really the worst thing in the world?
     no-else-continue,  # not so bad...
     no-else-break,  # same...
-    unnecessary-comprehension,  # [x for x in stream] is often useful...
     consider-using-enumerate,  # sometimes parallelism is better than enumerate.
     arguments-differ,  # we are not purists.
     simplifiable-if-statement,  # simpler to a computer maybe...
diff --git a/.travis.yml b/.travis.yml
index 16f495dd5..37878ed73 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,9 +8,9 @@ matrix:
         - python: 3.7
           dist: xenial
           sudo: true
-#        - python: 3.8
-#          dist: xenial
-#          sudo: true
+        - python: 3.8
+          dist: xenial
+          sudo: true
 
 services:
   - xvfb
@@ -29,6 +29,7 @@ before_install:
 install:
     - pip install -r requirements.txt
     - pip install scipy
+    - pip install python-Levenshtein
     - pip install setuptools
     - pip install coverage
     - cd ..
diff --git a/dist/dist.py b/dist/dist.py
index 4cdbd2103..39b45cfa6 100644
--- a/dist/dist.py
+++ b/dist/dist.py
@@ -65,8 +65,8 @@ while it is running.  This takes a while and runs single core, so allocate time.
             pypi
 
         [pypi]
-        username:yourusername
-        password:yourpassword
+        username:your_username
+        password:your_password
 
 16. Delete the two .tar.gz files in dist...
 
@@ -287,8 +287,6 @@ class Distributor:
                           ]:
             environLocal.warn('making %s' % buildType)
 
-            # setup.writeManifestTemplate(self.fpPackageDir)
-            # setup.runDisutils(type)
             savePath = os.getcwd()
             os.chdir(self.fpPackageDir)
             os.system('%s setup.py %s' % (PY, buildType))
diff --git a/documentation/docbuild/documenters.py b/documentation/docbuild/documenters.py
index 779fa547c..854f7806b 100644
--- a/documentation/docbuild/documenters.py
+++ b/documentation/docbuild/documenters.py
@@ -87,7 +87,7 @@ class ObjectDocumenter(Documenter):
 
     # PUBLIC PROPERTIES #
 
-    def referentPackagesystemPath(self):
+    def referentPackageSystemPath(self):
         raise NotImplementedError
 
     def rstAutodocDirectiveFormat(self):
@@ -97,7 +97,7 @@ class ObjectDocumenter(Documenter):
     def rstCrossReferenceString(self):
         return ':{0}:`~{1}`'.format(
             self.sphinxCrossReferenceRole,
-            self.referentPackagesystemPath,
+            self.referentPackageSystemPath,
             )
 
     def sphinxCrossReferenceRole(self):
@@ -131,6 +131,7 @@ class FunctionDocumenter(ObjectDocumenter):
     # INITIALIZER #
 
     def __init__(self, referent=None):
+        # noinspection PyTypeChecker
         if not isinstance(referent, types.FunctionType):
             raise Music21Exception('referent must be a function')
         super().__init__(referent)
@@ -145,17 +146,17 @@ class FunctionDocumenter(ObjectDocumenter):
     def __repr__(self):
         return '<{0}: {1}>'.format(
             self._packageSystemPath,
-            self.referentPackagesystemPath,
+            self.referentPackageSystemPath,
             )
 
     # PUBLIC PROPERTIES #
 
     @property
-    def referentPackagesystemPath(self):
+    def referentPackageSystemPath(self):
         '''
         >>> function = common.opFrac
         >>> documenter = FunctionDocumenter(function)
-        >>> documenter.referentPackagesystemPath
+        >>> documenter.referentPackageSystemPath
         'music21.common.numberTools.opFrac'
         '''
         path = '.'.join((
@@ -173,10 +174,10 @@ class FunctionDocumenter(ObjectDocumenter):
         ['.. autofunction:: music21.common.numberTools.opFrac', '']
         '''
         result = []
-        referentPackagesystemPath = self.referentPackagesystemPath.replace(
+        referentPackageSystemPath = self.referentPackageSystemPath.replace(
             '.__init__', '')
         result.append('.. autofunction:: {0}'.format(
-            referentPackagesystemPath,
+            referentPackageSystemPath,
             ))
         result.append('')
         return result
@@ -232,7 +233,7 @@ class MemberDocumenter(ObjectDocumenter):
     # PUBLIC PROPERTIES #
 
     @property
-    def referentPackagesystemPath(self):
+    def referentPackageSystemPath(self):
         path = '.'.join((
             self.definingClass.__module__,
             self.definingClass.__name__,
@@ -240,8 +241,9 @@ class MemberDocumenter(ObjectDocumenter):
             ))
         return path.replace('.__init__', '')
 
+    @property
     def rstAutodocDirectiveFormat(self):
-        pass
+        return []
 
     def sphinxCrossReferenceRole(self):
         pass
@@ -278,7 +280,7 @@ class MethodDocumenter(MemberDocumenter):
     def rstAutodocDirectiveFormat(self):
         result = []
         result.append('.. automethod:: {0}'.format(
-            self.referentPackagesystemPath,
+            self.referentPackageSystemPath,
             ))
         result.append('')
         return result
@@ -314,7 +316,7 @@ class AttributeDocumenter(MemberDocumenter):
     def rstAutodocDirectiveFormat(self):
         result = []
         result.append('.. autoattribute:: {0}'.format(
-            self.referentPackagesystemPath,
+            self.referentPackageSystemPath,
             ))
         result.append('')
         return result
@@ -486,7 +488,7 @@ class ClassDocumenter(ObjectDocumenter):
             documenterClass = AttributeDocumenter
             localMemberList = self._readonlyProperties
             inheritedMembersMapping = self._inheritedReadonlyPropertiesMapping
-        else:  # do not support writeonlyProperties
+        else:  # do not support write-only properties
             return
 
         documenter = documenterClass(
@@ -641,7 +643,7 @@ class ClassDocumenter(ObjectDocumenter):
         >>> klass = stream.Measure
         >>> documenter = ClassDocumenter(klass)
         >>> mapping = documenter.inheritedDocAttrMapping
-        >>> sortBy = lambda x: x.referentPackagesystemPath
+        >>> sortBy = lambda x: x.referentPackageSystemPath
         >>> for classDocumenter in sorted(mapping, key=sortBy):
         ...     classDocumenter
         ...
@@ -677,13 +679,13 @@ class ClassDocumenter(ObjectDocumenter):
         >>> klass = stream.Measure
         >>> documenter = ClassDocumenter(klass)
         >>> mapping = documenter.inheritedReadonlyPropertiesMapping
-        >>> sortBy = lambda x: x.referentPackagesystemPath
+        >>> sortBy = lambda x: x.referentPackageSystemPath
         >>> for classDocumenter in sorted(mapping, key=sortBy):
         ...     print('{0}:'.format(
-        ...         classDocumenter.referentPackagesystemPath))
+        ...         classDocumenter.referentPackageSystemPath))
         ...     for attributeDocumenter in mapping[classDocumenter][:10]:
         ...         print('- {0}'.format(
-        ...             attributeDocumenter.referentPackagesystemPath))
+        ...             attributeDocumenter.referentPackageSystemPath))
         ...
         music21.base.Music21Object:
         - music21.base.Music21Object.classSet
@@ -717,13 +719,13 @@ class ClassDocumenter(ObjectDocumenter):
         >>> klass = stream.Measure
         >>> documenter = ClassDocumenter(klass)
         >>> mapping = documenter.inheritedMethodsMapping
-        >>> sortBy = lambda x: x.referentPackagesystemPath
+        >>> sortBy = lambda x: x.referentPackageSystemPath
         >>> for classDocumenter in sorted(mapping, key=sortBy):
         ...     print('{0}:'.format(
-        ...         classDocumenter.referentPackagesystemPath))
+        ...         classDocumenter.referentPackageSystemPath))
         ...     for attributeDocumenter in mapping[classDocumenter][:10]:
         ...         print('- {0}'.format(
-        ...             attributeDocumenter.referentPackagesystemPath))
+        ...             attributeDocumenter.referentPackageSystemPath))
         ...
         music21.base.Music21Object:
         - music21.base.Music21Object.containerHierarchy
@@ -771,11 +773,11 @@ class ClassDocumenter(ObjectDocumenter):
         >>> klass = stream.Measure
         >>> documenter = ClassDocumenter(klass)
         >>> mapping = documenter.inheritedReadwritePropertiesMapping
-        >>> sortBy = lambda x: x.referentPackagesystemPath
+        >>> sortBy = lambda x: x.referentPackageSystemPath
         >>> for classDocumenter in sorted(mapping, key=sortBy):
-        ...     print('{0}:'.format(classDocumenter.referentPackagesystemPath))
+        ...     print('{0}:'.format(classDocumenter.referentPackageSystemPath))
         ...     for attributeDocumenter in mapping[classDocumenter][:10]:
-        ...         print('- {0}'.format(attributeDocumenter.referentPackagesystemPath))
+        ...         print('- {0}'.format(attributeDocumenter.referentPackageSystemPath))
         ...
         music21.base.Music21Object:
         - music21.base.Music21Object.activeSite
@@ -893,7 +895,7 @@ class ClassDocumenter(ObjectDocumenter):
         return self._readwriteProperties
 
     @property
-    def referentPackagesystemPath(self):
+    def referentPackageSystemPath(self):
         path = '.'.join((
             self.referent.__module__,
             self.referent.__name__,
@@ -903,10 +905,10 @@ class ClassDocumenter(ObjectDocumenter):
     @property
     def rstAutodocDirectiveFormat(self):
         result = []
-        referentPackagesystemPath = self.referentPackagesystemPath.replace(
+        referentPackageSystemPath = self.referentPackageSystemPath.replace(
             '.__init__', '')
         result.append('.. autoclass:: {0}'.format(
-            referentPackagesystemPath,
+            referentPackageSystemPath,
             ))
         result.append('')
         return result
@@ -947,7 +949,7 @@ class ClassDocumenter(ObjectDocumenter):
         if self.docAttr:
             for attrName, attrDescription in sorted(self.docAttr.items()):
                 path = '{0}.{1}'.format(
-                    self.referentPackagesystemPath.split('.')[-1],
+                    self.referentPackageSystemPath.split('.')[-1],
                     attrName,
                     )
                 directive = '.. attribute:: {0}'.format(path)
@@ -980,7 +982,7 @@ class ClassDocumenter(ObjectDocumenter):
                 formatString = '   - :attr:`~{0}.{1}`'
                 for attrName in attrNames:
                     result.append(formatString.format(
-                        baseDocumenter.referentPackagesystemPath,
+                        baseDocumenter.referentPackageSystemPath,
                         attrName,
                         ))
                 result.append('')
@@ -1117,8 +1119,8 @@ class ClassDocumenter(ObjectDocumenter):
 
         >>> from music21 import scale
         >>> klass = scale.MajorScale
-        >>> documenter = ClassDocumenter(klass)
-        >>> for line in documenter.rstMethodsFormat:
+        >>> classDocumenter = ClassDocumenter(klass)
+        >>> for line in classDocumenter.rstMethodsFormat:
         ...     line
         '.. rubric:: :class:`~music21.scale.MajorScale` methods'
         ''
@@ -1179,8 +1181,8 @@ class ClassDocumenter(ObjectDocumenter):
 
         >>> from music21 import note
         >>> klass = note.Note
-        >>> documenter = ClassDocumenter(klass)
-        >>> for line in documenter.rstReadonlyPropertiesFormat:
+        >>> classDocumenter = ClassDocumenter(klass)
+        >>> for line in classDocumenter.rstReadonlyPropertiesFormat:
         ...     line
         '.. rubric:: :class:`~music21.note.Note` read-only properties'
         ''
@@ -1223,8 +1225,8 @@ class ClassDocumenter(ObjectDocumenter):
 
         >>> from music21 import scale
         >>> klass = scale.MajorScale
-        >>> documenter = ClassDocumenter(klass)
-        >>> for line in documenter.rstReadwritePropertiesFormat:
+        >>> classDocumenter = ClassDocumenter(klass)
+        >>> for line in classDocumenter.rstReadwritePropertiesFormat:
         ...     line
         '.. rubric:: :class:`~music21.scale.MajorScale` read/write properties'
         ''
@@ -1323,9 +1325,9 @@ class ModuleDocumenter(ObjectDocumenter):
     def run(self):
         result = []
         result.extend(self.rstPageReferenceFormat)
-        referentPackagesystemPath = self.referentPackagesystemPath.replace(
+        referentPackageSystemPath = self.referentPackageSystemPath.replace(
             '.__init__', '')
-        result.extend(self.makeHeading(referentPackagesystemPath, 1))
+        result.extend(self.makeHeading(referentPackageSystemPath, 1))
         result.extend(self.rstEditingWarningFormat)
         result.extend(self.rstAutodocDirectiveFormat)
         for classDocumenter in self.classDocumenters:
@@ -1339,7 +1341,7 @@ class ModuleDocumenter(ObjectDocumenter):
     def __repr__(self):
         return '<{0}: {1}>'.format(
             self._packageSystemPath,
-            self.referentPackagesystemPath,
+            self.referentPackageSystemPath,
             )
 
     # PRIVATE METHODS #
@@ -1350,6 +1352,7 @@ class ModuleDocumenter(ObjectDocumenter):
             if name.startswith('_'):
                 continue
             named = getattr(self.referent, name)
+            # noinspection PyTypeChecker
             if isinstance(named, type):
                 if set(inspect.getmro(named)).intersection(
                     self._ignored_classes):
@@ -1375,9 +1378,9 @@ class ModuleDocumenter(ObjectDocumenter):
 
         >>> from music21 import serial
         >>> module = serial
-        >>> documenter = ModuleDocumenter(module)
-        >>> for classDocumenter in documenter.classDocumenters:
-        ...     print(classDocumenter.referentPackagesystemPath)
+        >>> modDocumenter = ModuleDocumenter(module)
+        >>> for classDocumenter in modDocumenter.classDocumenters:
+        ...     print(classDocumenter.referentPackageSystemPath)
         ...
         music21.serial.HistoricalTwelveToneRow
         music21.serial.ToneRow
@@ -1396,7 +1399,7 @@ class ModuleDocumenter(ObjectDocumenter):
                 del(classDocumenters[referent])
         for documenter in sorted(
                 classDocumenters.values(),
-                key=lambda x: x.referentPackagesystemPath):
+                key=lambda x: x.referentPackageSystemPath):
             result.append(documenter)
         return result
 
@@ -1409,9 +1412,9 @@ class ModuleDocumenter(ObjectDocumenter):
 
         >>> from music21 import serial
         >>> module = serial
-        >>> documenter = ModuleDocumenter(module)
-        >>> for functionDocumenter in documenter.functionDocumenters:
-        ...     print(functionDocumenter.referentPackagesystemPath)
+        >>> modDocumenter = ModuleDocumenter(module)
+        >>> for functionDocumenter in modDocumenter.functionDocumenters:
+        ...     print(functionDocumenter.referentPackageSystemPath)
         ...
         music21.serial.getHistoricalRowByName
         music21.serial.pcToToneRow
@@ -1428,7 +1431,7 @@ class ModuleDocumenter(ObjectDocumenter):
                 result.append(functionDocumenters[referent])
                 del(functionDocumenters[referent])
         for documenter in sorted(functionDocumenters.values(),
-            key=lambda x: x.referentPackagesystemPath):
+            key=lambda x: x.referentPackageSystemPath):
             result.append(documenter)
         return result
 
@@ -1441,7 +1444,7 @@ class ModuleDocumenter(ObjectDocumenter):
         return self._memberOrder
 
     @property
-    def referentPackagesystemPath(self):
+    def referentPackageSystemPath(self):
         if isinstance(self.referent.__name__, tuple):
             path = self.referent.__name__[0],
         else:
@@ -1452,7 +1455,7 @@ class ModuleDocumenter(ObjectDocumenter):
     def rstAutodocDirectiveFormat(self):
         result = []
         result.append('.. automodule:: {0}'.format(
-            self.referentPackagesystemPath,
+            self.referentPackageSystemPath,
             ))
         result.append('')
         return result
@@ -1481,9 +1484,9 @@ class ModuleDocumenter(ObjectDocumenter):
         'moduleStreamMakeNotation'
 
         '''
-        referentPackagesystemPath = self.referentPackagesystemPath.replace(
+        referentPackageSystemPath = self.referentPackageSystemPath.replace(
             '.__init__', '')
-        parts = referentPackagesystemPath.split('.')[1:]
+        parts = referentPackageSystemPath.split('.')[1:]
         for i, part in enumerate(parts):
             if not part[0].isupper():
                 parts[i] = part[0].upper() + part[1:]
diff --git a/documentation/docbuild/iterators.py b/documentation/docbuild/iterators.py
index 3889703c2..990c75f51 100644
--- a/documentation/docbuild/iterators.py
+++ b/documentation/docbuild/iterators.py
@@ -246,6 +246,7 @@ class FunctionIterator(Iterator):
 
     def __iter__(self):
         for x in CodebaseIterator(verbose=self.verbose):
+            # noinspection PyTypeChecker
             if isinstance(x, types.FunctionType):
                 yield x
 
diff --git a/documentation/source/developerReference/documenting.rst b/documentation/source/developerReference/documenting.rst
index 65da940b4..5447994b8 100644
--- a/documentation/source/developerReference/documenting.rst
+++ b/documentation/source/developerReference/documenting.rst
@@ -31,7 +31,7 @@ in the future).
 These files get converted to .html when you run `/documentation/make.py` so long as the excellent module
 Sphinx_ has been installed.
 
-.. _Sphinx: http://sphinx.pocoo.org/
+.. _Sphinx: http://www.sphinx-doc.org/
 
 You can also edit these .rst files directly in Jupyter notebook, which will make them show the
 layout as well.
@@ -78,21 +78,26 @@ by Sphinx.   Place all documentation for public modules, module-level
 functions, classes, and class-level attributes, properties, and methods
 as close to the relevant code as possible.
 
-If you're going to edit docs you'll need the latest version of Sphinx.  Go to the command line and Type::
+If you're going to edit docs you'll need the latest version of Sphinx.  Go to the command line and type::
 
     pip3 install sphinx
 
-Sphinx uses special characters to identify formatting of documentation. For example, to write a heading you can write:
-Helpful tips on Sphinx formatting may be found here:  `Sphinx Documentation Formatting <http://sphinx.pocoo.org/rest.html>`_
+Sphinx uses special characters to identify formatting styles in documentation.
+Helpful tips on Sphinx formatting may be found here:  `Sphinx Cheat Sheet <https://matplotlib.org/sampledoc/cheatsheet.html>`_
 
-The code would look like this::
+For example, the heading of this section was created by writing::
+
+    Documenting modules and classes
+    ===============================
+
+To write boldface you would write::
 
     **This is bold**
 
 The documentation looks like this:
 **This is bold**
 
-The code would look like this::
+To italicize, you would write::
 
     *This is italics*
 
@@ -114,7 +119,7 @@ another method's documentation, write::
 The documentation looks like this:
 :meth:`~music21.note.GeneralNote.addLyric`
 
-Or you would like to link to another class, write::
+Or to link to another class, write::
 
     :class:`~music21.note.Note`
 
@@ -175,7 +180,7 @@ test code doesn't get out of sync with each other.  Pretty cool, eh?
 Here's the thing: good programming means that you test as much as possible in
 the code, but good documentation means showing enough example code that the
 readers get it, but not so much that they want to claw out their own eyeballs.
-So how to proceed?  simply add the line "OMIT_FROM_Docs"  in ALL CAPS
+So how to proceed?  Simply add the line "OMIT_FROM_Docs"  in ALL CAPS
 instead of lowercase. (I can't write it in all caps here or nothing else will display!)
 
 Anything after that line in your documentation code won't display.  For instance,
diff --git a/documentation/source/usersGuide/usersGuide_58_sitesContext.ipynb b/documentation/source/usersGuide/usersGuide_58_sitesContext.ipynb
index ba7289e98..ddb7097ff 100644
--- a/documentation/source/usersGuide/usersGuide_58_sitesContext.ipynb
+++ b/documentation/source/usersGuide/usersGuide_58_sitesContext.ipynb
@@ -492,7 +492,7 @@
    "source": [
     "Each element has as its index the memory location of the site and a lightweight wrapper object around the site (i.e., stream) called a :class:`~music21.sites.SiteRef`.\n",
     "\n",
-    "*(all sites except \"None\" are currently \"Stream\" objects -- is was our intention at the beginning to have other types of site contexts, such as interpretative contexts (\"baroque\", \"meantone tuning\") and we might still someday add those, but for now, a site is a Stream)*\n",
+    "*(all sites except \"None\" are currently \"Stream\" objects -- it was our intention at the beginning to have other types of site contexts, such as interpretative contexts (\"baroque\", \"meantone tuning\") and we might still someday add those, but for now, a site is a Stream)*\n",
     "\n",
     "Let's look at the last one:"
    ]
diff --git a/music21/_version.py b/music21/_version.py
index 438f5c961..487f8f37a 100644
--- a/music21/_version.py
+++ b/music21/_version.py
@@ -42,7 +42,7 @@ When changing, update the single test case in base.py.
 Changing this number invalidates old pickles -- do it if the old pickles create a problem.
 '''
 
-__version_info__ = (6, 2, 0)  # can be 4-tuple: (7, 0, 5, 'a2')
+__version_info__ = (6, 3, 0)  # can be 4-tuple: (7, 0, 5, 'a2')
 
 v = '.'.join(str(x) for x in __version_info__[0:3])
 if len(__version_info__) > 3 and __version_info__[3]:
diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py
index 80fbf3722..d045ffc11 100644
--- a/music21/abcFormat/__init__.py
+++ b/music21/abcFormat/__init__.py
@@ -59,7 +59,7 @@ import io
 import pathlib
 import re
 import unittest
-from typing import Union
+from typing import Union, Optional, List, Tuple, Any
 
 from music21 import common
 from music21 import environment
@@ -135,20 +135,18 @@ class ABCToken(prebase.ProtoM21Object):
     adjustments to all tokens, then calls :meth:`~music21.abcFormat.ABCToken.parse` on all tokens.
 
     The source ABC string itself is stored in self.src
-
     '''
-
     def __init__(self, src=''):
-        self.src = src  # store source character sequence
+        self.src: str = src  # store source character sequence
 
     def _reprInternal(self):
         return repr(self.src)
 
-    def stripComment(self, strSrc):
+    @staticmethod
+    def stripComment(strSrc):
         '''
         removes ABC-style comments from a string:
 
-
         >>> ao = abcFormat.ABCToken()
         >>> ao.stripComment('asdf')
         'asdf'
@@ -158,6 +156,13 @@ class ABCToken(prebase.ProtoM21Object):
         'asdf  '
         >>> ao.stripComment('[ceg]% this chord appears 50% more often than other chords do')
         '[ceg]'
+
+        This is a static method, so it can also be called on the class itself:
+
+        >>> abcFormat.ABCToken.stripComment('b1 % a b-flat actually')
+        'b1 '
+
+        Changed: v6.2 -- made a staticmethod
         '''
         if '%' in strSrc:
             return strSrc.split('%')[0]
@@ -181,14 +186,35 @@ class ABCToken(prebase.ProtoM21Object):
 
 
 class ABCMetadata(ABCToken):
+    '''
+    Defines a token of metadata in ABC.
+
+    >>> md = abcFormat.ABCMetadata('I:linebreak')
+    >>> md.src
+    'I:linebreak'
+
+    Has two attributes, `tag` and `data` which are strings or None.
+    Initially both are set to None:
+
+    >>> print(md.tag)
+    None
+
+    After calling `preParse()`, these are separated:
+
+    >>> md.preParse()
+    >>> md.tag
+    'I'
+    >>> md.data
+    'linebreak'
+    '''
     # given a logical unit, create an object
     # may be a chord, notes, metadata, bars
     def __init__(self, src=''):
         super().__init__(src)
-        self.tag = None
-        self.data = None
+        self.tag: Optional[str] = None
+        self.data: Optional[str] = None
 
-    def preParse(self):
+    def preParse(self) -> None:
         '''
         Called before contextual adjustments and needs
         to have access to data.  Divides a token into
@@ -209,16 +235,17 @@ class ABCMetadata(ABCToken):
     def parse(self):
         pass
 
-    def isDefaultNoteLength(self):
-        '''Returns True if the tag is "L", False otherwise.
+    def isDefaultNoteLength(self) -> bool:
+        '''
+        Returns True if the tag is "L", False otherwise.
         '''
         if self.tag == 'L':
             return True
         return False
 
-    def isReferenceNumber(self):
-        '''Returns True if the tag is "X", False otherwise.
-
+    def isReferenceNumber(self) -> bool:
+        '''
+        Returns True if the tag is "X", False otherwise.
 
         >>> x = abcFormat.ABCMetadata('X:5')
         >>> x.preParse()
@@ -231,7 +258,7 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isMeter(self):
+    def isMeter(self) -> bool:
         '''
         Returns True if the tag is "M" for meter, False otherwise.
         '''
@@ -239,7 +266,7 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isTitle(self):
+    def isTitle(self) -> bool:
         '''
         Returns True if the tag is "T" for title, False otherwise.
         '''
@@ -247,7 +274,7 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isComposer(self):
+    def isComposer(self) -> bool:
         '''
         Returns True if the tag is "C" for composer, False otherwise.
         '''
@@ -255,7 +282,7 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isOrigin(self):
+    def isOrigin(self) -> bool:
         '''
         Returns True if the tag is "O" for origin, False otherwise.
         This value is set in the Metadata `localOfComposition` of field.
@@ -264,7 +291,7 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isVoice(self):
+    def isVoice(self) -> bool:
         '''
         Returns True if the tag is "V", False otherwise.
         '''
@@ -272,8 +299,9 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isKey(self):
-        '''Returns True if the tag is "K", False otherwise.
+    def isKey(self) -> bool:
+        '''
+        Returns True if the tag is "K", False otherwise.
         Note that in some cases a Key will encode clef information.
 
         (example from corpus: josquin/laDeplorationDeLaMorteDeJohannesOckeghem.abc)
@@ -282,20 +310,21 @@ class ABCMetadata(ABCToken):
             return True
         return False
 
-    def isTempo(self):
-        '''Returns True if the tag is "Q" for tempo, False otherwise.
+    def isTempo(self) -> bool:
+        '''
+        Returns True if the tag is "Q" for tempo, False otherwise.
         '''
         if self.tag == 'Q':
             return True
         return False
 
     def getTimeSignatureParameters(self):
-        '''If there is a time signature representation available,
+        '''
+        If there is a time signature representation available,
         get a numerator, denominator and an abbreviation symbol.
         To get a music21 :class:`~music21.meter.TimeSignature` object, use
         the :meth:`~music21.abcFormat.ABCMetadata.getTimeSignatureObject` method.
 
-
         >>> am = abcFormat.ABCMetadata('M:2/2')
         >>> am.preParse()
         >>> am.isMeter()
@@ -317,10 +346,9 @@ class ABCMetadata(ABCToken):
         >>> am.preParse()
         >>> am.getTimeSignatureParameters()
         (4, 4, 'normal')
-
         '''
         if not self.isMeter():
-            raise ABCTokenException('no time signature associated with this meta-data')
+            raise ABCTokenException('no time signature associated with this metadata')
 
         if self.data.lower() == 'none':
             return None
@@ -342,18 +370,23 @@ class ABCMetadata(ABCToken):
     def getTimeSignatureObject(self):
         '''
         Return a music21 :class:`~music21.meter.TimeSignature`
-        object for this metadata tag.
-
+        object for this metadata tag, if isMeter is True, otherwise raise exception.
 
         >>> am = abcFormat.ABCMetadata('M:2/2')
         >>> am.preParse()
         >>> ts = am.getTimeSignatureObject()
         >>> ts
         <music21.meter.TimeSignature 2/2>
+
+        >>> am = abcFormat.ABCMetadata('Q:40')
+        >>> am.getTimeSignatureObject()
+        Traceback (most recent call last):
+        music21.abcFormat.ABCTokenException: no time signature associated with
+            this non-metrical metadata.
         '''
         if not self.isMeter():
             raise ABCTokenException(
-                'no time signature associated with this non-metrical meta-data')
+                'no time signature associated with this non-metrical metadata.')
         from music21 import meter
         parameters = self.getTimeSignatureParameters()
         if parameters is None:
@@ -426,7 +459,7 @@ class ABCMetadata(ABCToken):
         from music21 import key
 
         if not self.isKey():
-            raise ABCTokenException('no key signature associated with this meta-data')
+            raise ABCTokenException('no key signature associated with this metadata.')
 
         # abc uses b for flat in key spec only
         keyNameMatch = ['c', 'g', 'd', 'a', 'e', 'b', 'f#', 'g#', 'a#',
@@ -517,7 +550,7 @@ class ABCMetadata(ABCToken):
         <music21.key.Key of g minor>
         '''
         if not self.isKey():
-            raise ABCTokenException('no key signature associated with this meta-data')
+            raise ABCTokenException('no key signature associated with this metadata')
         from music21 import key
         # return values of getKeySignatureParameters are sharps, mode
         # need to unpack list w/ *
@@ -528,7 +561,7 @@ class ABCMetadata(ABCToken):
         else:
             return ks.asKey(mode)
 
-    def getClefObject(self):
+    def getClefObject(self) -> Tuple[Optional['music21.clef.Clef'], Optional[int]]:
         '''
         Extract any clef parameters stored in the key metadata token.
         Assume that a clef definition suggests a transposition.
@@ -536,7 +569,6 @@ class ABCMetadata(ABCToken):
 
         Returns a two-element tuple of clefObj and transposition in semitones
 
-
         >>> am = abcFormat.ABCMetadata('K:Eb Lydian bass')
         >>> am.preParse()
         >>> am.getClefObject()
@@ -544,7 +576,7 @@ class ABCMetadata(ABCToken):
         '''
         if not self.isKey():
             raise ABCTokenException(
-                'no key signature associated with this meta-data; needed for getting Clef Object')
+                'no key signature associated with this metadata; needed for getting Clef Object')
 
         # placing this import in method for now; key.py may import this module
         clefObj = None
@@ -561,11 +593,10 @@ class ABCMetadata(ABCToken):
         # if not defined, returns None, None
         return clefObj, t
 
-    def getMetronomeMarkObject(self):
+    def getMetronomeMarkObject(self) -> Optional['music21.tempo.MetronomeMark']:
         '''
         Extract any tempo parameters stored in a tempo metadata token.
 
-
         >>> am = abcFormat.ABCMetadata('Q: "Allegro" 1/4=120')
         >>> am.preParse()
         >>> am.getMetronomeMarkObject()
@@ -593,7 +624,7 @@ class ABCMetadata(ABCToken):
 
         '''
         if not self.isTempo():
-            raise ABCTokenException('no tempo associated with this meta-data')
+            raise ABCTokenException('no tempo associated with this metadata')
         mmObj = None
         from music21 import tempo
         # see if there is a text expression in quotes
@@ -644,11 +675,10 @@ class ABCMetadata(ABCToken):
         # returns None if not defined
         return mmObj
 
-    def getDefaultQuarterLength(self):
+    def getDefaultQuarterLength(self) -> float:
         r'''
         If there is a quarter length representation available, return it as a floating point value
 
-
         >>> am = abcFormat.ABCMetadata('L:1/2')
         >>> am.preParse()
         >>> am.getDefaultQuarterLength()
@@ -718,7 +748,7 @@ class ABCMetadata(ABCToken):
                 return 0.5  # otherwise it is an eighth note
         else:  # pragma: no cover
             raise ABCTokenException(
-                'no quarter length associated with this meta-data: %s' % self.data)
+                'no quarter length associated with this metadata: %s' % self.data)
 
 
 class ABCBar(ABCToken):
@@ -1274,12 +1304,16 @@ class ABCNote(ABCToken):
             # might remove quotes from chord symbols here
 
             # index of end of last match
-            i = [m for m in reChordSymbol.finditer(strSrc)][-1].end()
+            i = list(reChordSymbol.finditer(strSrc))[-1].end()
             return chordSymbols, strSrc[i:]
         else:
             return [], strSrc
 
-    def getPitchName(self, strSrc, forceKeySignature=None):
+    def getPitchName(
+        self,
+        strSrc: str,
+        forceKeySignature=None
+    ) -> Tuple[Optional[str], Union[bool, None]]:
         '''
         Given a note or rest string without a chord symbol,
         return a music21 pitch string or None (if a rest),
@@ -1309,10 +1343,13 @@ class ABCNote(ABCToken):
         ('G-7', True)
         >>> an.getPitchName('=c')
         ('Cn5', True)
+
+        If pitch is a rest (z) then the Pitch name is None:
+
         >>> an.getPitchName('z4')
         (None, None)
 
-        Grace note
+        Grace note:
 
         >>> an.getPitchName('{c}')
         ('C5', None)
@@ -1325,6 +1362,9 @@ class ABCNote(ABCToken):
         >>> an.getPitchName('c')
         ('C#5', False)
 
+
+        Illegal pitch names raise an ABCHandlerException
+
         >>> an.getPitchName('x')
         Traceback (most recent call last):
         music21.abcFormat.ABCHandlerException: cannot find any pitch information in: 'x'
@@ -1418,17 +1458,21 @@ class ABCNote(ABCToken):
 
         # making upper here, but this is not relevant
         if carriedAccString:
-            pStr = '%s%s%s' % (name.upper(), carriedAccString, octave)
+            pStr = f'{name.upper()}{carriedAccString}{octave}'
         else:
-            pStr = '%s%s%s' % (name.upper(), accString, octave)
+            pStr = f'{name.upper()}{accString}{octave}'
+
+        # store in global cache for faster speed
+        _cacheKey = (
+            strSrc,
+            self.carriedAccidental,
+            str(activeKeySignature)
+        )
 
-        # store in global cache
-        _pitchTranslationCache[(strSrc,
-                                self.carriedAccidental,
-                                str(activeKeySignature))] = pStr, accidentalDisplayStatus
-        return pStr, accidentalDisplayStatus
+        _pitchTranslationCache[_cacheKey] = (pStr, accidentalDisplayStatus)
+        return (pStr, accidentalDisplayStatus)
 
-    def getQuarterLength(self, strSrc, forceDefaultQuarterLength=None):
+    def getQuarterLength(self, strSrc, forceDefaultQuarterLength=None) -> float:
         '''
         Called with parse(), after context processing, to calculate duration
 
@@ -1487,7 +1531,7 @@ class ABCNote(ABCToken):
 
         numStr = []
         for c in strSrc:
-            if c.isdigit() or c in '/':
+            if c.isdigit() or c == '/':
                 numStr.append(c)
         numStr = ''.join(numStr)
         numStr = numStr.strip()
@@ -1552,8 +1596,11 @@ class ABCNote(ABCToken):
 
         return ql
 
-    def parse(self, forceDefaultQuarterLength=None,
-                    forceKeySignature=None):
+    def parse(
+        self,
+        forceDefaultQuarterLength=None,
+        forceKeySignature=None
+    ) -> None:
         # environLocal.printDebug(['parse', self.src])
         self.chordSymbols, nonChordSymStr = self._splitChordSymbols(self.src)
         # get pitch name form remaining string
@@ -1638,24 +1685,41 @@ class ABCChord(ABCNote):
 
 # ------------------------------------------------------------------------------
 class ABCHandler:
-    # divide elements of a character stream into objects and handle
-    # store in a list, and pass global information to components
-    # Optionally, specify the (major, minor, patch) version of ABC to process--
-    # e.g., (1.2.0). If not set, default ABC 1.3 parsing is performed.
-    def __init__(self, abcVersion=None):
+    '''
+    An ABCHandler is able to divide elements of a character stream into objects and handle
+    store in a list, and passes global information to components
+
+    Optionally, specify the (major, minor, patch) version of ABC to process--
+    e.g., (1.2.0). If not set, default ABC 1.3 parsing is performed.
+
+    If lineBreaksDefinePhrases is True then new lines within music elements
+    define new phrases.  This is useful for parsing extra information from
+    the Essen Folksong repertory
+
+    New in v6.2 -- lineBreaksDefinePhrases -- does not yet do anything
+    '''
+    def __init__(self, abcVersion=None, lineBreaksDefinePhrases=False):
         # tokens are ABC objects import n a linear stream
         self.abcVersion = abcVersion
         self.abcDirectives = {}
         self.tokens = []
         self.activeParens = []
         self.activeSpanners = []
+        self.lineBreaksDefinePhrases = lineBreaksDefinePhrases
+        self.pos = -1
+        self.skipAhead = 0
+        self.isFirstComment = True
+        self.strSrc = ''
+        self.srcLen = len(self.strSrc)  # just documenting this.
+        self.currentCollectStr = ''
 
     @staticmethod
-    def _getLinearContext(strSrc, i):
+    def _getLinearContext(source, i: int) -> Tuple[Any, Any, Any, Any]:
         '''
-        Find the local context of a string or list of objects
+        Find the local context of a string or iterable of objects
         beginning at a particular index.
-        Returns charPrev, charThis, charNext, charNextNext.
+
+        Returns a tuple of charPrev, charThis, charNext, charNextNext.
 
         Staticmethod
 
@@ -1669,7 +1733,7 @@ class ABCHandler:
         >>> abcFormat.ABCHandler._getLinearContext('12345', 4)
         ('4', '5', None, None)
 
-        >>> ah._getLinearContext([32, None, 8, 11, 53], 4)
+        >>> abcFormat.ABCHandler._getLinearContext([32, None, 8, 11, 53], 4)
         (11, 53, None, None)
         >>> ah._getLinearContext([32, None, 8, 11, 53], 2)
         (None, 8, 11, 53)
@@ -1677,61 +1741,59 @@ class ABCHandler:
         (None, 32, None, 8)
         '''
         # Note: this is performance critical method
-
-        lastIndex = len(strSrc) - 1
+        lastIndex = len(source) - 1
         if i > lastIndex:
-            raise ABCHandlerException('bad index value: %d max is %d' % (i, lastIndex))
+            raise ABCHandlerException(f'bad index value {i}, max is {lastIndex}')
 
-        # find local area of string
+        # find local area of iterable
+        cPrev = None
         if i > 0:
-            cPrev = strSrc[i - 1]
-        else:
-            cPrev = None
-        # set this characters
-        c = strSrc[i]
+            cPrev = source[i - 1]
+
+        # set current characters or items
+        c = source[i]
 
         cNext = None
-        if i < len(strSrc) - 1:
-            cNext = strSrc[i + 1]
+        if i < len(source) - 1:
+            cNext = source[i + 1]
 
-        # get 2 chars forward
+        # get 2 entries forward
         cNextNext = None
-        if i < len(strSrc) - 2:
-            cNextNext = strSrc[i + 2]
+        if i < len(source) - 2:
+            cNextNext = source[i + 2]
 
         return cPrev, c, cNext, cNextNext
         # return cPrevNotSpace, cPrev, c, cNext, cNextNotSpace, cNextNext
 
     @staticmethod
-    def _getNextLineBreak(strSrc, i):
-        '''
+    def _getNextLineBreak(strSrc: str, i: int) -> Optional[int]:
+        r'''
         Return index of next line break after character i.
 
         Staticmethod
 
         >>> ah = abcFormat.ABCHandler()
-        >>> strSrc = 'de  we\\n wer bfg\\n'
-        >>> ah._getNextLineBreak(strSrc, 0)
+        >>> inputString = 'de  we\n wer bfg\n'
+        >>> ah._getNextLineBreak(inputString, 0)
         6
-        >>> strSrc[0:6]
+        >>> inputString[0:6]
         'de  we'
 
         from last line break
 
-        >>> abcFormat.ABCHandler._getNextLineBreak(strSrc, 6)
+        >>> abcFormat.ABCHandler._getNextLineBreak(inputString, 6)
         15
-        >>> strSrc[ah._getNextLineBreak(strSrc, 0):]
-        '\\n wer bfg\\n'
+        >>> inputString[ah._getNextLineBreak(inputString, 0):]
+        '\n wer bfg\n'
         '''
         lastIndex = len(strSrc) - 1
-        j = i + 1  # start with next
-        while True:
-            if j > lastIndex or strSrc[j] == '\n':
-                return j  # will increment to next char on loop
-            j += 1
+        for j in range(i + 1, lastIndex + 1):
+            if strSrc[j] == '\n':
+                return j
+        return lastIndex + 1
 
     @staticmethod
-    def barlineTokenFilter(token):
+    def barlineTokenFilter(token: str) -> List[ABCBar]:
         '''
         Some single barline tokens are better replaced
         with two tokens. This method, given a token,
@@ -1748,6 +1810,11 @@ class ABCHandler:
 
         >>> abcFormat.ABCHandler.barlineTokenFilter(':|1')
         [<music21.abcFormat.ABCBar ':|'>, <music21.abcFormat.ABCBar '[1'>]
+
+        If nothing matches, the original token is returned as an ABCBar object:
+
+        >>> abcFormat.ABCHandler.barlineTokenFilter('hi')
+        [<music21.abcFormat.ABCBar 'hi'>]
         '''
         post = []
         if token == '::':
@@ -1777,7 +1844,7 @@ class ABCHandler:
     # --------------------------------------------------------------------------
     # token processing
 
-    def _accidentalPropagation(self):
+    def _accidentalPropagation(self) -> str:
         '''
         Determine how accidentals should 'carry through the measure.'
 
@@ -1795,14 +1862,135 @@ class ABCHandler:
             return self.abcDirectives['propagate-accidentals']
         return 'pitch'  # Default per abc 2.1 standard
 
-    def tokenize(self, strSrc):
+    def parseCommentForVersionInformation(self, commentLine: str):
+        '''
+        If this is the first comment then searches for a version
+        match and set it as .abcVersion
+
+        If not isFirstComment then does nothing:
+
+        >>> ah = abcFormat.ABCHandler()
+        >>> ah.abcVersion is None
+        True
+        >>> ah.isFirstComment
+        True
+
+        >>> ah.parseCommentForVersionInformation('%abc-2.3.2')
+        >>> ah.abcVersion
+        (2, 3, 2)
+        >>> ah.isFirstComment
+        False
+
+        Now will do nothing since isFirstComment is False
+
+        >>> ah.parseCommentForVersionInformation('%abc-4.9.7')
+        >>> ah.abcVersion
+        (2, 3, 2)
+        '''
+        if not self.isFirstComment:
+            return
+        self.isFirstComment = False
+        verMats = reAbcVersion.match(commentLine)
+        if verMats:
+            abcMajor = int(verMats.group(2))
+            abcMinor = int(verMats.group(3))
+            if verMats.group(4):
+                abcPatch = int(verMats.group(4))
+            else:
+                abcPatch = 0
+            verTuple = (abcMajor, abcMinor, abcPatch)
+            self.abcVersion = verTuple
+
+    def processComment(self):
+        r'''
+        Processes the comment at self.pos in self.strSrc, setting self.skipAhead,
+        possibly self.abcVersion, and self.abcDirectives for the directiveKey.
+
+        TODO: store the comment in the stream also.
+
+        >>> from textwrap import dedent
+        >>> ah = abcFormat.ABCHandler()
+        >>> data = dedent("""
+        ...    Hello % this is a comment
+        ...    Bye
+        ...    """)
+        >>> ah.strSrc = data
+        >>> ah.pos = 6
+        >>> ah.processComment()
+        >>> ah.skipAhead
+        19
+        >>> len(' this is a comment\n')
+        19
+        '''
+        self.skipAhead = self._getNextLineBreak(
+            self.strSrc, self.pos
+        ) - (self.pos + 1)
+        commentLine = self.strSrc[self.pos:self.pos + self.skipAhead + 1]
+        self.parseCommentForVersionInformation(commentLine)
+        directiveMatches = reDirective.match(commentLine)
+        if directiveMatches:
+            directiveKey = directiveMatches.group(1)
+            directiveValue = directiveMatches.group(2)
+            self.abcDirectives[directiveKey] = directiveValue
+        # environLocal.printDebug(['got comment:', repr(self.strSrc[i:j + 1])])
+
+    @staticmethod
+    def startsMetadata(c: str, cNext: Optional[str], cNextNext: Optional[str]) -> bool:
+        '''
+        Returns True if this context describes the start of a metadata section, like
+
+        A:something
+
+        Metadata: capital letter, with next char as ':' and some following character
+
+        >>> ah = abcFormat.ABCHandler
+        >>> ah.startsMetadata('A', ':', 's')
+        True
+
+        lowercase w: is a special case for lyric defs
+
+        >>> ah.startsMetadata('w', ':', 's')
+        True
+
+        Following char must be ":"
+
+        >>> ah.startsMetadata('A', ' ', 's')
+        False
+
+        Pipe after colon indicates not metadata (bar info).
+        For example need to not misinterpret repeat bars as metadata
+
+        e.g. dAG FED:|2 dAG FGA|
+
+        this is incorrect, but we can avoid it by
+        looking for a leading pipe and returning False
+
+        >>> ah.startsMetadata('A', ':', '|')
+        False
+
+        >>> ah.startsMetadata('A', ':', None)
+        False
+        '''
+        if cNext != ':':
+            return False
+        elif cNextNext is None:
+            return False
+        elif cNextNext == '|':
+            return False
+        elif c == 'w':
+            return True  # special case, w:...
+        elif c.isalpha() and c.isupper():
+            return True
+        return False
+
+
+    def tokenize(self, strSrc: str) -> None:
         '''
         Walk the abc string, creating ABC objects along the way.
 
         This may be called separately from process(), in the case
         that pre/post parse processing is not needed.
 
-
         >>> abch = abcFormat.ABCHandler()
         >>> abch.tokens
         []
@@ -1830,10 +2018,11 @@ class ABCHandler:
         >>> abch.tokens
         [<music21.abcFormat.ABCTuplet '(6::2'>, <music21.abcFormat.ABCNote 'f'>]
         '''
-        currentIndex = -1
-        collect = []
-        lastIndex = len(strSrc) - 1
-        skipAhead = 0
+        self.srcLen = len(strSrc)
+        self.strSrc = strSrc
+        self.pos = -1
+        self.currentCollectStr = ''
+        self.skipAhead = 0
         # noinspection SpellCheckingInspection
         accidentalsAndDecorations = '.~^=_HLMOPSTuv'
         accidentals = '^=_'
@@ -1842,64 +2031,34 @@ class ABCHandler:
         accidentalized = {}
         accidental = None
         abcPitch = None  # ABC substring defining any pitch within the current token
-        isFirstComment = True
+        self.isFirstComment = True
 
-        while currentIndex < lastIndex:
-            currentIndex += 1
-            currentIndex += skipAhead
-            skipAhead = 0
-            if currentIndex > lastIndex:
+        while self.pos < self.srcLen - 1:
+            self.pos += 1
+            self.pos += self.skipAhead
+            self.skipAhead = 0
+            if self.pos > self.srcLen - 1:
                 break
 
-            q = self._getLinearContext(strSrc, currentIndex)
+            q = self._getLinearContext(self.strSrc, self.pos)
             unused_cPrev, c, cNext, cNextNext = q
             # cPrevNotSpace, cPrev, c, cNext, cNextNotSpace, cNextNext = q
 
             # comment lines, also encoding defs
             if c == '%':
-                skipAhead = self._getNextLineBreak(strSrc, currentIndex) - (currentIndex + 1)
-                commentLine = strSrc[currentIndex:currentIndex + skipAhead + 1]
-                if isFirstComment:
-                    isFirstComment = False
-                    verMats = reAbcVersion.match(commentLine)
-                    if verMats:
-                        abcMajor = int(verMats.group(2))
-                        abcMinor = int(verMats.group(3))
-                        if verMats.group(4):
-                            abcPatch = int(verMats.group(4))
-                        else:
-                            abcPatch = 0
-                        verTuple = (abcMajor, abcMinor, abcPatch)
-                        self.abcVersion = verTuple
-                dirMats = reDirective.match(commentLine)
-                if dirMats:
-                    var = dirMats.group(1)
-                    val = dirMats.group(2)
-                    self.abcDirectives[var] = val
-                # environLocal.printDebug(['got comment:', repr(strSrc[i:j + 1])])
+                self.processComment()
                 continue
 
-            # metadata: capital letter, with next char as ':'
-            # or w: (lyric defs)
-            # some meta data might have bar symbols, for example
-            # need to not misinterpret repeat bars as meta
-            # e.g. dAG FED:|2 dAG FGA| this is incorrect, but can avoid by
-            # looking for a leading pipe
-
-            if (((c.isalpha() and c.isupper()) or c in 'w')
-                    and cNext is not None
-                    and cNext == ':'
-                    and cNextNext is not None
-                    and cNextNext not in '|'):
+            if self.startsMetadata(c, cNext, cNextNext):
                 # collect until end of line; add one to get line break
-                j = self._getNextLineBreak(strSrc, currentIndex)
-                skipAhead = j - (currentIndex + 1)
-                collect = strSrc[currentIndex:j].strip()
-                # environLocal.printDebug(['got metadata:', repr(''.join(collect))])
-                # print('Skipped %d, collected %r, currentIndex %d, new index %d' % (
-                #    skipAhead, collect, currentIndex, j))
-
-                self.tokens.append(ABCMetadata(collect))
+                j = self._getNextLineBreak(self.strSrc, self.pos)
+                self.skipAhead = j - (self.pos + 1)
+                self.currentCollectStr = self.strSrc[self.pos:j].strip()
+                # environLocal.printDebug(['got metadata:', repr(self.currentCollectStr)])
+                # print('Skipped %d, collected %r, pos %d, new index %d' % (
+                #    self.skipAhead, self.currentCollectStr, self.pos, j))
+
+                self.tokens.append(ABCMetadata(self.currentCollectStr))
                 continue
 
             # get bars: if not a space and not alphanumeric
@@ -1911,69 +2070,70 @@ class ABCHandler:
                     barTokenArchetype = ABC_BARS[barIndex][0]
                     if len(barTokenArchetype) == 3:
                         if cNextNext is not None and (c + cNext + cNextNext == barTokenArchetype):
-                            skipAhead = 2
+                            self.skipAhead = 2
                             matchBars = True
                             break
                     elif cNext is not None and (len(barTokenArchetype) == 2):
                         if c + cNext == barTokenArchetype:
-                            skipAhead = 1
+                            self.skipAhead = 1
                             matchBars = True
                             break
                     elif len(barTokenArchetype) == 1:
                         if c == barTokenArchetype:
-                            skipAhead = 0
+                            self.skipAhead = 0
                             matchBars = True
                             break
                 if matchBars is True:
                     accidentalized = {}
                     accidental = None
-                    j = currentIndex + skipAhead + 1
-                    collect = strSrc[currentIndex:j]
+                    j = self.pos + self.skipAhead + 1
+                    self.currentCollectStr = self.strSrc[self.pos:j]
                     # filter and replace with 2 tokens if necessary
-                    for tokenSub in self.barlineTokenFilter(collect):
+                    for tokenSub in self.barlineTokenFilter(self.currentCollectStr):
                         self.tokens.append(tokenSub)
-                    # environLocal.printDebug(['got bars:', repr(collect)])
-                    # if collect == '::':
+                    # environLocal.printDebug(['got bars:', repr(self.currentCollectStr)])
+                    # if self.currentCollectStr == '::':
                     #     # create a start and and an end
                     #     self.tokens.append(ABCBar(':|'))
                     #     self.tokens.append(ABCBar('|:'))
                     # else:
-                    #     self.tokens.append(ABCBar(collect))
+                    #     self.tokens.append(ABCBar(self.currentCollectStr))
                     continue
 
             # get tuplet indicators: (2, (3, (p:q:r or (3::
             if c == '(' and cNext is not None and cNext.isdigit():
-                skipAhead = 1
-                j = currentIndex + skipAhead + 1  # always two characters
-                unused1, possibleColon, qChar, unused2 = self._getLinearContext(strSrc, j)
+                self.skipAhead = 1
+                j = self.pos + self.skipAhead + 1  # always two characters
+                unused1, possibleColon, qChar, unused2 = self._getLinearContext(self.strSrc, j)
                 if possibleColon == ':':
                     j += 1
-                    skipAhead += 1
+                    self.skipAhead += 1
                     if qChar is not None and qChar.isdigit():
                         j += 1
-                        skipAhead += 1
-                    unused1, possibleColon, rChar, unused2 = self._getLinearContext(strSrc, j)
+                        self.skipAhead += 1
+                    unused1, possibleColon, rChar, unused2 = self._getLinearContext(self.strSrc, j)
                     if possibleColon == ':':
                         j += 1  # include the r characters
-                        skipAhead += 1
+                        self.skipAhead += 1
                         if rChar is not None and rChar.isdigit():
                             j += 1
-                            skipAhead += 1
+                            self.skipAhead += 1
 
-                collect = strSrc[currentIndex:j]
-                # environLocal.printDebug(['got tuplet start:', repr(collect)])
-                self.tokens.append(ABCTuplet(collect))
+                self.currentCollectStr = self.strSrc[self.pos:j]
+                # environLocal.printDebug(['got tuplet start:', repr(self.currentCollectStr)])
+                self.tokens.append(ABCTuplet(self.currentCollectStr))
                 continue
 
             # get broken rhythm modifiers: < or >, >>, up to <<<
             if c in '<>':
-                j = currentIndex + 1
-                while j < lastIndex and strSrc[j] in '<>':
+                j = self.pos + 1
+                while j < self.srcLen - 1 and self.strSrc[j] in '<>':
                     j += 1
-                collect = strSrc[currentIndex:j]
-                # environLocal.printDebug(['got bidrectional rhythm mod:', repr(collect)])
-                self.tokens.append(ABCBrokenRhythmMarker(collect))
-                skipAhead = j - (currentIndex + 1)
+                self.currentCollectStr = self.strSrc[self.pos:j]
+                # environLocal.printDebug(
+                #     ['got bidrectional rhythm mod:', repr(self.currentCollectStr)])
+                self.tokens.append(ABCBrokenRhythmMarker(self.currentCollectStr))
+                self.skipAhead = j - (self.pos + 1)
                 continue
 
             # get dynamics. skip over the open paren to avoid confusion.
@@ -1984,18 +2144,18 @@ class ABCHandler:
                                '!diminuendo(!': ABCDimStart,
                                '!diminuendo)!': ABCParenStop,
                                }
-                j = currentIndex + 1
-                while j < currentIndex + 20:  # a reasonable upper bound
-                    if strSrc[j] == '!':
-                        if strSrc[currentIndex:j + 1] in exclaimDict:
-                            exclaimClass = exclaimDict[strSrc[currentIndex:j + 1]]
+                j = self.pos + 1
+                while j < self.pos + 20 and j < self.srcLen:  # a reasonable upper bound
+                    if self.strSrc[j] == '!':
+                        if self.strSrc[self.pos:j + 1] in exclaimDict:
+                            exclaimClass = exclaimDict[self.strSrc[self.pos:j + 1]]
                             exclaimObject = exclaimClass(c)
                             self.tokens.append(exclaimObject)
-                            skipAhead = j - currentIndex  # not + 1
+                            self.skipAhead = j - self.pos  # not + 1
                             break
                         # NB: We're currently skipping over all other '!' expressions
                         else:
-                            skipAhead = j - currentIndex  # not + 1
+                            self.skipAhead = j - self.pos  # not + 1
                             break
                     j += 1
                 # not found, continue...
@@ -2019,32 +2179,32 @@ class ABCHandler:
             # get chord symbols / guitar chords; collected and joined with
             # chord or notes
             if c == '"':
-                j = currentIndex + 1
-                while j < lastIndex and strSrc[j] not in '"':
+                j = self.pos + 1
+                while j < self.srcLen - 1 and self.strSrc[j] != '"':
                     j += 1
                 j += 1  # need character that caused break
                 # there may be more than one chord symbol: need to accumulate
-                activeChordSymbol += strSrc[currentIndex:j]
+                activeChordSymbol += self.strSrc[self.pos:j]
                 # environLocal.printDebug(['got chord symbol:', repr(activeChordSymbol)])
-                skipAhead = j - (currentIndex + 1)
+                self.skipAhead = j - (self.pos + 1)
                 continue
 
             # get chords
             if c == '[':
-                j = currentIndex + 1
-                while j < lastIndex and strSrc[j] not in ']':
+                j = self.pos + 1
+                while j < self.srcLen - 1 and self.strSrc[j] != ']':
                     j += 1
                 j += 1  # need character that caused break
                 # prepend chord symbol
                 if activeChordSymbol != '':
-                    collect = activeChordSymbol + strSrc[currentIndex:j]
+                    self.currentCollectStr = activeChordSymbol + self.strSrc[self.pos:j]
                     activeChordSymbol = ''  # reset
                 else:
-                    collect = strSrc[currentIndex:j]
+                    self.currentCollectStr = self.strSrc[self.pos:j]
 
-                # environLocal.printDebug(['got chord:', repr(collect)])
-                self.tokens.append(ABCChord(collect))
-                skipAhead = j - (currentIndex + 1)
+                # environLocal.printDebug(['got chord:', repr(self.currentCollectStr)])
+                self.tokens.append(ABCChord(self.currentCollectStr))
+                self.skipAhead = j - (self.pos + 1)
                 # TODO: Chords need to be aware of accidentals too.
                 # Also what happens to prefixes and suffixes attached to chords,
                 # like ties.
@@ -2111,50 +2271,50 @@ class ABCHandler:
                     abcPitch = c
                 if c in accidentals:
                     accidental = c
-                j = currentIndex + 1
+                j = self.pos + 1
 
-                while j <= lastIndex:
+                while j <= self.srcLen - 1:
                     # if we have not found pitch alpha
                     # decorations and/or accidentals may precede note names
-                    if not foundPitchAlpha and strSrc[j] in accidentalsAndDecorations:
+                    if not foundPitchAlpha and self.strSrc[j] in accidentalsAndDecorations:
                         j += 1
-                        if strSrc[j] in accidentals:
-                            accidental += strSrc[j]
+                        if self.strSrc[j] in accidentals:
+                            accidental += self.strSrc[j]
                         continue
                     # only allow one pitch alpha to be a continue condition
-                    elif (not foundPitchAlpha and strSrc[j].isalpha()
+                    elif (not foundPitchAlpha and self.strSrc[j].isalpha()
                           # noinspection SpellCheckingInspection
-                          and strSrc[j] not in '~wuvhHLTSN'):
+                          and self.strSrc[j] not in '~wuvhHLTSN'):
                         foundPitchAlpha = True
-                        abcPitch = strSrc[j]
+                        abcPitch = self.strSrc[j]
                         j += 1
                         continue
                     # continue conditions after alpha:
                     # , register modification (, ') or number, rhythm indication
                     # number, /,
-                    elif strSrc[j].isdigit() or strSrc[j] in ',/,\'':
-                        if strSrc[j] in ',\'':  # Register (octave) modification
-                            abcPitch += strSrc[j]
+                    elif self.strSrc[j].isdigit() or self.strSrc[j] in ',/,\'':
+                        if self.strSrc[j] in ',\'':  # Register (octave) modification
+                            abcPitch += self.strSrc[j]
                         j += 1
                         continue
                     else:  # space, all else: break
                         break
                 # prepend chord symbol
                 if activeChordSymbol != '':
-                    collect = activeChordSymbol + strSrc[currentIndex:j]
+                    self.currentCollectStr = activeChordSymbol + self.strSrc[self.pos:j]
                     activeChordSymbol = ''  # reset
                 else:
-                    collect = strSrc[currentIndex:j]
-                # environLocal.printDebug(['got note event:', repr(collect)])
+                    self.currentCollectStr = self.strSrc[self.pos:j]
+                # environLocal.printDebug(['got note event:', repr(self.currentCollectStr)])
 
                 # NOTE: skipping a number of articulations and other markers
-                # not yet supported
+                # that are not yet supported
                 # some collections here are not yet supported; others may be
                 # the result of errors in encoded files
                 # v is up bow; might be: "^Segno"v which also should be dropped
                 # H is fermata
                 # . dot may be staccato, but should be attached to pitch
-                if collect in ('w', 'u', 'v', 'v.', 'h', 'H', 'vk',
+                if self.currentCollectStr in ('w', 'u', 'v', 'v.', 'h', 'H', 'vk',
                                'uk', 'U', '~',
                                '.', '=', 'V', 'v.', 'S', 's',
                                'i', 'I', 'ui', 'u.', 'Q', 'Hy', 'Hx',
@@ -2164,17 +2324,21 @@ class ABCHandler:
                     pass
                 # these are bad chords, or other problematic notations like
                 # "D.C."x
-                elif (collect.startswith('"')
-                      and (collect[-1] in ('u', 'v', 'k', 'K', 'Q', '.',
-                                           'y', 'T', 'w', 'h', 'x')
-                           or collect.endswith('v.'))):
+                elif (self.currentCollectStr.startswith('"')
+                      and (self.currentCollectStr[-1] in ('u', 'v', 'k', 'K', 'Q', '.',
+                                                          'y', 'T', 'w', 'h', 'x',)
+                           or self.currentCollectStr.endswith('v.'))):
                     pass
-                elif collect.startswith('x') or collect.startswith('H') or collect.startswith('Z'):
+                elif (self.currentCollectStr.startswith('x')
+                      or self.currentCollectStr.startswith('H')
+                      or self.currentCollectStr.startswith('Z')):
                     pass
                 # not sure what =20 refers to
-                elif len(collect) > 1 and collect.startswith('=') and collect[1].isdigit():
+                elif (len(self.currentCollectStr) > 1
+                      and self.currentCollectStr.startswith('=')
+                      and self.currentCollectStr[1].isdigit()):
                     pass
-                # only let valid collect strings be parsed
+                # only let valid self.currentCollectStr strings be parsed
                 elif abcPitch:
                     pitchClass = abcPitch[0].upper()
                     carriedAccidental = None
@@ -2191,11 +2355,12 @@ class ABCHandler:
                             carriedAccidental = accidentalized[pitchClass]
                         elif propagation == 'octave' and abcPitch in accidentalized:
                             carriedAccidental = accidentalized[abcPitch]
-                    self.tokens.append(ABCNote(collect, carriedAccidental=carriedAccidental))
+                    abcNote = ABCNote(self.currentCollectStr, carriedAccidental=carriedAccidental)
+                    self.tokens.append(abcNote)
                 else:
-                    self.tokens.append(ABCNote(collect))
+                    self.tokens.append(ABCNote(self.currentCollectStr))
 
-                skipAhead = j - (currentIndex + 1)
+                self.skipAhead = j - (self.pos + 1)
                 continue
             # look for white space: can be used to determine beam groups
             # no action: normal continuation of 1 char
@@ -2337,7 +2502,8 @@ class ABCHandler:
                 if lastDefaultQL is None:
                     raise ABCHandlerException(
                         'no active default note length provided for note processing. '
-                        + 'tPrev: %s, t: %s, tNext: %s' % (tPrev, t, tNext))
+                        + f'tPrev: {tPrev}, t: {t}, tNext: {tNext}'
+                    )
                 t.activeDefaultQuarterLength = lastDefaultQL
                 t.activeKeySignature = lastKeySignature
                 t.applicableSpanners = self.activeSpanners[:]  # fast copy of a list
@@ -2380,7 +2546,7 @@ class ABCHandler:
             # environLocal.printDebug(['tokenProcess: calling parse()', t])
             t.parse()
 
-    def process(self, strSrc):
+    def process(self, strSrc: str) -> None:
         self.tokens = []
         self.tokenize(strSrc)
         self.tokenProcess()
@@ -2693,7 +2859,10 @@ class ABCHandler:
         return post
 
     @staticmethod
-    def _buildMeasureBoundaryIndices(positionList, lastValidIndex):
+    def _buildMeasureBoundaryIndices(
+        positionList: List[int],
+        lastValidIndex: int
+    ) -> List[List[int]]:
         '''
         Staticmethod
 
@@ -2742,7 +2911,7 @@ class ABCHandler:
         # environLocal.printDebug(['splitByMeasure(); pairs pre filter', pairs])
         return pairs
 
-    def splitByMeasure(self):
+    def splitByMeasure(self) -> List['ABCHandlerBar']:
         '''
         Divide a token list by Measures, also
         defining start and end bars of each Measure.
@@ -2851,7 +3020,7 @@ class ABCHandler:
         #         print('\t', t)
         return post
 
-    def tokensToBarIndices(self):
+    def tokensToBarIndices(self) -> List[int]:
         '''
         Return a list of indices indicating which tokens in self.tokens are
         bar lines or the last piece of metadata before a note or chord.
@@ -2879,7 +3048,7 @@ class ABCHandler:
 
         return barIndices
 
-    def hasNotes(self):
+    def hasNotes(self) -> bool:
         '''
         If tokens are processed, return True if ABCNote or
         ABCChord classes are defined
@@ -2909,7 +3078,7 @@ class ABCHandler:
         else:
             return False
 
-    def getTitle(self):
+    def getTitle(self) -> Optional[str]:
         '''
         Get the first title tag. Used for testing.
 
@@ -2967,7 +3136,7 @@ class ABCHandlerBar(ABCHandler):
         return ah
 
 
-def mergeLeadingMetaData(barHandlers):
+def mergeLeadingMetaData(barHandlers: List[ABCHandlerBar]) -> List[ABCHandlerBar]:
     '''
     Given a list of ABCHandlerBar objects, return a list of ABCHandlerBar
     objects where leading metadata is merged, if possible,
@@ -3014,10 +3183,11 @@ def mergeLeadingMetaData(barHandlers):
 class ABCFile(prebase.ProtoM21Object):
     '''
     ABC File or String access
-    # Optionally, specify the (major, minor, patch) version of ABC to process--
-    # e.g., (1.2.0). If not set, default ABC 1.3 parsing is performed.
-    '''
 
+    The abcVersion attribution optionally specifies the (major, minor, patch)
+    version of ABC to process-- e.g., (1.2.0).
+    If not set, default ABC 1.3 parsing is performed.
+    '''
     def __init__(self, abcVersion=None):
         self.abcVersion = abcVersion
         self.file = None
@@ -3058,20 +3228,52 @@ class ABCFile(prebase.ProtoM21Object):
         '''
         return self.readstr(self.file.read(), number)
 
-    def extractReferenceNumber(self, strSrc, number):
+    @staticmethod
+    def extractReferenceNumber(strSrc: str, number: int) -> str:
         '''
-        Extract a single reference number from many defined in a file.
-        This permits loading a single work from a collection/opus
+        Extract the string data relating to a single reference number
+        from a file that defines multiple songs or pieces.
+
+        This method permits loading a single work from a collection/opus
         without parsing the entire file.
+
+        Here is sample data that is not correct ABC but demonstrates the basic concept:
+
+        >>> fileData = """
+        ...   X:1
+        ...   Hello
+        ...   X:2
+        ...   Aloha
+        ...   X:3
+        ...   Goodbye
+        ...   """
+
+        >>> file2 = abcFormat.ABCFile.extractReferenceNumber(fileData, 2)
+        >>> print(file2)
+        X:2
+        Aloha
+
+        If the number does not exist, raises an ABCFileException:
+
+        >>> abcFormat.ABCFile.extractReferenceNumber(fileData, 99)
+        Traceback (most recent call last):
+        music21.abcFormat.ABCFileException: cannot find requested
+            reference number in source file: 99
+
+
+        If the same number is defined twice in one file (should not be) only
+        the first data is returned.
+
+        Changed in v6.2: now a static method.
         '''
         collect = []
         gather = False
         for line in strSrc.split('\n'):
             # must be a single line definition
             # rstrip because of '\r\n' carriage returns
-            if line.strip().startswith('X:') and line.replace(' ', '').rstrip() == 'X:%s' % number:
+            if line.strip().startswith('X:') and line.replace(' ', '').rstrip() == f'X:{number}':
                 gather = True
-            elif line.strip().startswith('X:') and gather is False:
+            elif line.strip().startswith('X:') and not gather:
                 # some numbers are like X:0490 but we may request them as 490...
                 try:
                     forcedNum = int(line.replace(' ', '').rstrip().replace('X:', ''))
@@ -3081,19 +3283,20 @@ class ABCFile(prebase.ProtoM21Object):
                     pass
             # if already gathering and find another ref number definition
             # stop gathering
-            elif gather is True and line.strip().startswith('X:'):
+            elif gather and line.strip().startswith('X:'):
                 break
-            if gather is True:
+
+            if gather:
                 collect.append(line)
 
         if not collect:
             raise ABCFileException(
-                'cannot find requested reference number in source file: %s' % number)
+                f'cannot find requested reference number in source file: {number}')
 
         post = '\n'.join(collect)
         return post
 
-    def readstr(self, strSrc, number=None):
+    def readstr(self, strSrc: str, number: Optional[int] = None) -> ABCHandler:
         '''
         Read a string and process all Tokens.
         Returns a ABCHandler instance.
@@ -3111,9 +3314,6 @@ class ABCFile(prebase.ProtoM21Object):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testTokenization(self):
         from music21.abcFormat import testFiles
 
@@ -3159,7 +3359,7 @@ class Test(unittest.TestCase):
                                 '"C"', '"D7"', '"D"'])
 
         # get index of last match of many
-        i = [m for m in reChordSymbol.finditer(src)][-1].end()
+        i = list(reChordSymbol.finditer(src))[-1].end()
 
         src = '=d2'
         self.assertEqual(rePitchName.findall(src)[0], 'd')
diff --git a/music21/abcFormat/translate.py b/music21/abcFormat/translate.py
index 213bb3ab1..f7a67a512 100644
--- a/music21/abcFormat/translate.py
+++ b/music21/abcFormat/translate.py
@@ -581,9 +581,6 @@ class ABCTranslateException(exceptions21.Music21Exception):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         from music21 import abcFormat
         # from music21.abcFormat import testFiles
diff --git a/music21/alpha/analysis/hasher.py b/music21/alpha/analysis/hasher.py
index a9ceb83a6..cf1245f04 100644
--- a/music21/alpha/analysis/hasher.py
+++ b/music21/alpha/analysis/hasher.py
@@ -581,9 +581,6 @@ class NoteHash(tuple):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def _approximatelyEqual(self, a, b, sig_fig=2):
         '''
         use to look at whether beat lengths are close, within a certain range
@@ -759,9 +756,6 @@ class Test(unittest.TestCase):
 
 class TestExternal(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     # def testBasicHash(self):
     #     # from pprint import pprint as pp
     #     from music21 import corpus
diff --git a/music21/alpha/analysis/search.py b/music21/alpha/analysis/search.py
index ecbe05ed5..96b44a647 100644
--- a/music21/alpha/analysis/search.py
+++ b/music21/alpha/analysis/search.py
@@ -246,9 +246,6 @@ def findConsecutiveScale(source, targetScale, degreesRequired=5,
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testFindConsecutiveScaleA(self):
         from music21 import note
 
diff --git a/music21/analysis/correlate.py b/music21/analysis/correlate.py
index 47e6079d1..053bc72e0 100644
--- a/music21/analysis/correlate.py
+++ b/music21/analysis/correlate.py
@@ -147,7 +147,7 @@ class ActivityMatch:
 
             # if hasattr(entrySrc, 'pitches'):  # a chord
             if entrySrc.isChord:
-                sub = [n for n in entrySrc]
+                sub = list(entrySrc)
             else:
                 sub = [entrySrc]
 
@@ -184,9 +184,6 @@ class ActivityMatch:
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
@@ -203,6 +200,8 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/analysis/discrete.py b/music21/analysis/discrete.py
index 47506da1b..616e34ea0 100644
--- a/music21/analysis/discrete.py
+++ b/music21/analysis/discrete.py
@@ -22,10 +22,10 @@ The :class:`music21.analysis.discrete.KrumhanslSchmuckler`
 :class:`music21.analysis.discrete.Ambitus` (for pitch range analysis) provide examples.
 '''
 # TODO: make an analysis.base for the Discrete and analyzeStream aspects, then create
-# range and key modules in analysis
+#     range and key modules in analysis
 
 import unittest
-from typing import Union, List, Any, Tuple
+from typing import Union, List, Any, Tuple, Iterable
 
 from collections import OrderedDict
 from music21 import exceptions21
@@ -72,7 +72,7 @@ class DiscreteAnalysis:
         # store alternative solutions, which may be sorted or not
         self.alternativeSolutions = []
 
-    def _rgbToHex(self, rgb):
+    def _rgbToHex(self, rgb: Iterable[Union[float, int]]) -> str:
         '''
         Utility conversion method
 
@@ -84,7 +84,7 @@ class DiscreteAnalysis:
         rgb = round(rgb[0]), round(rgb[1]), round(rgb[2])
         return '#%02x%02x%02x' % rgb
 
-    def _hexToRgb(self, value):
+    def _hexToRgb(self, value: str) -> List[int]:
         '''
         Utility conversion method for six-digit hex values to RGB lists.
 
@@ -293,6 +293,7 @@ class KeyWeightKeyAnalysis(DiscreteAnalysis):
                 dst[validKey.name] = self._rgbToHex(rgbStep)
 
     def _getSharpFlatCount(self, subStream) -> Tuple[int, int]:
+        # noinspection PyShadowingNames
         '''
         Determine count of sharps and flats in a Stream
 
@@ -514,6 +515,7 @@ class KeyWeightKeyAnalysis(DiscreteAnalysis):
         return 'Keys'
 
     def solutionToColor(self, solution):
+        # noinspection PyShadowingNames
         '''
         Given a two-element tuple of (tonicPitch, modality) return the proper color
 
@@ -711,15 +713,24 @@ class KeyWeightKeyAnalysis(DiscreteAnalysis):
 # specialize subclass by class
 class KrumhanslSchmuckler(KeyWeightKeyAnalysis):
     '''
-    Implementation of Krumhansl-Schmuckler weightings for
+    Implementation of Krumhansl-Schmuckler/Kessler weightings for
     Krumhansl-Schmuckler key determination algorithm.
+
+    Values from from http://extra.humdrum.org/man/keycor/, which describes these
+    weightings as "Strong tendency to identify the dominant key as the tonic."
+
+    Changed in v.6.3 -- it used to be that these were different from the
+    Kessler profiles, but that was likely a typo.  Thus, KrumhanslKessler and
+    KrumhanslSchmuckler are synonyms of each other.
     '''
     _DOC_ALL_INHERITED = False
-    name = 'Krumhansl Schmuckler Key Analysis'
+    name = 'Krumhansl Schmuckler/Kessler Key Analysis'
     identifiers = ['key.krumhansl', 'key.schmuckler', 'key.krumhansl-schmuckler',
                    'key.krumhanslschmuckler',
                    'krumhansl', 'schmuckler', 'krumhansl-schmuckler',
                    'krumhanslschmuckler',
+                   'key.kessler', 'key.krumhansl-kessler', 'key.krumhanslkessler',
+                   'kessler', 'krumhansl-kessler', 'krumhanslkessler',
                    ]
 
     def __init__(self, referenceStream=None):
@@ -730,7 +741,6 @@ class KrumhanslSchmuckler(KeyWeightKeyAnalysis):
         Returns the key weights. To provide different key weights,
         subclass and override this method. The defaults here are KrumhanslSchmuckler.
 
-
         >>> a = analysis.discrete.KrumhanslSchmuckler()
         >>> len(a.getWeights('major'))
         12
@@ -738,44 +748,6 @@ class KrumhanslSchmuckler(KeyWeightKeyAnalysis):
         12
         '''
         weightType = weightType.lower()
-        if weightType == 'major':
-            return [6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39, 3.66, 2.29, 2.88]
-        elif weightType == 'minor':
-            return [6.33, 2.68, 3.52, 5.38, 2.60, 3.53, 2.54, 4.75, 3.98, 2.69, 3.34, 3.17]
-        else:
-            raise DiscreteAnalysisException(f'Weights must be major or minor, not {weightType}')
-
-
-class KrumhanslKessler(KeyWeightKeyAnalysis):
-    '''
-    Implementation of Krumhansl-Kessler weightings for Krumhansl-Schmuckler
-    key determination algorithm.
-
-    Values from from http://extra.humdrum.org/man/keycor/, which describes these
-    weightings as "Strong tendency to identify the dominant key as the tonic."
-    '''
-    # from http://extra.humdrum.org/man/keycor/
-    _DOC_ALL_INHERITED = False
-    name = 'Krumhansl Kessler Key Analysis'
-    identifiers = ['key.kessler', 'key.krumhansl-kessler', 'key.krumhanslkessler',
-                   'kessler', 'krumhansl-kessler', 'krumhanslkessler',
-                   ]
-
-    def __init__(self, referenceStream=None):
-        super().__init__(referenceStream=referenceStream)
-
-    def getWeights(self, weightType='major'):
-        '''
-        Returns the key weights.
-
-        >>> a = analysis.discrete.KrumhanslKessler()
-        >>> len(a.getWeights('major'))
-        12
-        >>> len(a.getWeights('minor'))
-        12
-        '''
-        weightType = weightType.lower()
-        # note: only one value is different from KrumhanslSchmuckler
         if weightType == 'major':
             return [6.35, 2.23, 3.48, 2.33, 4.38, 4.09, 2.52, 5.19, 2.39,
                     3.66, 2.29, 2.88]
@@ -785,6 +757,9 @@ class KrumhanslKessler(KeyWeightKeyAnalysis):
             raise DiscreteAnalysisException(f'Weights must be major or minor, not {weightType}')
 
 
+KrumhanslKessler = KrumhanslSchmuckler
+
+
 class AardenEssen(KeyWeightKeyAnalysis):
     '''
     Implementation of Aarden-Essen weightings for Krumhansl-Schmuckler key determination algorithm.
@@ -819,7 +794,6 @@ class AardenEssen(KeyWeightKeyAnalysis):
         12
         '''
         weightType = weightType.lower()
-        # note: only one value is different from KrumhanslSchmuckler
         if weightType == 'major':
             return [17.7661, 0.145624, 14.9265, 0.160186, 19.8049, 11.3587,
                     0.291248, 22.062, 0.145624,
@@ -833,7 +807,7 @@ class AardenEssen(KeyWeightKeyAnalysis):
 
 class SimpleWeights(KeyWeightKeyAnalysis):
     '''
-    Implementation of Craig Sapp's simple weights for Krumhansl-Schmuckler
+    Implementation of simple weights by Craig Sapp for Krumhansl-Schmuckler
     key determination algorithm.
 
     Values from from http://extra.humdrum.org/man/keycor/, which describes
@@ -861,7 +835,7 @@ class SimpleWeights(KeyWeightKeyAnalysis):
         12
         '''
         weightType = weightType.lower()
-        # note: only one value is different from KrumhanslSchmuckler
+
         if weightType == 'major':
             return [2, 0, 1, 0, 1, 1, 0, 2, 0, 1, 0, 1]
         elif weightType == 'minor':
@@ -941,7 +915,6 @@ class TemperleyKostkaPayne(KeyWeightKeyAnalysis):
         12
         '''
         weightType = weightType.lower()
-        # note: only one value is different from KrumhanslSchmuckler
         if weightType == 'major':
             return [0.748, 0.060, 0.488, 0.082, 0.670, 0.460,
                     0.096, 0.715, 0.104, 0.366, 0.057, 0.400]
@@ -953,8 +926,10 @@ class TemperleyKostkaPayne(KeyWeightKeyAnalysis):
 
 
 # store a constant with all classes
-keyWeightKeyAnalysisClasses = [KrumhanslSchmuckler, KrumhanslKessler,
-                               AardenEssen, SimpleWeights, BellmanBudge, TemperleyKostkaPayne]
+keyWeightKeyAnalysisClasses = [KrumhanslSchmuckler,
+                               AardenEssen, SimpleWeights,
+                               BellmanBudge, TemperleyKostkaPayne,
+                               ]
 
 
 # -----------------------------------------------------------------------------
@@ -978,6 +953,7 @@ class Ambitus(DiscreteAnalysis):
         self._generateColors()
 
     def _generateColors(self, numColors=None):
+        # noinspection PyShadowingNames
         '''
         Provide uniformly distributed colors across the entire range.
 
@@ -1063,30 +1039,30 @@ class Ambitus(DiscreteAnalysis):
 
         return pitchesFound[minPitchIndex], pitchesFound[maxPitchIndex]
 
-    def getPitchRanges(self, subStream):
+    def getPitchRanges(self, subStream) -> Tuple[int, int]:
         '''
         For a given subStream, return the smallest .ps difference
         between any two pitches and the largest difference
         between any two pitches. This is used to get the
         smallest and largest ambitus possible in a given work.
 
-
-        >>> p = analysis.discrete.Ambitus()
+        >>> ambitusAnalyzer = analysis.discrete.Ambitus()
         >>> s = stream.Stream()
         >>> c = chord.Chord(['a2', 'b4', 'c8'])
         >>> s.append(c)
-        >>> [int(thisPitch.ps) for thisPitch in p.getPitchSpan(s)]
+        >>> [int(thisPitch.ps) for thisPitch in ambitusAnalyzer.getPitchSpan(s)]
         [45, 108]
-        >>> p.getPitchRanges(s)
+        >>> ambitusAnalyzer.getPitchRanges(s)
         (26, 63)
 
         >>> s = corpus.parse('bach/bwv66.6')
-        >>> p.getPitchRanges(s)
+        >>> ambitusAnalyzer.getPitchRanges(s)
         (0, 34)
 
+        An empty stream has pitch range (0, 0)
 
         >>> s = stream.Stream()
-        >>> p.getPitchRanges(s)
+        >>> ambitusAnalyzer.getPitchRanges(s)
         (0, 0)
         '''
         ssfn = subStream.flat.notes
@@ -1277,7 +1253,7 @@ class MelodicIntervalDiversity(DiscreteAnalysis):
 
         # if this has parts, need to move through each at a time
         if sStream.hasPartLikeStreams():
-            procList = [s for s in sStream.getElementsByClass('Stream')]
+            procList = list(sStream.getElementsByClass('Stream'))
         else:  # assume a single list of notes, or sStream is a part
             procList = [sStream]
 
@@ -1408,7 +1384,6 @@ def analysisClassFromMethodName(method):
     analysisClasses = [
         Ambitus,
         KrumhanslSchmuckler,
-        KrumhanslKessler,
         AardenEssen,
         SimpleWeights,
         BellmanBudge,
@@ -1450,9 +1425,6 @@ def analysisClassFromMethodName(method):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testKeyAnalysisKrumhansl(self):
         from music21 import converter
 
@@ -1574,12 +1546,6 @@ class Test(unittest.TestCase):
         self.assertEqual(str(post[1]), 'major')
         self.assertEqual(str(post[2])[0:7], '0.81063')
 
-        p = KrumhanslKessler()
-        k = p.getSolution(s)
-        post = [k.tonic, k.mode, k.correlationCoefficient]
-        self.assertEqual(str(post[0]), 'F#')
-        self.assertEqual(str(post[1]), 'major')
-
         p = AardenEssen()
         k = p.getSolution(s)
         post = [k.tonic, k.mode, k.correlationCoefficient]
@@ -1636,7 +1602,7 @@ class Test(unittest.TestCase):
 # define presented order in documentation
 _DOC_ORDER = [analyzeStream, DiscreteAnalysis, Ambitus, MelodicIntervalDiversity,
               KeyWeightKeyAnalysis, SimpleWeights, AardenEssen, BellmanBudge,
-              KrumhanslSchmuckler, KrumhanslKessler, TemperleyKostkaPayne]
+              KrumhanslSchmuckler, TemperleyKostkaPayne]
 
 # -----------------------------------------------------------------------------
 
diff --git a/music21/analysis/elements.py b/music21/analysis/elements.py
index 746bb0f5e..982746cb9 100644
--- a/music21/analysis/elements.py
+++ b/music21/analysis/elements.py
@@ -12,7 +12,7 @@ import collections
 
 _MOD = 'analysis.elements'
 
-def attributeCount(streamOrStreamIter, attrName='quarterLength'):
+def attributeCount(streamOrStreamIter, attrName='quarterLength') -> collections.Counter:
     '''
     Return a collections.Counter of attribute usage for elements in a stream
     or StreamIterator
diff --git a/music21/analysis/metrical.py b/music21/analysis/metrical.py
index 8007bf0fc..5a7cc0648 100644
--- a/music21/analysis/metrical.py
+++ b/music21/analysis/metrical.py
@@ -162,9 +162,6 @@ def thomassenMelodicAccent(streamIn):
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testSingle(self):
         '''Need to test direct meter creation w/o stream
         '''
@@ -189,17 +186,7 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 
 class Test(unittest.TestCase):
-    '''Unit tests
-    '''
-
-    def runTest(self):
-        pass
-
-    def setUp(self):
-        pass
-
-    def testDoNothing(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/analysis/neoRiemannian.py b/music21/analysis/neoRiemannian.py
index 195c51d4f..c2d5b5d7a 100644
--- a/music21/analysis/neoRiemannian.py
+++ b/music21/analysis/neoRiemannian.py
@@ -232,9 +232,7 @@ def isNeoR(c1, c2, transforms='LRP'):
 
     c2NO = c2.normalOrder
 
-    transformList = [x for x in transforms]
-
-    for i in transformList:
+    for i in transforms:
         if i == 'L':
             c = L(c1)
             if c.normalOrder == c2NO:
diff --git a/music21/analysis/patel.py b/music21/analysis/patel.py
index 3541a9817..657b5355a 100644
--- a/music21/analysis/patel.py
+++ b/music21/analysis/patel.py
@@ -109,8 +109,7 @@ class PatelException(exceptions21.Music21Exception):
     pass
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/analysis/reduceChords.py b/music21/analysis/reduceChords.py
index 3465585f7..fed053fa2 100644
--- a/music21/analysis/reduceChords.py
+++ b/music21/analysis/reduceChords.py
@@ -145,8 +145,7 @@ class ChordReducer:
     def _debug(scoreTree):
         for part, subtree in scoreTree.toPartwiseTimespanTrees().items():
             print(part)
-            timespanList = [x for x in subtree]
-            for timespan in timespanList:
+            for timespan in subtree:
                 print('\t', timespan)
             overlap = subtree.maximumOverlap()
             if overlap >= 1:
@@ -325,7 +324,7 @@ class ChordReducer:
             return verticality.bassTimespan
 
         for unused_part, subtree in partwiseTrees.items():
-            timespanList = [x for x in subtree]
+            timespanList = list(subtree)
             for bassTimespan, group in itertools.groupby(timespanList, procedure):
                 group = list(group)
 
@@ -435,7 +434,7 @@ class ChordReducer:
 
         mapping = scoreTree.toPartwiseTimespanTrees()
         subtree = mapping[part]
-        timespanList = [x for x in subtree]
+        timespanList = list(subtree)
         for unused_key, group in itertools.groupby(timespanList, procedure):
             # measureNumber, pitches = key
             group = list(group)
@@ -686,9 +685,6 @@ class ChordReducer:
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testSimpleMeasure(self):
         # from music21 import chord
         s = stream.Measure()
@@ -701,8 +697,6 @@ class Test(unittest.TestCase):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testTrecentoMadrigal(self):
         from music21 import corpus
diff --git a/music21/analysis/reduceChordsOld.py b/music21/analysis/reduceChordsOld.py
index add4a35df..ae3448410 100644
--- a/music21/analysis/reduceChordsOld.py
+++ b/music21/analysis/reduceChordsOld.py
@@ -322,9 +322,6 @@ class ChordReducer:
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testSimpleMeasure(self):
         from music21 import chord
         s = stream.Measure()
@@ -337,9 +334,6 @@ class Test(unittest.TestCase):
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testTrecentoMadrigal(self):
         from music21 import corpus
         # c = corpus.parse('beethoven/opus18no1', 2).measures(1, 19)
diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py
index 6e6ea3b6c..58f5585f1 100644
--- a/music21/analysis/reduction.py
+++ b/music21/analysis/reduction.py
@@ -837,7 +837,7 @@ class PartReduction:
             partMaxRef[partBundle['pGroupId']] = partMax
 
         try:
-            maxOfMax = max([e for e in partMaxRef.values()])
+            maxOfMax = max(partMaxRef.values())
         except ValueError:  # empty part?
             maxOfMax = 0
 
@@ -888,10 +888,6 @@ class PartReduction:
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
-
     def testExtractionA(self):
         from music21 import analysis, corpus
         s = corpus.parse('bwv66.6')
@@ -942,7 +938,7 @@ class Test(unittest.TestCase):
         post = sr.reduce()
         # post.show()
         self.assertEqual(len(post.parts), 5)
-        match = [n for n in post.parts[0].flat.notes]
+        match = post.parts[0].flat.notes
         self.assertEqual(len(match), 3)
         # post.show()
 
diff --git a/music21/analysis/windowed.py b/music21/analysis/windowed.py
index 801fb31af..21347af02 100644
--- a/music21/analysis/windowed.py
+++ b/music21/analysis/windowed.py
@@ -339,9 +339,7 @@ class WindowedAnalysis:
 
 # -----------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
-
-    def runTest(self):
-        pass
+    pass
 
 
 class TestMockProcessor:
@@ -354,9 +352,6 @@ class TestMockProcessor:
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         from music21 import corpus
         from music21.analysis import discrete
diff --git a/music21/articulations.py b/music21/articulations.py
index 062f771a7..1d6b93e4c 100644
--- a/music21/articulations.py
+++ b/music21/articulations.py
@@ -661,9 +661,6 @@ class HandbellIndication(TechnicalIndication):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         a = FretBend()
         self.assertEqual(a.bendAlter, None)
diff --git a/music21/audioSearch/__init__.py b/music21/audioSearch/__init__.py
index 412492890..0ecad38b0 100644
--- a/music21/audioSearch/__init__.py
+++ b/music21/audioSearch/__init__.py
@@ -974,9 +974,7 @@ class AudioSearchException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/audioSearch/recording.py b/music21/audioSearch/recording.py
index a8fa670ce..1c11c0d9b 100644
--- a/music21/audioSearch/recording.py
+++ b/music21/audioSearch/recording.py
@@ -114,16 +114,11 @@ class RecordingException(exceptions21.Music21Exception):
 
 # -----------------------------------------
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testRecording(self):
         '''
         record one second of data and print 10 records
diff --git a/music21/audioSearch/scoreFollower.py b/music21/audioSearch/scoreFollower.py
index 11fd7d9cf..611347e63 100644
--- a/music21/audioSearch/scoreFollower.py
+++ b/music21/audioSearch/scoreFollower.py
@@ -568,10 +568,6 @@ class ScoreFollower:
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    pass
-
-    def runTest(self):
-        pass
 
     def xtestRunScoreFollower(self):
         from music21 import corpus
diff --git a/music21/audioSearch/transcriber.py b/music21/audioSearch/transcriber.py
index 5ea5d7ce3..955a0db54 100644
--- a/music21/audioSearch/transcriber.py
+++ b/music21/audioSearch/transcriber.py
@@ -139,10 +139,6 @@ def monophonicStreamFromFile(fileName, useScale=None):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    pass
-
-    def runTest(self):
-        pass
 
     def xtestRunTranscribe(self):
         saveFile = environLocal.getRootTempDir() / 'new_song.wav'
diff --git a/music21/bar.py b/music21/bar.py
index ea2db7c5c..c80143651 100644
--- a/music21/bar.py
+++ b/music21/bar.py
@@ -375,10 +375,6 @@ class Repeat(repeat.RepeatMark, Barline):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
-
     def testSortOrder(self):
         from music21 import stream, clef, note, metadata
         m = stream.Measure()
diff --git a/music21/base.py b/music21/base.py
index 2db2c96ec..9c17b730a 100644
--- a/music21/base.py
+++ b/music21/base.py
@@ -28,7 +28,7 @@ available after importing `music21`.
 <class 'music21.base.Music21Object'>
 
 >>> music21.VERSION_STR
-'6.2.0'
+'6.3.0'
 
 Alternatively, after doing a complete import, these classes are available
 under the module "base":
@@ -3307,11 +3307,14 @@ class Music21Object(prebase.ProtoM21Object):
         >>> m = stream.Measure()
         >>> m.timeSignature = meter.TimeSignature('3/4')
         >>> m.repeatAppend(n, 6)
-        >>> [m.notes[i].beat for i in range(6)]
+        >>> [n.beat for n in m.notes]
         [1.0, 1.5, 2.0, 2.5, 3.0, 3.5]
 
+
+        Fractions are returned for positions that cannot be represented perfectly using floats:
+
         >>> m.timeSignature = meter.TimeSignature('6/8')
-        >>> [m.notes[i].beat for i in range(6)]
+        >>> [n.beat for n in m.notes]
         [1.0, Fraction(4, 3), Fraction(5, 3), 2.0, Fraction(7, 3), Fraction(8, 3)]
 
         >>> s = stream.Stream()
@@ -3321,31 +3324,67 @@ class Music21Object(prebase.ProtoM21Object):
         [1.0, 2.0, 3.0, 1.0, 2.0, 3.0, 1.0, 2.0]
 
 
-        >>> s = stream.Stream()
-        >>> ts = meter.TimeSignature('4/4')
-        >>> s.insert(0, ts)
-        >>> n = note.Note(type='eighth')
-        >>> s.repeatAppend(n, 8)
-        >>> s.makeMeasures(inPlace=True)
-        >>> [n.beat for n in s.flat.notes]
-        [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5]
+        Notes inside flat streams can still find the original beat placement from outer
+        streams:
+
+        >>> p = stream.Part()
+        >>> ts = meter.TimeSignature('2/4')
+        >>> p.insert(0, ts)
 
+        >>> n = note.Note('C4', type='eighth')
+        >>> m1 = stream.Measure(number=1)
+        >>> m1.repeatAppend(n, 4)
+
+        >>> m2 = stream.Measure(number=2)
+        >>> m2.repeatAppend(n, 4)
+
+        >>> p.append([m1, m2])
+        >>> [n.beat for n in p.flat.notes]
+        [1.0, 1.5, 2.0, 2.5, 1.0, 1.5, 2.0, 2.5]
+
+
+        Fractions print out as improper fraction strings
 
         >>> m = stream.Measure()
         >>> m.timeSignature = meter.TimeSignature('4/4')
         >>> n = note.Note()
         >>> n.quarterLength = 1/3
         >>> m.repeatAppend(n, 12)
-        >>> for i in range(5):
-        ...    print(m.notes[i].beat)
+        >>> for n in m.notes[:5]:
+        ...    print(n.beat)
         1.0
         4/3
         5/3
         2.0
         7/3
+
+        If there is no TimeSignature object in sites then returns the special float
+        'nan' meaning "Not a Number":
+
+        >>> isolatedNote = note.Note('E4')
+        >>> isolatedNote.beat
+        nan
+
+        Not-a-number objects do not compare equal to themselves:
+
+        >>> isolatedNote.beat == isolatedNote.beat
+        False
+
+        Instead to test for nan, import the math module and use `isnan()`:
+
+        >>> import math
+        >>> math.isnan(isolatedNote.beat)
+        True
+
+
+        Changed in v.6.3 -- returns `nan` if
+        there is no TimeSignature in sites.  Previously raised an exception.
         '''
-        ts = self._getTimeSignatureForBeat()
-        return ts.getBeatProportion(ts.getMeasureOffsetOrMeterModulusOffset(self))
+        try:
+            ts = self._getTimeSignatureForBeat()
+            return ts.getBeatProportion(ts.getMeasureOffsetOrMeterModulusOffset(self))
+        except Music21ObjectException:
+            return float('nan')
 
     @property
     def beatStr(self) -> str:
@@ -3356,25 +3395,38 @@ class Music21Object(prebase.ProtoM21Object):
         fractional designation to show progress through the beat.
 
 
-        >>> n = note.Note()
-        >>> n.quarterLength = 0.5
+        >>> n = note.Note(type='eighth')
         >>> m = stream.Measure()
         >>> m.timeSignature = meter.TimeSignature('3/4')
         >>> m.repeatAppend(n, 6)
-        >>> [m.notes[i].beatStr for i in range(6)]
+
+        >>> [n.beatStr for n in m.notes]
         ['1', '1 1/2', '2', '2 1/2', '3', '3 1/2']
+
         >>> m.timeSignature = meter.TimeSignature('6/8')
-        >>> [m.notes[i].beatStr for i in range(6)]
+        >>> [n.beatStr for n in m.notes]
         ['1', '1 1/3', '1 2/3', '2', '2 1/3', '2 2/3']
 
         >>> s = stream.Stream()
         >>> s.insert(0, meter.TimeSignature('3/4'))
-        >>> s.repeatAppend(note.Note(), 8)
+        >>> s.repeatAppend(note.Note(type='quarter'), 8)
         >>> [n.beatStr for n in s.notes]
         ['1', '2', '3', '1', '2', '3', '1', '2']
+
+        If there is no TimeSignature object in sites then returns 'nan' for not a number.
+
+        >>> isolatedNote = note.Note('E4')
+        >>> isolatedNote.beatStr
+        'nan'
+
+        Changed in v.6.3 -- returns 'nan' if
+        there is no TimeSignature in sites.  Previously raised an exception.
         '''
-        ts = self._getTimeSignatureForBeat()
-        return ts.getBeatProportionStr(ts.getMeasureOffsetOrMeterModulusOffset(self))
+        try:
+            ts = self._getTimeSignatureForBeat()
+            return ts.getBeatProportionStr(ts.getMeasureOffsetOrMeterModulusOffset(self))
+        except Music21ObjectException:
+            return 'nan'
 
     @property
     def beatDuration(self) -> 'music21.duration.Duration':
@@ -3386,26 +3438,53 @@ class Music21Object(prebase.ProtoM21Object):
         If extending beyond the Measure, or in a Stream with a TimeSignature,
         the meter modulus value will be returned.
 
-        >>> n = note.Note()
-        >>> n.quarterLength = 0.5
+        >>> n = note.Note('C4', type='eighth')
+        >>> n.duration
+        <music21.duration.Duration 0.5>
+
         >>> m = stream.Measure()
         >>> m.timeSignature = meter.TimeSignature('3/4')
         >>> m.repeatAppend(n, 6)
-        >>> [m.notes[i].beatDuration.quarterLength for i in range(6)]
+        >>> n0 = m.notes[0]
+        >>> n0.beatDuration
+        <music21.duration.Duration 1.0>
+
+        Notice that the beat duration is the same for all these notes
+        and has nothing to do with the duration of the element itself
+
+        >>> [n.beatDuration.quarterLength for n in m.notes]
         [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
 
+        Changing the time signature changes the beat duration:
+
         >>> m.timeSignature = meter.TimeSignature('6/8')
-        >>> [m.notes[i].beatDuration.quarterLength for i in range(6)]
+        >>> [n.beatDuration.quarterLength for n in m.notes]
         [1.5, 1.5, 1.5, 1.5, 1.5, 1.5]
 
+        Complex time signatures will give different note lengths:
+
         >>> s = stream.Stream()
         >>> s.insert(0, meter.TimeSignature('2/4+3/4'))
-        >>> s.repeatAppend(note.Note(), 8)
+        >>> s.repeatAppend(note.Note(type='quarter'), 8)
         >>> [n.beatDuration.quarterLength for n in s.notes]
         [2.0, 2.0, 3.0, 3.0, 3.0, 2.0, 2.0, 3.0]
+
+
+        If there is no TimeSignature object in sites then returns a duration object
+        of Zero length.
+
+        >>> isolatedNote = note.Note('E4')
+        >>> isolatedNote.beatDuration
+        <music21.duration.Duration 0.0>
+
+        Changed in v.6.3 -- returns a duration.Duration object of length 0 if
+        there is no TimeSignature in sites.  Previously raised an exception.
         '''
-        ts = self._getTimeSignatureForBeat()
-        return ts.getBeatDuration(ts.getMeasureOffsetOrMeterModulusOffset(self))
+        try:
+            ts = self._getTimeSignatureForBeat()
+            return ts.getBeatDuration(ts.getMeasureOffsetOrMeterModulusOffset(self))
+        except Music21ObjectException:
+            return duration.Duration(0)
 
     @property
     def beatStrength(self) -> float:
@@ -3418,47 +3497,61 @@ class Music21Object(prebase.ProtoM21Object):
         minimum accent weight will be returned.
 
 
-        >>> n = note.Note()
-        >>> n.quarterLength = 0.5
+        >>> n = note.Note(type='eighth')
         >>> m = stream.Measure()
         >>> m.timeSignature = meter.TimeSignature('3/4')
         >>> m.repeatAppend(n, 6)
-        >>> [m.notes[i].beatStrength for i in range(6)]
+
+        The first note of a measure is (generally?) always beat strength 1.0:
+
+        >>> m.notes[0].beatStrength
+        1.0
+
+        Notes on weaker beats have lower strength:
+
+        >>> [n.beatStrength for n in m.notes]
         [1.0, 0.25, 0.5, 0.25, 0.5, 0.25]
 
         >>> m.timeSignature = meter.TimeSignature('6/8')
-        >>> [m.notes[i].beatStrength for i in range(6)]
+        >>> [n.beatStrength for n in m.notes]
         [1.0, 0.25, 0.25, 0.5, 0.25, 0.25]
 
 
+        Importantly, the actual numbers here have no particular meaning.  You cannot
+        "add" two beatStrengths of 0.25 and say that they have the same beat strength
+        as one note of 0.5.  Only the ordinal relations really matter.  Even taking
+        an average of beat strengths is a tiny bit methodologically suspect (though
+        it is common in research for lack of a better method).
+
         We can also get the beatStrength for elements not in
         a measure, if the enclosing stream has a :class:`~music21.meter.TimeSignature`.
         We just assume that the time signature carries through to
         hypothetical following measures:
 
-
-        >>> n = note.Note('E--3', type='quarter')
+        >>> n = note.Note('E-3', type='quarter')
         >>> s = stream.Stream()
         >>> s.insert(0.0, meter.TimeSignature('2/2'))
         >>> s.repeatAppend(n, 12)
-        >>> [s.notes[i].beatStrength for i in range(12)]
+        >>> [n.beatStrength for n in s.notes]
         [1.0, 0.25, 0.5, 0.25, 1.0, 0.25, 0.5, 0.25, 1.0, 0.25, 0.5, 0.25]
 
 
-        Changing the meter changes the output, of course:
+        Changing the meter changes the output, of course, as can be seen from the
+        fourth quarter note onward:
 
         >>> s.insert(4.0, meter.TimeSignature('3/4'))
-        >>> [s.notes[i].beatStrength for i in range(12)]
+        >>> [n.beatStrength for n in s.notes]
         [1.0, 0.25, 0.5, 0.25, 1.0, 0.5, 0.5, 1.0, 0.5, 0.5, 1.0, 0.5]
 
 
-        Test not using measures
+        The method returns correct numbers for the prevailing time signature
+        even if no measures have been made:
 
-        >>> n = note.Note('E--3')
-        >>> n.quarterLength = 2
+        >>> n = note.Note('E--3', type='half')
         >>> s = stream.Stream()
         >>> s.isMeasure
         False
+
         >>> s.insert(0, meter.TimeSignature('2/2'))
         >>> s.repeatAppend(n, 16)
         >>> s.notes[0].beatStrength
@@ -3469,22 +3562,37 @@ class Music21Object(prebase.ProtoM21Object):
         1.0
         >>> s.notes[5].beatStrength
         0.5
+
+        Getting the beatStrength of an object without a time signature in its context
+        returns the not-a-number special object 'nan':
+
+        >>> n2 = note.Note(type='whole')
+        >>> n2.beatStrength
+        nan
+        >>> from math import isnan
+        >>> isnan(n2.beatStrength)
+        True
+
+        Changed in v6.3 -- return 'nan' instead of raising an exception.
         '''
-        ts = self._getTimeSignatureForBeat()
-        meterModulus = ts.getMeasureOffsetOrMeterModulusOffset(self)
+        try:
+            ts = self._getTimeSignatureForBeat()
+            meterModulus = ts.getMeasureOffsetOrMeterModulusOffset(self)
 
-        return ts.getAccentWeight(meterModulus,
-                                  forcePositionMatch=True,
-                                  permitMeterModulus=False)
+            return ts.getAccentWeight(meterModulus,
+                                      forcePositionMatch=True,
+                                      permitMeterModulus=False)
+        except Music21ObjectException:
+            return float('nan')
 
     def _getSeconds(self) -> float:
         # do not search of duration is zero
-        if self.duration is None or self.duration.quarterLength == 0.0:
+        if self.duration.quarterLength == 0.0:
             return 0.0
 
         ti = self.getContextByClass('TempoIndication')
         if ti is None:
-            raise Music21ObjectException('this object does not have a TempoIndication in Sites')
+            return float('nan')
         mm = ti.getSoundingMetronomeMark()
         # once we have mm, simply pass in this duration
         return mm.durationToSeconds(self.duration)
@@ -3502,14 +3610,82 @@ class Music21Object(prebase.ProtoM21Object):
     seconds = property(_getSeconds, _setSeconds, doc='''
         Get or set the duration of this object in seconds, assuming
         that this object has a :class:`~music21.tempo.MetronomeMark`
-        or :class:`~music21.tempo.MetricModulation` in its past context.
+        or :class:`~music21.tempo.MetricModulation`
+        (or any :class:`~music21.tempo.TempoIndication`) in its past context.
 
         >>> s = stream.Stream()
-        >>> s.repeatAppend(note.Note(), 12)
-        >>> s.insert(0, tempo.MetronomeMark(number=120))
-        >>> s.insert(6, tempo.MetronomeMark(number=240))
+        >>> for i in range(3):
+        ...    s.append(note.Note(type='quarter'))
+        ...    s.append(note.Note(type='quarter', dots=1))
+        >>> s.insert(0, tempo.MetronomeMark(number=60))
+        >>> s.insert(2, tempo.MetronomeMark(number=120))
+        >>> s.insert(4, tempo.MetronomeMark(number=30))
         >>> [n.seconds for n in s.notes]
-        [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25]
+        [1.0, 1.5, 0.5, 0.75, 2.0, 3.0]
+
+        Setting the number of seconds on a music21 object changes its duration:
+
+        >>> lastNote = s.notes[-1]
+        >>> lastNote.duration.fullName
+        'Dotted Quarter'
+        >>> lastNote.seconds = 4.0
+        >>> lastNote.duration.fullName
+        'Half'
+
+        Any object of length 0 has zero-second length:
+
+        >>> tc = clef.TrebleClef()
+        >>> tc.seconds
+        0.0
+
+        If an object has positive duration but no tempo indication in its context,
+        then the special number 'nan' for "not-a-number" is returned:
+
+        >>> r = note.Rest(type='whole')
+        >>> r.seconds
+        nan
+
+        Check for 'nan' with the `math.isnan()` routine:
+
+        >>> import math
+        >>> math.isnan(r.seconds)
+        True
+
+        Setting seconds for an element without a tempo-indication in its sites raises
+        a Music21ObjectException:
+
+        >>> r.seconds = 2.0
+        Traceback (most recent call last):
+        music21.base.Music21ObjectException: this object does not have a TempoIndication in Sites
+
+        Note that if an object is in multiple Sites with multiple Metronome marks,
+        the activeSite (or the hierarchy of the activeSite)
+        determines its seconds for getting or setting:
+
+        >>> r = note.Rest(type='whole')
+        >>> m1 = stream.Measure()
+        >>> m1.insert(0, tempo.MetronomeMark(number=60))
+        >>> m1.append(r)
+        >>> r.seconds
+        4.0
+
+        >>> m2 = stream.Measure()
+        >>> m2.insert(0, tempo.MetronomeMark(number=120))
+        >>> m2.append(r)
+        >>> r.seconds
+        2.0
+        >>> r.activeSite = m1
+        >>> r.seconds
+        4.0
+        >>> r.seconds = 1.0
+        >>> r.duration.type
+        'quarter'
+        >>> r.activeSite = m2
+        >>> r.seconds = 1.0
+        >>> r.duration.type
+        'half'
+
+        Changed in v6.3 -- return nan instead of raising an exception.
         ''')
 
 
@@ -3719,8 +3895,6 @@ class TestMock(Music21Object):
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testCopyAndDeepcopy(self):
         '''
@@ -4275,15 +4449,15 @@ class Test(unittest.TestCase):
         s3.append(n3)
 
         # only get n1 here, as that is only level available
-        self.assertEqual([n for n in s1.recurse().getElementsByClass('Note')], [n1])
-        self.assertEqual([n for n in s2.recurse().getElementsByClass('Note')], [n2])
-        self.assertEqual([c for c in s1.recurse().getElementsByClass('Clef')], [c1])
-        self.assertEqual([c for c in s2.recurse().getElementsByClass('Clef')], [c2])
+        self.assertEqual(s1.recurse().getElementsByClass('Note')[0], n1)
+        self.assertEqual(s2.recurse().getElementsByClass('Note')[0], n2)
+        self.assertEqual(s1.recurse().getElementsByClass('Clef')[0], c1)
+        self.assertEqual(s2.recurse().getElementsByClass('Clef')[0], c2)
 
         # attach s2 to s1
         s2.append(s1)
         # stream 1 gets both notes
-        self.assertEqual([n for n in s2.recurse().getElementsByClass('Note')], [n2, n1])
+        self.assertEqual(list(s2.recurse().getElementsByClass('Note')), [n2, n1])
 
     def testSetEditorial(self):
         b2 = Music21Object()
diff --git a/music21/beam.py b/music21/beam.py
index 7ba026e60..fce6628b5 100644
--- a/music21/beam.py
+++ b/music21/beam.py
@@ -684,9 +684,7 @@ class Beams(prebase.ProtoM21Object, EqualSlottedObjectMixin):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/braille/basic.py b/music21/braille/basic.py
index 34ee44c60..540f8f63b 100644
--- a/music21/braille/basic.py
+++ b/music21/braille/basic.py
@@ -1671,9 +1671,7 @@ class BrailleBasicException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/braille/examples.py b/music21/braille/examples.py
index ac603524c..812277c35 100644
--- a/music21/braille/examples.py
+++ b/music21/braille/examples.py
@@ -185,9 +185,6 @@ def happyBirthday():
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testHappyBirthdayDebug(self):
         from music21.braille.translate import objectToBraille
         hb = happyBirthday()
diff --git a/music21/braille/noteGrouping.py b/music21/braille/noteGrouping.py
index 1d588cba3..a5ef7d2e7 100644
--- a/music21/braille/noteGrouping.py
+++ b/music21/braille/noteGrouping.py
@@ -276,9 +276,7 @@ def transcribeNoteGrouping(brailleElementGrouping, showLeadingOctave=True):
 
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/braille/objects.py b/music21/braille/objects.py
index cece01ca9..e9695f9a5 100644
--- a/music21/braille/objects.py
+++ b/music21/braille/objects.py
@@ -77,9 +77,7 @@ class BrailleExplicitNoteExtraSmaller(BrailleExplicitNoteLength):
 
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/braille/segment.py b/music21/braille/segment.py
index ad77b8cae..8ce0da7b6 100644
--- a/music21/braille/segment.py
+++ b/music21/braille/segment.py
@@ -2323,9 +2323,6 @@ def splitMeasure(music21Measure, beatDivisionOffset=0, useTimeSignature=None):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testGetRawSegments(self):
         from music21 import converter
 
diff --git a/music21/braille/text.py b/music21/braille/text.py
index 32214a309..c6eb54e2e 100644
--- a/music21/braille/text.py
+++ b/music21/braille/text.py
@@ -676,9 +676,7 @@ class BrailleTextException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py
index 671dac1af..9a44b9024 100644
--- a/music21/chord/__init__.py
+++ b/music21/chord/__init__.py
@@ -5155,9 +5155,6 @@ def fromIntervalVector(notation, getZRelation=False):
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         for pitchList in [['g2', 'c4', 'c#6'],
                           ['c', 'd-', 'f#', 'g']]:
@@ -5184,9 +5181,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def pitchOut(self, listIn):
         '''
         make tests for old-style pitch representation still work.
@@ -5212,6 +5206,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/chord/tables.py b/music21/chord/tables.py
index 8ee275d4a..0a2ce829f 100644
--- a/music21/chord/tables.py
+++ b/music21/chord/tables.py
@@ -2920,9 +2920,6 @@ def seekChordTablesAddress(c):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testDummy(self):
         self.assertEqual(True, True)
 
diff --git a/music21/clef.py b/music21/clef.py
index 8a9cf9a63..420a9a736 100644
--- a/music21/clef.py
+++ b/music21/clef.py
@@ -863,9 +863,6 @@ def bestClef(streamObj: 'music21.stream.Stream',
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
@@ -881,6 +878,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/common/fileTools.py b/music21/common/fileTools.py
index cfa908b41..c55d01937 100644
--- a/music21/common/fileTools.py
+++ b/music21/common/fileTools.py
@@ -30,6 +30,8 @@ __all__ = [
     'readFileEncodingSafe',
     'readPickleGzip',
     'cd',
+    'preparePathClassesForUnpickling',
+    'restorePathClassesAfterUnpickling',
 ]
 
 
@@ -60,6 +62,7 @@ def readPickleGzip(filePath: Union[str, pathlib.Path]) -> Any:
     Read a gzip-compressed pickle file, uncompress it, unpickle it, and
     return the contents.
     '''
+    preparePathClassesForUnpickling()
     with gzip.open(filePath, 'rb') as pickledFile:
         try:
             uncompressed = pickledFile.read()
@@ -68,8 +71,10 @@ def readPickleGzip(filePath: Union[str, pathlib.Path]) -> Any:
             # pickle exceptions cannot be caught directly
             # because they might come from pickle or _pickle and the latter cannot
             # be caught.
+            restorePathClassesAfterUnpickling()
             raise Music21Exception('Cannot load file ' + str(filePath)) from e
 
+    restorePathClassesAfterUnpickling()
     return newMdb
 
 def readFileEncodingSafe(filePath, firstGuess='utf-8'):
@@ -122,8 +127,35 @@ def readFileEncodingSafe(filePath, firstGuess='utf-8'):
     # might also raise FileNotFoundError, but let that bubble
 
 
-# -----------------------------------------------------------------------------
+_storedPathlibClasses = {'posixPath': pathlib.PosixPath, 'windowsPath': pathlib.WindowsPath}
+
+def preparePathClassesForUnpickling():
+    '''
+    When we need to unpickle a function that might have relative paths
+    (like some music21 stream options), Windows chokes if the PosixPath
+    is not defined, but usually can still unpickle easily.
+    '''
+    from music21.common.misc import getPlatform
+    platform = getPlatform()
+    if platform == 'win':
+        pathlib.PosixPath = pathlib.WindowsPath
+    else:
+        pathlib.WindowsPath = pathlib.PosixPath
+
 
+def restorePathClassesAfterUnpickling():
+    '''
+    After unpickling, leave pathlib alone.
+    '''
+    from music21.common.misc import getPlatform
+    platform = getPlatform()
+    if platform == 'win':
+        pathlib.PosixPath = _storedPathlibClasses['posixPath']
+    else:
+        pathlib.WindowsPath = _storedPathlibClasses['windowsPath']
+
+
+# -----------------------------------------------------------------------------
 if __name__ == '__main__':
     import music21
     music21.mainTest()
diff --git a/music21/common/numberTools.py b/music21/common/numberTools.py
index 76fb1ac4a..19904ed4e 100644
--- a/music21/common/numberTools.py
+++ b/music21/common/numberTools.py
@@ -1141,9 +1141,6 @@ class Test(unittest.TestCase):
     Tests not requiring file output.
     '''
 
-    def runTest(self):
-        pass
-
     def setUp(self):
         pass
 
diff --git a/music21/configure.py b/music21/configure.py
index 1975128f5..9d1164bc3 100644
--- a/music21/configure.py
+++ b/music21/configure.py
@@ -1590,9 +1590,6 @@ _DOC_ORDER = []
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testYesOrNo(self):
         print()
         environLocal.printDebug(['starting: YesOrNo()'])
@@ -1661,9 +1658,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testYesOrNo(self):
         from music21 import configure
         d = configure.YesOrNo(default=True, tryAgain=False,
diff --git a/music21/converter/__init__.py b/music21/converter/__init__.py
index f09c3518e..443d420b2 100644
--- a/music21/converter/__init__.py
+++ b/music21/converter/__init__.py
@@ -46,7 +46,7 @@ import unittest
 import urllib
 import zipfile
 
-from typing import Union
+from typing import Union, Tuple
 
 __all__ = [
     'subConverters', 'ArchiveManagerException', 'PickleFilterException',
@@ -298,7 +298,7 @@ class PickleFilter:
         if pickleFp.exists():
             os.remove(pickleFp)
 
-    def status(self):
+    def status(self) -> Tuple[pathlib.Path, bool, pathlib.Path]:
         '''
         Given a file path specified with __init__, look for an up to date pickled
         version of this file path. If it exists, return its fp, otherwise return the
@@ -388,6 +388,7 @@ def registerSubconverter(newSubConverter):
 
 
 def unregisterSubconverter(removeSubconverter):
+    # noinspection PyShadowingNames
     '''
     Remove a Subconverter from the list of registered subconverters.
 
@@ -498,6 +499,7 @@ class Converter:
         self.stream.fileFormat = useFormat
 
     def getFormatFromFileExtension(self, fp):
+        # noinspection PyShadowingNames
         '''
         gets the format from a file extension.
 
@@ -804,6 +806,7 @@ class Converter:
         defaultSubconverters = []
         for i in sorted(subConverters.__dict__):
             name = getattr(subConverters, i)
+            # noinspection PyTypeChecker
             if (callable(name)
                     and not isinstance(name, types.FunctionType)
                     and hasattr(name, '__mro__')   # Typing imports break this.
@@ -1159,7 +1162,8 @@ def parse(value: Union[bundles.MetadataEntry, bytes, str, pathlib.Path],
         return parseData(value, number=number, format=m21Format, **keywords)
 
 
-def freeze(streamObj, fmt=None, fp=None, fastButUnsafe=False, zipType='zlib'):
+def freeze(streamObj, fmt=None, fp=None, fastButUnsafe=False, zipType='zlib') -> pathlib.Path:
+    # noinspection PyShadowingNames
     '''Given a StreamObject and a file path, serialize and store the Stream to a file.
 
     This function is based on the :class:`~music21.converter.StreamFreezer` object.
@@ -1171,7 +1175,6 @@ def freeze(streamObj, fmt=None, fp=None, fastButUnsafe=False, zipType='zlib'):
 
     The file path is returned.
 
-
     >>> c = converter.parse('tinynotation: 4/4 c4 d e f')
     >>> c.show('text')
     {0.0} <music21.stream.Measure 1 offset=0.0>
@@ -1184,7 +1187,7 @@ def freeze(streamObj, fmt=None, fp=None, fastButUnsafe=False, zipType='zlib'):
         {4.0} <music21.bar.Barline type=final>
     >>> fp = converter.freeze(c, fmt='pickle')
     >>> #_DOCS_SHOW fp
-    '/tmp/music21/sjiwoe.pgz'
+    PosixPath('/tmp/music21/sjiwoe.pgz')
 
     The file can then be "thawed" back into a Stream using the
     :func:`~music21.converter.thaw` method.
@@ -1271,9 +1274,6 @@ def thawStr(strData):
 class TestExternal(unittest.TestCase):  # pragma: no cover
     # interpreter loading
 
-    def runTest(self):
-        pass
-
     def testMusicXMLConversion(self):
         from music21.musicxml import testFiles
         for mxString in testFiles.ALL:  # @UndefinedVariable
@@ -1342,9 +1342,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
@@ -1356,6 +1353,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             obj = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(obj) and not isinstance(obj, types.FunctionType):
                 i = copy.copy(obj)
                 j = copy.deepcopy(obj)
diff --git a/music21/converter/subConverters.py b/music21/converter/subConverters.py
index 520ce0154..9c0cee55b 100644
--- a/music21/converter/subConverters.py
+++ b/music21/converter/subConverters.py
@@ -1422,8 +1422,6 @@ class Test(unittest.TestCase):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testXMLShow(self):
         from music21 import corpus
diff --git a/music21/corpus/chorales.py b/music21/corpus/chorales.py
index 1f16d7966..6fdab3523 100644
--- a/music21/corpus/chorales.py
+++ b/music21/corpus/chorales.py
@@ -1760,15 +1760,10 @@ class BachException(exceptions21.Music21Exception):
 
 
 # class Test(unittest.TestCase):
-#
-#     def runTest(self):
-#         pass
+#     pass
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testGetRiemenschneider1(self):
         from music21 import corpus
         for chorale in corpus.chorales.Iterator(1, 2,
diff --git a/music21/corpus/corpora.py b/music21/corpus/corpora.py
index ae41c4519..15683105f 100644
--- a/music21/corpus/corpora.py
+++ b/music21/corpus/corpora.py
@@ -480,9 +480,7 @@ class Corpus(prebase.ProtoM21Object):
         [<music21.corpus.work.DirectoryInformation bach>,
          <music21.corpus.work.DirectoryInformation beach>]
                  '''
-        results = [di for di in self.directoryInformation]
-
-        return results
+        return list(self.directoryInformation)
 
 # -----------------------------------------------------------------------------
 
diff --git a/music21/corpus/manager.py b/music21/corpus/manager.py
index 289742fd5..d9b996d71 100644
--- a/music21/corpus/manager.py
+++ b/music21/corpus/manager.py
@@ -323,6 +323,8 @@ def cacheMetadataBundleFromDisk(corpusObject):
         metadataBundle = metadata.bundles.MetadataBundle(corpusName)
         metadataBundle.read()
         metadataBundle.validate()
+        # _metadataBundles needs TypedDict.
+        # noinspection PyTypeChecker
         _metadataBundles[corpusName] = metadataBundle
 
 
diff --git a/music21/corpus/virtual.py b/music21/corpus/virtual.py
index 11f5d0cee..fd24401f8 100644
--- a/music21/corpus/virtual.py
+++ b/music21/corpus/virtual.py
@@ -10,7 +10,7 @@
 # ------------------------------------------------------------------------------
 '''
 The virtual.py module is a library of references to remotely stored music data files,
-as well as meta-data necessary to download and, if available, access an already downloaded file.
+as well as metadata necessary to download and, if available, access an already downloaded file.
 
 This is probably not the best way to handle this -- instead it should be a link to
 a directory or listing of files and repositories, each handled as a different Corpus.
@@ -185,13 +185,10 @@ class PachelbelCanonD(VirtualWork):
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
     # interpreter loading
-    def runTest(self):
-        pass
+    pass
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testBasic(self):
         '''Test copying all objects defined in the virtual corpus module
diff --git a/music21/derivation.py b/music21/derivation.py
index 83bed9716..cbe5268a2 100644
--- a/music21/derivation.py
+++ b/music21/derivation.py
@@ -299,9 +299,7 @@ class Derivation(SlottedObjectMixin):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/duration.py b/music21/duration.py
index b2962966d..26465e337 100644
--- a/music21/duration.py
+++ b/music21/duration.py
@@ -3217,9 +3217,6 @@ class TupletFixer:
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testSingle(self):
         from music21 import note
         a = Duration()
@@ -3249,9 +3246,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
diff --git a/music21/dynamics.py b/music21/dynamics.py
index 7fb749d94..b39e16f4a 100644
--- a/music21/dynamics.py
+++ b/music21/dynamics.py
@@ -393,9 +393,6 @@ class Diminuendo(DynamicWedge):
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testSingle(self):
         a = Dynamic('ffff')
         a.show()
@@ -416,9 +413,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
diff --git a/music21/editorial.py b/music21/editorial.py
index 8a05dbe0d..eeda23a27 100644
--- a/music21/editorial.py
+++ b/music21/editorial.py
@@ -11,7 +11,7 @@
 # License:      BSD, see license.txt
 # -----------------------------------------------------------------------------
 '''
-Editorial objects store comments and other meta-data associated with specific
+Editorial objects store comments and other metadata associated with specific
 :class:`~music21.base.Music21Object` elements such as Notes.
 
 Some of the aspects of :class:`~music21.editorial.Editorial` objects
@@ -32,10 +32,10 @@ the object, call `.hasEditorialInformation`
 >>> n = note.Note('C#4')
 >>> n.hasEditorialInformation
 False
+
 >>> n.editorial.unedited = True
 >>> n.hasEditorialInformation
 True
-
 '''
 import unittest
 from music21 import exceptions21
@@ -85,7 +85,6 @@ class Editorial(prebase.ProtoM21Object, dict):
         :width: 103
 
     '''
-
     _DOC_ATTR = {
         'comments': '''
             a list of :class:`~music21.editorial.Comment` objects that represent any comments
@@ -166,7 +165,6 @@ class Comment(prebase.ProtoM21Object, style.StyleMixin):
     >>> c.style.color
     'red'
     '''
-
     def __init__(self, text=None):
         super().__init__()
         self.text = text
@@ -188,9 +186,6 @@ class Comment(prebase.ProtoM21Object, style.StyleMixin):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
@@ -206,6 +201,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/expressions.py b/music21/expressions.py
index 9a46f0894..2ca2f1bfe 100644
--- a/music21/expressions.py
+++ b/music21/expressions.py
@@ -1219,9 +1219,6 @@ class TremoloSpanner(spanner.Spanner):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def x_testRealize(self):
         from music21 import note
         from music21 import stream
diff --git a/music21/features/base.py b/music21/features/base.py
index 0f8d9e4e6..814c58f91 100644
--- a/music21/features/base.py
+++ b/music21/features/base.py
@@ -1286,22 +1286,6 @@ def getIndex(featureString, extractorType=None):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
-#    def testGetAllExtractorsMethods(self):
-#        '''
-#        ahh..this test takes a really long time....
-#        '''
-#        from music21 import stream, features, pitch
-#        s = corpus.parse('bwv66.6').measures(1, 5)
-#        self.assertEqual( len(features.alljSymbolicFeatures(s)), 70)
-#        self.assertEqual(len (features.allNativeFeatures(s)),21)
-#        self.assertEqual(str(features.alljSymbolicVectors(s)[1:5]),
-# '[[2.6630434782608696], [2], [2], [0.391304347826087]]')
-#        self.assertEqual(str(features.allNativeVectors(s)[0:4]),
-# '[[1], [1.0328322202181006], [2], [1.0]]')
-
     def testStreamFormsA(self):
 
         from music21 import features
@@ -1514,6 +1498,19 @@ class Test(unittest.TestCase):
     # --------------------------------------------------------------------------
     # silent tests
 
+#    def testGetAllExtractorsMethods(self):
+#        '''
+#        ahh..this test takes a really long time....
+#        '''
+#        from music21 import stream, features, pitch
+#        s = corpus.parse('bwv66.6').measures(1, 5)
+#        self.assertEqual( len(features.alljSymbolicFeatures(s)), 70)
+#        self.assertEqual(len (features.allNativeFeatures(s)),21)
+#        self.assertEqual(str(features.alljSymbolicVectors(s)[1:5]),
+# '[[2.6630434782608696], [2], [2], [0.391304347826087]]')
+#        self.assertEqual(str(features.allNativeVectors(s)[0:4]),
+# '[[1], [1.0328322202181006], [2], [1.0]]')
+
     def x_testComposerClassificationJSymbolic(self):  # pragma: no cover
         '''
         Demonstrating writing out data files for feature extraction. Here,
diff --git a/music21/features/jSymbolic.py b/music21/features/jSymbolic.py
index 1a8f443d9..c23006c9a 100644
--- a/music21/features/jSymbolic.py
+++ b/music21/features/jSymbolic.py
@@ -4440,9 +4440,6 @@ def getCompletionStats():
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testAverageMelodicIntervalFeature(self):
         from music21 import stream, pitch, note, features
         s = stream.Stream()
diff --git a/music21/features/native.py b/music21/features/native.py
index 71c8a71a5..f775ed9c7 100644
--- a/music21/features/native.py
+++ b/music21/features/native.py
@@ -1037,9 +1037,6 @@ featureExtractors = [
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testIncorrectlySpelledTriadPrevalence(self):
         from music21 import stream, features, chord
 
diff --git a/music21/figuredBass/checker.py b/music21/figuredBass/checker.py
index 13d2969f0..1cdb6ea45 100644
--- a/music21/figuredBass/checker.py
+++ b/music21/figuredBass/checker.py
@@ -736,9 +736,7 @@ _DOC_ORDER = [extractHarmonies, getVoiceLeadingMoments,
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/examples.py b/music21/figuredBass/examples.py
index 3046e5a3a..84884f8c2 100644
--- a/music21/figuredBass/examples.py
+++ b/music21/figuredBass/examples.py
@@ -507,9 +507,7 @@ _DOC_ORDER = [exampleA, exampleB, exampleC, exampleD, V43ResolutionExample,
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py
index 3dedc1eac..881d27eef 100644
--- a/music21/figuredBass/notation.py
+++ b/music21/figuredBass/notation.py
@@ -602,9 +602,7 @@ _DOC_ORDER = [Notation, Figure, Modifier]
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/possibility.py b/music21/figuredBass/possibility.py
index f5b1fd948..7a5997e5b 100644
--- a/music21/figuredBass/possibility.py
+++ b/music21/figuredBass/possibility.py
@@ -1000,9 +1000,7 @@ class PossibilityException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/realizer.py b/music21/figuredBass/realizer.py
index 0246a760e..a8b874e06 100644
--- a/music21/figuredBass/realizer.py
+++ b/music21/figuredBass/realizer.py
@@ -793,9 +793,7 @@ class FiguredBassLineException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/realizerScale.py b/music21/figuredBass/realizerScale.py
index 38388a489..50e36524c 100644
--- a/music21/figuredBass/realizerScale.py
+++ b/music21/figuredBass/realizerScale.py
@@ -202,9 +202,7 @@ class FiguredBassScaleException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/resolution.py b/music21/figuredBass/resolution.py
index 9d4d5cab3..8a2e753d2 100644
--- a/music21/figuredBass/resolution.py
+++ b/music21/figuredBass/resolution.py
@@ -789,9 +789,7 @@ class ResolutionException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/rules.py b/music21/figuredBass/rules.py
index 29e8aa3b5..5753abb64 100644
--- a/music21/figuredBass/rules.py
+++ b/music21/figuredBass/rules.py
@@ -182,9 +182,7 @@ class FiguredBassRulesException(exceptions21.Music21Exception):
 
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/figuredBass/segment.py b/music21/figuredBass/segment.py
index 9d8bf24b3..314e72d36 100644
--- a/music21/figuredBass/segment.py
+++ b/music21/figuredBass/segment.py
@@ -1018,9 +1018,7 @@ class SegmentException(exceptions21.Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py
index 69ad031d8..df7795904 100644
--- a/music21/freezeThaw.py
+++ b/music21/freezeThaw.py
@@ -75,7 +75,7 @@ import time
 import unittest
 import zlib
 
-from typing import Union
+from typing import Union, List
 
 from music21 import base
 from music21 import common
@@ -144,8 +144,6 @@ class StreamFreezer(StreamFreezeThawBase):
     Use the :func:`~music21.converter.unfreeze` to read from a
     serialized file
 
-    >>> from music21 import freezeThaw
-
     >>> s = stream.Stream()
     >>> s.repeatAppend(note.Note('C4'), 8)
     >>> temp = [s[n].transpose(n, inPlace=True) for n in range(len(s))]
@@ -215,7 +213,6 @@ class StreamFreezer(StreamFreezeThawBase):
         self.streamIds = streamIds
 
         self.subStreamFreezers = {}  # this will keep track of sub freezers for spanners
-        #
 
         if streamObj is not None and fastButUnsafe is False:
             # deepcopy necessary because we mangle sites in the objects
@@ -260,8 +257,6 @@ class StreamFreezer(StreamFreezeThawBase):
         attribute `fastButUnsafe = True` setting of StreamFreezer ignores the destructive
         effects of these processes and skips the deepcopy.
 
-        >>> from music21 import freezeThaw
-
         >>> a = stream.Stream()
         >>> n = note.Note()
         >>> n.duration.type = 'whole'
@@ -470,8 +465,8 @@ class StreamFreezer(StreamFreezeThawBase):
                 self.setupStoredElementOffsetTuples(e)
             e.sites.remove(streamObj)
             e.activeSite = None
-#                e._preFreezeId = id(e)
-#                elementDict[id(e)] = s.elementOffset(e)
+            # e._preFreezeId = id(e)
+            # elementDict[id(e)] = s.elementOffset(e)
         for e in streamObj._endElements:
             elementTuple = (e, 'end')
             storedElementOffsetTuples.append(elementTuple)
@@ -487,12 +482,15 @@ class StreamFreezer(StreamFreezeThawBase):
         streamObj._endElements = []
         streamObj.coreElementsChanged()
 
-    def findActiveStreamIdsInHierarchy(self,
-                                       hierarchyObject=None,
-                                       getSpanners=True,
-                                       getVariants=True):
+    def findActiveStreamIdsInHierarchy(
+        self,
+        hierarchyObject=None,
+        getSpanners=True,
+        getVariants=True
+    ) -> List[int]:
         '''
-        Return a list of all Stream ids anywhere in the hierarchy.
+        Return a list of all Stream ids anywhere in the hierarchy.  By id,
+        we mean `id(s)` not `s.id` -- so they are memory locations and unique.
 
         Stores them in .streamIds.
 
@@ -604,9 +602,8 @@ class StreamFreezer(StreamFreezeThawBase):
     # --------------------------------------------------------------------------
 
     def parseWriteFmt(self, fmt):
-        '''Parse a passed-in write format
-
-        >>> from music21 import freezeThaw
+        '''
+        Parse a passed-in write format
 
         >>> sf = freezeThaw.StreamFreezer()
         >>> sf.parseWriteFmt(None)
@@ -659,10 +656,10 @@ class StreamFreezer(StreamFreezeThawBase):
         environLocal.printDebug(['writing fp', str(fp)])
 
         if fmt == 'pickle':
-            # a negative protocol value will get the highest protocol;
-            # this is generally desirable
+            # previously used highest protocol, but now protocols are changing too
+            # fast, and might not be compatible for sharing.
             # packStream() returns a storage dictionary
-            pickleString = pickle.dumps(storage, protocol=pickle.HIGHEST_PROTOCOL)
+            pickleString = pickle.dumps(storage)
             if zipType == 'zlib':
                 pickleString = zlib.compress(pickleString)
 
@@ -711,8 +708,6 @@ class StreamThawer(StreamFreezeThawBase):
     In general user :func:`~music21.converter.parse` to read from a
     serialized file.
 
-    >>> from music21 import freezeThaw
-
     >>> s = stream.Stream()
     >>> s.repeatAppend(note.Note('C4'), 8)
     >>> temp = [s[n].transpose(n, inPlace=True) for n in range(len(s))]
@@ -746,9 +741,6 @@ class StreamThawer(StreamFreezeThawBase):
 
         If streamObj is None, runs it on the embedded stream
 
-        >>> from music21 import freezeThaw
-
-
         >>> a = stream.Stream()
         >>> n = note.Note()
         >>> n.duration.type = 'whole'
@@ -928,6 +920,7 @@ class StreamThawer(StreamFreezeThawBase):
 
         fmt = self.parseOpenFmt(fileData)
         if fmt == 'pickle':
+            common.restorePathClassesAfterUnpickling()
             # environLocal.printDebug(['opening fp', fp])
             f = open(fp, 'rb')
             if zipType is None:
@@ -938,11 +931,14 @@ class StreamThawer(StreamFreezeThawBase):
                 try:
                     storage = pickle.loads(uncompressed)
                 except AttributeError as e:
+                    common.restoreWindowsAfterUnpickling()
                     raise FreezeThawException(
                         f'Problem in decoding: {e}'
                     ) from e
             else:
+                common.restorePathClassesAfterUnpickling()
                 raise FreezeThawException(f'Unknown zipType {zipType}')
+            common.restorePathClassesAfterUnpickling()
             f.close()
         elif fmt == 'jsonpickle':
             import jsonpickle
@@ -985,9 +981,6 @@ class StreamThawer(StreamFreezeThawBase):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testSimpleFreezeThaw(self):
         from music21 import stream, note
         s = stream.Stream()
@@ -1161,7 +1154,6 @@ class Test(unittest.TestCase):
             n = note.Note(pitchName)
             n.duration.type = durType
             m.append(n)
-#            stream2.append(n)
         stream2.append(m)
         # c.show('t')
         variant.addVariant(c.parts[0], 6.0, stream2,
diff --git a/music21/graph/__init__.py b/music21/graph/__init__.py
index b35c74013..21d86b3da 100644
--- a/music21/graph/__init__.py
+++ b/music21/graph/__init__.py
@@ -130,9 +130,6 @@ def plotStream(
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testAll(self):
         from music21 import corpus, dynamics
         a = corpus.parse('bach/bwv57.8')
@@ -143,9 +140,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
@@ -160,6 +154,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/graph/findPlot.py b/music21/graph/findPlot.py
index 3d925d7d3..9288b3726 100644
--- a/music21/graph/findPlot.py
+++ b/music21/graph/findPlot.py
@@ -60,6 +60,7 @@ def getPlotClasses():
     allPlot = []
     for i in sorted(plot.__dict__):
         name = getattr(plot, i)
+        # noinspection PyTypeChecker
         if (callable(name)
                 and not isinstance(name, types.FunctionType)
                 and plot.PlotStreamMixin in name.__mro__
@@ -82,6 +83,7 @@ def getAxisClasses():
     allAxis = []
     for i in sorted(axis.__dict__):
         name = getattr(axis, i)
+        # noinspection PyTypeChecker
         if (callable(name)
                 and not isinstance(name, types.FunctionType)
                 and axis.Axis in name.__mro__):
diff --git a/music21/graph/plot.py b/music21/graph/plot.py
index 94c32978e..3e13a622b 100644
--- a/music21/graph/plot.py
+++ b/music21/graph/plot.py
@@ -82,13 +82,14 @@ class PlotStreamMixin(prebase.ProtoM21Object):
         self.savedKeywords = keywords
 
     def _reprInternal(self) -> str:
+        # noinspection PyShadowingNames
         '''
         The representation of the Plot shows the stream repr
         in addition to the class name.
 
-        >>> s = stream.Stream()
-        >>> s.id = 'empty'
-        >>> plot = graph.plot.ScatterPitchClassQuarterLength(s)
+        >>> st = stream.Stream()
+        >>> st.id = 'empty'
+        >>> plot = graph.plot.ScatterPitchClassQuarterLength(st)
         >>> plot
         <music21.graph.plot.ScatterPitchClassQuarterLength for <music21.stream.Stream empty>>
 
@@ -948,7 +949,6 @@ class WindowedKey(WindowedAnalysis):
     Set the processor class to one of the following for different uses:
 
     >>> p = graph.plot.WindowedKey(s.parts[0])
-    >>> p.processorClass = analysis.discrete.KrumhanslKessler
     >>> p.processorClass = analysis.discrete.AardenEssen
     >>> p.processorClass = analysis.discrete.SimpleWeights
     >>> p.processorClass = analysis.discrete.BellmanBudge
@@ -1584,9 +1584,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
@@ -1602,6 +1599,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/graph/primitives.py b/music21/graph/primitives.py
index b9b6421e7..41032079b 100644
--- a/music21/graph/primitives.py
+++ b/music21/graph/primitives.py
@@ -1565,9 +1565,6 @@ class Graph3DBars(Graph):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
@@ -1595,9 +1592,6 @@ class Test(unittest.TestCase):
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         a = GraphScatter(doneAction=None, title='x to x*x', alpha=1)
         data = [(x, x * x) for x in range(50)]
diff --git a/music21/harmony.py b/music21/harmony.py
index 2d6531058..486d74082 100644
--- a/music21/harmony.py
+++ b/music21/harmony.py
@@ -438,16 +438,27 @@ class Harmony(chord.Chord):
 
     # PUBLIC METHODS #
 
-    def addChordStepModification(self, degree):
+    def addChordStepModification(self, degree, *, updatePitches=False):
         '''Add a harmony degree specification to this Harmony as a
         :class:`~music21.harmony.ChordStepModification` object.
 
+        TODO: updatePitches will default True in v.7
+
         >>> hd = harmony.ChordStepModification('add', 4)
         >>> h = harmony.ChordSymbol()
         >>> h.addChordStepModification(hd)
         >>> h.addChordStepModification('juicy')
         Traceback (most recent call last):
         music21.harmony.HarmonyException: cannot add this object as a degree: juicy
+
+        Alteration will also impact the pitches,
+        if the keyword argument updatePitches is given as True
+
+        >>> h = harmony.ChordSymbol('C')
+        >>> mod = harmony.ChordStepModification('alter', 5, -1)
+        >>> h.addChordStepModification(mod, updatePitches=True)
+        >>> h.pitches
+        (<music21.pitch.Pitch C3>, <music21.pitch.Pitch E3>, <music21.pitch.Pitch G-3>)
         '''
         if not isinstance(degree, ChordStepModification):
             # TODO: possibly create ChordStepModification objects from other
@@ -456,6 +467,8 @@ class Harmony(chord.Chord):
                 'cannot add this object as a degree: {0}'.format(degree))
 
         self.chordStepModifications.append(degree)
+        if(updatePitches):
+            self._updatePitches()
 
     def findFigure(self):
         return 'No Figure Representation'
@@ -1701,7 +1714,7 @@ class ChordSymbol(Harmony):
                 degree = degree.replace('A', '')  # A is for 'Altered'
                 if hD.degree == int(degree):
                     # transpose by semitones (positive for up, negative for down)
-                    p = p.transpose(hD.interval)
+                    p = p.transpose(hD.interval, inPlace=True)
                     pitchFound = True
 
                     # for degreeString in self._degreesList:
@@ -1827,15 +1840,18 @@ class ChordSymbol(Harmony):
         # priority since there is no well defined nomenclature
         if 'add' in remaining:
             degree = remaining[remaining.index('add') + 3:]
-            self.addChordStepModification(ChordStepModification('add', int(degree)))
+            self.addChordStepModification(
+                ChordStepModification('add', int(degree)), updatePitches=False)
             return
         if 'alter' in remaining:
             degree = remaining[remaining.index('alter') + 5:]
-            self.addChordStepModification(ChordStepModification('alter', int(degree)))
+            self.addChordStepModification(
+                ChordStepModification('alter', int(degree)), updatePitches=False)
             return
         if 'omit' in remaining or 'subtract' in remaining:
             degree = remaining[remaining.index('omit') + 4:]
-            self.addChordStepModification(ChordStepModification('subtract', int(degree)))
+            self.addChordStepModification(
+                ChordStepModification('subtract', int(degree)), updatePitches=False)
             return
 
         st = st.replace(',', '')
@@ -1896,7 +1912,7 @@ class ChordSymbol(Harmony):
 
         for degree, alterBy in degrees:
             self.addChordStepModification(
-                ChordStepModification('add', degree, alterBy))
+                ChordStepModification('add', degree, alterBy), updatePitches=False)
 
     def _updatePitches(self):
         '''
@@ -2441,9 +2457,6 @@ def realizeChordSymbolDurations(piece):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testChordAttributes(self):
         from music21 import harmony
         cs = harmony.ChordSymbol('Cm')
@@ -2584,9 +2597,6 @@ class Test(unittest.TestCase):
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testReadInXML(self):
         from music21 import harmony
         from music21 import corpus, stream
diff --git a/music21/humdrum/harmparser.py b/music21/humdrum/harmparser.py
index f9dbb9132..6752e91c3 100644
--- a/music21/humdrum/harmparser.py
+++ b/music21/humdrum/harmparser.py
@@ -233,8 +233,6 @@ class HarmParser:
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testTriads(self):
         # noinspection SpellCheckingInspection
diff --git a/music21/humdrum/questions.py b/music21/humdrum/questions.py
index ff7f711cf..b08ac752e 100644
--- a/music21/humdrum/questions.py
+++ b/music21/humdrum/questions.py
@@ -13,7 +13,7 @@ _DOC_IGNORE_MODULE_OR_PACKAGE = True
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
+    def xtest001(self):
         pass
 
     # thoughts on how to do this...
diff --git a/music21/humdrum/spineParser.py b/music21/humdrum/spineParser.py
index a6d8a5dcd..f23466e14 100644
--- a/music21/humdrum/spineParser.py
+++ b/music21/humdrum/spineParser.py
@@ -2770,8 +2770,6 @@ class GlobalReference(base.Music21Object):
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testLoadMazurka(self):
         # hf1 = HumdrumFile('d:/web/eclipse/music21misc/mazurka06-2.krn')
@@ -3117,9 +3115,6 @@ class Test(unittest.TestCase):
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testShowSousa(self):
         hf1 = HumdrumDataCollection(testFiles.sousaStars)
         hf1.parse()
diff --git a/music21/instrument.py b/music21/instrument.py
index 6afd2b332..804e61808 100644
--- a/music21/instrument.py
+++ b/music21/instrument.py
@@ -2121,16 +2121,11 @@ def fromString(instrumentString):
 
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
-
-    def runTest(self):
-        pass
+    pass
 
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
@@ -2143,6 +2138,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/interval.py b/music21/interval.py
index 17e411f1a..0e20f991d 100644
--- a/music21/interval.py
+++ b/music21/interval.py
@@ -3927,9 +3927,6 @@ def subtract(intervalList):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testFirst(self):
         from music21.note import Note
         from music21.pitch import Accidental
diff --git a/music21/key.py b/music21/key.py
index d88dd9c3a..30ece2582 100644
--- a/music21/key.py
+++ b/music21/key.py
@@ -1184,9 +1184,6 @@ class Key(KeySignature, scale.DiatonicScale):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
diff --git a/music21/languageExcerpts/naturalLanguageObjects.py b/music21/languageExcerpts/naturalLanguageObjects.py
index f2fbd8892..469ef1b0d 100644
--- a/music21/languageExcerpts/naturalLanguageObjects.py
+++ b/music21/languageExcerpts/naturalLanguageObjects.py
@@ -129,9 +129,6 @@ def toChord(pitchArray, languageString):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testConvertPitches(self):
         # testing defaults in case of invalid language and invalid input
         self.assertEqual('<music21.pitch.Pitch C>', repr(toPitch('hello', '')))
diff --git a/music21/layout.py b/music21/layout.py
index 69e6b267d..97681b079 100644
--- a/music21/layout.py
+++ b/music21/layout.py
@@ -1582,9 +1582,6 @@ _DOC_ORDER = [ScoreLayout, PageLayout, SystemLayout, StaffLayout, LayoutBase,
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         from music21 import note
         from music21.musicxml import m21ToXml
diff --git a/music21/mei/base.py b/music21/mei/base.py
index 4fcd10390..4e47876d3 100644
--- a/music21/mei/base.py
+++ b/music21/mei/base.py
@@ -174,7 +174,7 @@ tool.
 
 '''
 # pylint: disable=misplaced-comparison-constant
-
+from typing import Optional, Union
 from xml.etree import ElementTree as ETree
 
 from collections import defaultdict
@@ -340,20 +340,22 @@ class MeiToM21Converter:
 
 # Module-level Functions
 # -----------------------------------------------------------------------------
-def safePitch(name, accidental=None, octave=''):
+def safePitch(
+    name: str,
+    accidental: Optional[str] = None,
+    octave: Union[str, int] = ''
+) -> pitch.Pitch:
     '''
     Safely build a :class:`Pitch` from a string.
 
     When :meth:`Pitch.__init__` is given an empty string, it raises a :exc:`PitchException`. This
     function instead returns a default :class:`Pitch` instance.
 
-    :param str name: Desired name of the :class:`Pitch`.
-    :param str accidental: (Optional) Symbol for the accidental.
-    :param octave: (Optional) Octave number.
-    :type octave: str or int
+    name: Desired name of the :class:`Pitch`.
+    accidental: (Optional) Symbol for the accidental.
+    octave: (Optional) Octave number.
 
-    :returns: A :class:`Pitch` with the appropriate properties.
-    :rtype: :class:`music21.pitch.Pitch`
+    Returns A :class:`Pitch` with the appropriate properties.
 
     >>> from music21.mei.base import safePitch  # OMIT_FROM_DOCS
     >>> safePitch('D#6')
diff --git a/music21/metadata/__init__.py b/music21/metadata/__init__.py
index b5a19fd5a..464d104a5 100644
--- a/music21/metadata/__init__.py
+++ b/music21/metadata/__init__.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Name:         metadata.py
-# Purpose:      music21 classes for representing score and work meta-data
+# Purpose:      music21 classes for representing score and work metadata
 #
 # Authors:      Christopher Ariza
 #               Michael Scott Cuthbert
@@ -1282,8 +1282,7 @@ class RichMetadata(Metadata):
 # -----------------------------------------------------------------------------
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/metadata/bundles.py b/music21/metadata/bundles.py
index 81de3536e..386b3c076 100644
--- a/music21/metadata/bundles.py
+++ b/music21/metadata/bundles.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Name:         bundles.py
-# Purpose:      music21 classes for representing score and work meta-data
+# Purpose:      music21 classes for representing score and work metadata
 #
 # Authors:      Christopher Ariza
 #               Michael Scott Cuthbert
@@ -1377,9 +1377,6 @@ class MetadataBundle(prebase.ProtoM21Object):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testOneFromCorpus(self):
         from music21.corpus.corpora import CoreCorpus
         cc = CoreCorpus()
diff --git a/music21/metadata/caching.py b/music21/metadata/caching.py
index a6c6d952a..420b3f7c8 100644
--- a/music21/metadata/caching.py
+++ b/music21/metadata/caching.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Name:         caching.py
-# Purpose:      music21 classes for representing score and work meta-data
+# Purpose:      music21 classes for representing score and work metadata
 #
 # Authors:      Christopher Ariza
 #               Michael Scott Cuthbert
@@ -348,7 +348,7 @@ class JobProcessor:
             worker.start()
         if jobs:
             for job in jobs:
-                job_queue.put(pickle.dumps(job, protocol=pickle.HIGHEST_PROTOCOL))
+                job_queue.put(pickle.dumps(job))  # do not use highest protocol to generate.
             for unused_jobCounter in range(len(jobs)):
                 job = pickle.loads(result_queue.get())
                 results = job.getResults()
@@ -423,8 +423,7 @@ class WorkerProcess(multiprocessing.Process):  # @UndefinedVariable pylint: disa
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/metadata/primitives.py b/music21/metadata/primitives.py
index f0984b559..1e2ef9e91 100644
--- a/music21/metadata/primitives.py
+++ b/music21/metadata/primitives.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Name:         primitives.py
-# Purpose:      music21 classes for representing score and work meta-data
+# Purpose:      music21 classes for representing score and work metadata
 #
 # Authors:      Christopher Ariza
 #               Michael Scott Cuthbert
@@ -1177,9 +1177,6 @@ class Imprint(prebase.ProtoM21Object):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testText(self):
         from music21 import metadata
 
diff --git a/music21/meter.py b/music21/meter.py
index de9a2d32c..8232ee876 100644
--- a/music21/meter.py
+++ b/music21/meter.py
@@ -4410,8 +4410,6 @@ class SenzaMisuraTimeSignature(base.Music21Object):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testSingle(self):
         '''Need to test direct meter creation w/o stream
@@ -4464,14 +4462,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 
 
 class Test(unittest.TestCase):
-    '''Unit tests
-    '''
-
-    def runTest(self):
-        pass
-
-    def setUp(self):
-        pass
 
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
diff --git a/music21/midi/__init__.py b/music21/midi/__init__.py
index 22fec44c4..009fce244 100644
--- a/music21/midi/__init__.py
+++ b/music21/midi/__init__.py
@@ -708,6 +708,8 @@ class MidiEvent:
             d2 = d1
             d1 = 0
 
+
+
         # environLocal.printDebug(['got target char value', charValue,
         # 'getVariableLengthNumber(charValue)', getVariableLengthNumber(charValue)[0],
         # 'd1', d1, 'd2', d2,])
@@ -726,10 +728,10 @@ class MidiEvent:
         Demonstration.  First let's create a helper function and a MidiEvent:
 
         >>> to_bytes = midi.intsToHexBytes
-        >>> midiBytes = to_bytes([0x90, 60, 120])
-        >>> midiBytes
+        >>> midBytes = to_bytes([0x90, 60, 120])
+        >>> midBytes
         b'\x90<x'
-        >>> midiBytes += b'hello'
+        >>> midBytes += b'hello'
         >>> mt = midi.MidiTrack(1)
         >>> me1 = midi.MidiEvent(mt)
         >>> me1
@@ -738,7 +740,7 @@ class MidiEvent:
 
         Now show how the midiBytes changes the event:
 
-        >>> remainder = me1.parseChannelVoiceMessage(midiBytes)
+        >>> remainder = me1.parseChannelVoiceMessage(midBytes)
         >>> me1
         <MidiEvent NOTE_ON, t=0, track=1, channel=1, pitch=60, velocity=120>
 
@@ -1604,7 +1606,7 @@ class MidiFile(prebase.ProtoM21Object):
         ws = self.writestr()
         self.file.write(ws)
 
-    def writestr(self):
+    def writestr(self) -> bytes:
         '''
         Generate the MIDI data header and convert the list of
         MidiTrack objects in self.tracks into MIDI data and return it as bytes.
@@ -1616,7 +1618,7 @@ class MidiFile(prebase.ProtoM21Object):
             midiBytes = midiBytes + trk.getBytes()
         return midiBytes
 
-    def writeMThdStr(self):
+    def writeMThdStr(self) -> bytes:
         '''
         Convert the information in self.ticksPerQuarterNote
         into MIDI data header and return it as bytes.
@@ -1640,20 +1642,15 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
     These are tests that open windows and rely on external software
     '''
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         pass
 
+
 # ------------------------------------------------------------------------------
 
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testWriteMThdStr(self):
         '''
         Convert bytes of Ascii midi data to binary midi bytes.
diff --git a/music21/midi/percussion.py b/music21/midi/percussion.py
index 818cb7d44..a26cbf2fa 100644
--- a/music21/midi/percussion.py
+++ b/music21/midi/percussion.py
@@ -203,9 +203,6 @@ class PercussionMapper:
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
@@ -220,6 +217,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/o any args
                     obj = name()
diff --git a/music21/midi/translate.py b/music21/midi/translate.py
index fa7832322..6196ee146 100644
--- a/music21/midi/translate.py
+++ b/music21/midi/translate.py
@@ -2325,9 +2325,6 @@ def midiFileToStream(mf, inputM21=None, quantizePost=True, **keywords):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testMidiAsciiStringToBinaryString(self):
         from binascii import a2b_hex
 
diff --git a/music21/musedata/__init__.py b/music21/musedata/__init__.py
index 0521130af..36228f624 100644
--- a/music21/musedata/__init__.py
+++ b/music21/musedata/__init__.py
@@ -721,7 +721,7 @@ class MuseDataMeasure(prebase.ProtoM21Object):
     def getRecords(self):
         '''Return a lost of all records stored in this measure as MuseDataRecord.
         '''
-        return [mdr for mdr in self]
+        return list(self)
 
 
 # ------------------------------------------------------------------------------
@@ -1424,7 +1424,7 @@ class MuseDataPart(prebase.ProtoM21Object):
     def getMeasures(self):
         '''Return a list of all measures stored in this part as MuseDataMeasure objects.
         '''
-        return [mdm for mdm in self]
+        return list(self)
 
 
 # ------------------------------------------------------------------------------
@@ -1693,9 +1693,6 @@ class MuseDataDirectory(prebase.ProtoM21Object):
 # noinspection SpellCheckingInspection
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     # def testLoadFromString(self):
     #     from music21.musedata import testFiles
     #
diff --git a/music21/musedata/base12_26.py b/music21/musedata/base12_26.py
index efe56292a..4059191cb 100644
--- a/music21/musedata/base12_26.py
+++ b/music21/musedata/base12_26.py
@@ -151,9 +151,7 @@ Base26IntervalTable = {0: 'P1',
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 # ------------------------------------------------------------------------------
 
diff --git a/music21/musedata/base40.py b/music21/musedata/base40.py
index 43d241f3b..8d5e2ba1a 100644
--- a/music21/musedata/base40.py
+++ b/music21/musedata/base40.py
@@ -494,9 +494,7 @@ class BaseN:
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/musedata/translate.py b/music21/musedata/translate.py
index ce39826ad..8de48cfb5 100644
--- a/music21/musedata/translate.py
+++ b/music21/musedata/translate.py
@@ -367,9 +367,6 @@ def museDataWorkToStreamScore(museDataWork, inputM21=None):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         from music21 import musedata
         from music21 import common
diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py
index f56e0b68b..ee40c0397 100644
--- a/music21/musicxml/m21ToXml.py
+++ b/music21/musicxml/m21ToXml.py
@@ -6082,8 +6082,6 @@ def indent(elem, level=0):
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def getXml(self, obj):
         gex = GeneralObjectExporter()
@@ -6252,8 +6250,6 @@ class Test(unittest.TestCase):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testBasic(self):
         pass
diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py
index 830a0dc23..501397373 100644
--- a/music21/musicxml/xmlToM21.py
+++ b/music21/musicxml/xmlToM21.py
@@ -4146,7 +4146,7 @@ class MeasureParser(XMLParserBase):
             environLocal.warn('Cannot find voice %r; putting outside of voices.' %
                               (useVoice))
             environLocal.warn('Current voiceIds: {0}'.format(list(self.voicesById)))
-            environLocal.warn('Current voices: {0}'.format([v for v in m.voices]))
+            environLocal.warn(f'Current voices: {m.voices}')
 
         return thisVoice
 
@@ -4461,7 +4461,7 @@ class MeasureParser(XMLParserBase):
             # TODO: - should allow float, but meaningless to allow microtones in this context.
             seta(hd, mxDegree, 'degree-alter', 'interval', transform=int)
             seta(hd, mxDegree, 'degree-type', 'modType')
-            cs.addChordStepModification(hd)
+            cs.addChordStepModification(hd, updatePitches=False)
 
         if cs.chordKind != 'none':
             cs._updatePitches()
@@ -5865,15 +5865,15 @@ class Test(unittest.TestCase):
 
         self.assertEqual(str(i3.transposition), '<music21.interval.Interval P-5>')
 
-        self.assertEqual(self.pitchOut([p for p in s.parts[0].flat.pitches]),
+        self.assertEqual(self.pitchOut(s.parts[0].flat.pitches),
                          '[A4, A4, A4, A4, A4, A4, A4, A4, '
                          + 'E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, '
                          + 'A4, A4, A4, A4]')
-        self.assertEqual(self.pitchOut([p for p in s.parts[1].flat.pitches]),
+        self.assertEqual(self.pitchOut(s.parts[1].flat.pitches),
                          '[B4, B4, B4, B4, '
                          + 'F#4, F#4, F#4, F#4, F#4, F#4, F#4, F#4, F#4, F#4, F#4, '
                          + 'F#4, F#4, F#4, F#4, F#4, B4, B4, B4, B4, B4, B4]')
-        self.assertEqual(self.pitchOut([p for p in s.parts[2].flat.pitches]),
+        self.assertEqual(self.pitchOut(s.parts[2].flat.pitches),
                          '[E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, E5, '
                          + 'E5, E5, E5, E5, E5, E5, E5, E5]')
 
@@ -6488,9 +6488,9 @@ class Test(unittest.TestCase):
         v1, v2 = s.recurse().voices
         self.assertEqual(v1.duration.quarterLength, v2.duration.quarterLength)
 
-        restV1 = [r for r in v1.getElementsByClass(note.Rest)][0]
+        restV1 = v1.getElementsByClass(note.Rest)[0]
         self.assertTrue(restV1.style.hideObjectOnPrint)
-        restsV2 = [r for r in v2.getElementsByClass(note.Rest)]
+        restsV2 = v2.getElementsByClass(note.Rest)
         self.assertEqual([r.style.hideObjectOnPrint for r in restsV2], [True, True])
 
     def testMultiDigitEnding(self):
@@ -6512,6 +6512,20 @@ class Test(unittest.TestCase):
         repeatBracket = score2.recurse().getElementsByClass('RepeatBracket')[0]
         self.assertListEqual(repeatBracket.getNumberList(), [1])
 
+    def testChordAlteration(self):
+        from music21 import musicxml
+        from xml.etree.ElementTree import fromstring as EL
+        MP = musicxml.xmlToM21.MeasureParser()
+        elStr = (r'''<harmony><root><root-step>C</root-step></root><kind text="7b5">dominant</kind>
+        <degree><degree-value>5</degree-value><degree-alter>-1</degree-alter>
+        <degree-type>alter</degree-type></degree></harmony>''')
+        mxHarmony = EL(elStr)
+        cs = MP.xmlToChordSymbol(mxHarmony)
+        # Check that we parsed a modification
+        self.assertTrue(len(cs.getChordStepModifications()) == 1)
+        # And that it affected the correct pitch in the right way
+        self.assertTrue(pitch.Pitch("G-3") == cs.pitches[2])
+
 
 if __name__ == '__main__':
     import music21
diff --git a/music21/note.py b/music21/note.py
index 01f9b5743..81f8074d9 100644
--- a/music21/note.py
+++ b/music21/note.py
@@ -1610,9 +1610,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
     These are tests that open windows and rely on external software
     '''
 
-    def runTest(self):
-        pass
-
     def testSingle(self):
         '''Need to test direct meter creation w/o stream
         '''
@@ -1641,9 +1638,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''
         Test copying all objects defined in this module
@@ -1658,6 +1652,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             name = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(name) and not isinstance(name, types.FunctionType):
                 try:  # see if obj can be made w/ args
                     obj = name()
diff --git a/music21/noteworthy/translate.py b/music21/noteworthy/translate.py
index a2794c41a..891b77d26 100644
--- a/music21/noteworthy/translate.py
+++ b/music21/noteworthy/translate.py
@@ -848,9 +848,6 @@ class NoteworthyTranslateException(Music21Exception):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         nwcTranslatePath = common.getSourceFilePath() / 'noteworthy'
         simplePath = nwcTranslatePath / 'verySimple.nwctxt'
@@ -899,8 +896,6 @@ class Test(unittest.TestCase):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testComplete(self):
         nwcTranslatePath = common.getSourceFilePath() / 'noteworthy'
diff --git a/music21/pitch.py b/music21/pitch.py
index eba60040a..9c5c43023 100644
--- a/music21/pitch.py
+++ b/music21/pitch.py
@@ -4556,14 +4556,14 @@ class Pitch(prebase.ProtoM21Object):
 
     def updateAccidentalDisplay(
         self,
-        pitchPast=None,
-        pitchPastMeasure=None,
-        alteredPitches=None,
-        cautionaryPitchClass=True,
-        cautionaryAll=False,
-        overrideStatus=False,
-        cautionaryNotImmediateRepeat=True,
-        lastNoteWasTied=False,
+        pitchPast: Optional[List['Pitch']] = None,
+        pitchPastMeasure: Optional[List['Pitch']] = None,
+        alteredPitches: Optional[List['Pitch']] = None,
+        cautionaryPitchClass: bool = True,
+        cautionaryAll: bool = False,
+        overrideStatus: bool = False,
+        cautionaryNotImmediateRepeat: bool = True,
+        lastNoteWasTied: bool = False,
     ):
         '''
         Given an ordered list of Pitch objects in `pitchPast`, determine if
@@ -4572,8 +4572,8 @@ class Pitch(prebase.ProtoM21Object):
 
         Changes to this Pitch object's Accidental object are made in-place.
 
-        `pitchPast` is a list of pitches preceding this pitch.  If None, a new
-        list will be made.
+        `pitchPast` is a list of pitches preceding this pitch in the same measure.
+        If None, a new list will be made.
 
         `pitchPastMeasure` is a list of pitches preceding this pitch but in a
         previous measure. If None, a new list will be made.
@@ -4634,8 +4634,8 @@ class Pitch(prebase.ProtoM21Object):
             alteredPitches = []
 
         # TODO: this presently deals with chords as simply a list
-        # we might permit pitchPast to contain a list of pitches, to represent
-        # a simultaneity?
+        #    we might permit pitchPast to contain a list of pitches, to represent
+        #    a simultaneity?
 
         # should we display accidental if no previous accidentals have been displayed
         # i.e. if it's the first instance of an accidental after a tie
@@ -4649,14 +4649,15 @@ class Pitch(prebase.ProtoM21Object):
             elif self.accidental.displayStatus is None:  # not set; need to set
                 # configure based on displayStatus alone, continue w/ normal
                 pass
-            elif (self.accidental is not None
-                  and self.accidental.displayStatus in (True, False)):
+            elif self.accidental.displayStatus in (True, False):
                 return  # exit: already set, do not override
 
         if lastNoteWasTied is True:
             if self.accidental is not None:
                 if self.accidental.displayType != 'even-tied':
                     self.accidental.displayStatus = False
+                else:
+                    self.accidental.displayStatus = True
                 return
             else:
                 return  # exit: nothing more to do
@@ -4735,11 +4736,15 @@ class Pitch(prebase.ProtoM21Object):
         # which are the same as this one and in the same measure?
         # if so, set continuousRepeatsInMeasure to True
         # else, set to False
+        continuousRepeatsInMeasure: bool
 
         # figure out if this pitch is in the measure (pPastInMeasure = True)
         # or not.
         for i in reversed(range(len(pitchPastAll))):
+
             # is the past pitch in the measure or out of the measure?
+            pPastInMeasure: bool
+
             if i < outOfMeasureLength:
                 pPastInMeasure = False
                 continuousRepeatsInMeasure = False
@@ -4942,7 +4947,7 @@ class Pitch(prebase.ProtoM21Object):
                 self.accidental.displayStatus = True
             else:
                 self.accidental.displayStatus = False
-            displayAccidentalIfNoPreviousAccidentals = False  # just to be sure
+            # displayAccidentalIfNoPreviousAccidentals = False  # just to be sure
         elif not setFromPitchPast and self.accidental is not None:
             if not self._nameInKeySignature(alteredPitches):
                 self.accidental.displayStatus = True
@@ -5030,9 +5035,6 @@ class Pitch(prebase.ProtoM21Object):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
         '''
diff --git a/music21/repeat.py b/music21/repeat.py
index bfd1ec5be..b9b35602f 100644
--- a/music21/repeat.py
+++ b/music21/repeat.py
@@ -1082,7 +1082,7 @@ class Expander:
                                   <music21.stream.Measure 2 offset=3.0>>]}]
         '''
         groups = []
-        mEnumerated = [x for x in enumerate(streamObj)]
+        mEnumerated = list(enumerate(streamObj))
 
         # use known self._repeatBrackets and correlate with indices
         # store numbers to find when we have a new group
@@ -2470,8 +2470,7 @@ class RepeatFinder:
         for mGroup in mGroups:
             # make sure we haven't already processed these measures
             alreadyProcessed = False
-            measureNumbers = [x for x in mGroup[0]]
-            measureNumbers.extend(mGroup[1])
+            measureNumbers = mGroup[0] + mGroup[1]
             for mNum in measureNumbers:
                 if mNum in processed:
                     alreadyProcessed = True
@@ -2595,9 +2594,6 @@ class RepeatFinder:
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testFilterByRepeatMark(self):
         from music21 import stream, bar, repeat, note
 
diff --git a/music21/roman.py b/music21/roman.py
index a8446cdb2..57cf997f7 100644
--- a/music21/roman.py
+++ b/music21/roman.py
@@ -2552,9 +2552,6 @@ class RomanNumeral(harmony.Harmony):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module.
         '''
@@ -2978,9 +2975,6 @@ class Test(unittest.TestCase):
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testFromChordify(self):
         from music21 import corpus
         b = corpus.parse('bwv103.6')
diff --git a/music21/romanText/clercqTemperley.py b/music21/romanText/clercqTemperley.py
index ef45d5548..34ab7cff3 100644
--- a/music21/romanText/clercqTemperley.py
+++ b/music21/romanText/clercqTemperley.py
@@ -1,8 +1,8 @@
 # -*- coding: utf-8 -*-
 # ------------------------------------------------------------------------------
 # Name:         clercqTemperley.py
-# Purpose:      Demonstration of using music21 to parse Clercq-Temperley's
-#               popular music flavor of RomanText
+# Purpose:      Routines to parse the popular music
+#               Roman Numeral encoding system used by Clercq-Temperley
 #
 # Authors:      Beth Hadley
 #               Michael Scott Cuthbert
@@ -103,6 +103,7 @@ class CTSongException(exceptions21.Music21Exception):
 
 
 class CTSong(prebase.ProtoM21Object):
+    # noinspection PyShadowingNames
     r"""
     This parser is an object-oriented approach to parsing clercqTemperley text files into music.
 
@@ -404,6 +405,7 @@ class CTSong(prebase.ProtoM21Object):
 
     @property
     def rules(self):
+        # noinspection PyShadowingNames
         '''
         Get the rules of a CTSong. the Rules is an OrderedDict of
         objects of type CTRule. If only a text file
@@ -501,6 +503,7 @@ class CTSong(prebase.ProtoM21Object):
         return self._homeKeySig
 
     def toScore(self, labelRomanNumerals=True, labelSubsectionsOnScore=True):
+        # noinspection PyShadowingNames
         '''
         creates Score object out of a from CTSong...also creates CTRule objects in the process,
         filling their .streamFromCTSong attribute with the corresponding smaller inner stream.
@@ -957,14 +960,10 @@ class CTRule(prebase.ProtoM21Object):
 
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testB(self):
         from music21.romanText import clercqTemperley
diff --git a/music21/romanText/rtObjects.py b/music21/romanText/rtObjects.py
index ff200723d..7ed572907 100644
--- a/music21/romanText/rtObjects.py
+++ b/music21/romanText/rtObjects.py
@@ -1445,8 +1445,6 @@ class RTFile(prebase.ProtoM21Object):
 # ------------------------------------------------------------------------------
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testBasicA(self):
         from music21.romanText import testFiles
diff --git a/music21/romanText/translate.py b/music21/romanText/translate.py
index ba5e944fb..ae5fec8bc 100644
--- a/music21/romanText/translate.py
+++ b/music21/romanText/translate.py
@@ -1138,9 +1138,6 @@ def romanTextToStreamOpus(rtHandler, inputM21=None):
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testExternalA(self):
         from music21.romanText import testFiles
 
@@ -1156,9 +1153,6 @@ class TestSlow(unittest.TestCase):  # pragma: no cover
     These tests are currently too slow to run every time.
     '''
 
-    def runTest(self):
-        pass
-
     # noinspection SpellCheckingInspection
     def testBasicA(self):
         from music21.romanText import testFiles
diff --git a/music21/romanText/tsvConverter.py b/music21/romanText/tsvConverter.py
index eea230f9d..8eacf6350 100644
--- a/music21/romanText/tsvConverter.py
+++ b/music21/romanText/tsvConverter.py
@@ -544,10 +544,10 @@ class M21toTSV:
                 'phraseend',
             )
 
-            csvOut.writerow([x for x in headers])
+            csvOut.writerow(headers)
 
             for thisEntry in self.tsvData:
-                csvOut.writerow([x for x in thisEntry])
+                csvOut.writerow(thisEntry)
 
 # ------------------------------------------------------------------------------
 def is_minor(test_key):
diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py
index 7e9a58ce3..1273a9e2b 100644
--- a/music21/scale/__init__.py
+++ b/music21/scale/__init__.py
@@ -3163,9 +3163,6 @@ class WeightedHexatonicBlues(ConcreteScale):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def pitchOut(self, listIn):
         out = '['
         for p in listIn:
diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py
index 1f83f167f..4729ba1c9 100644
--- a/music21/scale/intervalNetwork.py
+++ b/music21/scale/intervalNetwork.py
@@ -2968,9 +2968,6 @@ class BoundIntervalNetwork(IntervalNetwork):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def pitchOut(self, listIn):
         out = '['
         for p in listIn:
diff --git a/music21/scale/scala/__init__.py b/music21/scale/scala/__init__.py
index 83a816f8b..013fca71f 100644
--- a/music21/scale/scala/__init__.py
+++ b/music21/scale/scala/__init__.py
@@ -557,17 +557,12 @@ def search(target):
 
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
-
-    def runTest(self):
-        pass
+    pass
 
 
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testScalaScaleA(self):
         msg = '''! slendro5_2.scl
 !
diff --git a/music21/search/base.py b/music21/search/base.py
index 687254a93..757a1647b 100644
--- a/music21/search/base.py
+++ b/music21/search/base.py
@@ -1111,6 +1111,7 @@ class Test(unittest.TestCase):
             if match:
                 continue
             obj = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(obj) and not isinstance(obj, types.FunctionType):
                 i = copy.copy(obj)
                 j = copy.deepcopy(obj)
diff --git a/music21/search/segment.py b/music21/search/segment.py
index 8fb46c324..b3e90a823 100644
--- a/music21/search/segment.py
+++ b/music21/search/segment.py
@@ -291,7 +291,7 @@ def getDifflibOrPyLev(
         smObject = difflib.SequenceMatcher(junk, '', seq2)
     else:
         try:
-            import StringMatcher as pyLevenshtein
+            from Levenshtein import StringMatcher as pyLevenshtein
             smObject = pyLevenshtein.StringMatcher(junk, '', seq2)
         except ImportError:
             smObject = difflib.SequenceMatcher(junk, '', seq2)
diff --git a/music21/search/serial.py b/music21/search/serial.py
index 0610fc434..8d94c6126 100644
--- a/music21/search/serial.py
+++ b/music21/search/serial.py
@@ -1951,7 +1951,7 @@ def _labelGeneral(segmentsToLabel, inputStream, segmentDict, reps, includeChords
         hasParts = True
 
     segmentList = [segmentDict[label] for label in segmentDict]
-    labelList = [label for label in segmentDict]
+    labelList = list(segmentDict)  # dicts are ordered as of Python 3.6 practically, 3.7 officially
     numSearchSegments = len(segmentList)
     numSegmentsToLabel = len(segmentsToLabel)
     reorderedSegmentsToLabel = sorted(segmentsToLabel, key=attrgetter(
@@ -2404,9 +2404,7 @@ def labelTransposedAndInvertedMultisets(inputStream,
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/serial.py b/music21/serial.py
index 1f54d7af0..4f16fb45a 100644
--- a/music21/serial.py
+++ b/music21/serial.py
@@ -1352,33 +1352,6 @@ def rowToMatrix(p: List[int]) -> str:
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
-#    def testRows(self):
-#        from music21 import interval
-#
-#        self.assertEqual(len(vienneseRows), 71)
-#
-#        totalRows = 0
-#        cRows = 0
-#        for thisRow in vienneseRows:
-#            thisRow = thisRow()
-#            self.assertIsInstance(thisRow, TwelveToneRow)
-#
-#            if thisRow.composer == 'Berg':
-#                continue
-#            post = thisRow.title
-#
-#            totalRows += 1
-#            if thisRow[0].pitchClass == 0:
-#                cRows += 1
-
-#             if interval.notesToInterval(thisRow[0],
-#                                    thisRow[6]).intervalClass == 6:
-#              # between element 1 and element 7 is there a TriTone?
-#              rowsWithTTRelations += 1
-
     def testMatrix(self):
         src = getHistoricalRowByName('SchoenbergOp37')
         self.assertEqual([p.name for p in src],
@@ -1446,10 +1419,35 @@ class Test(unittest.TestCase):
             if match:
                 continue
             obj = getattr(sys.modules[self.__module__], part)
+            # noinspection PyTypeChecker
             if callable(obj) and not isinstance(obj, types.FunctionType):
                 i = copy.copy(obj)
                 j = copy.deepcopy(obj)
 
+    # def testRows(self):
+    #     from music21 import interval
+    #
+    #     self.assertEqual(len(vienneseRows), 71)
+    #
+    #     totalRows = 0
+    #     cRows = 0
+    #     for thisRow in vienneseRows:
+    #         thisRow = thisRow()
+    #         self.assertIsInstance(thisRow, TwelveToneRow)
+    #
+    #         if thisRow.composer == 'Berg':
+    #             continue
+    #         post = thisRow.title
+    #
+    #         totalRows += 1
+    #         if thisRow[0].pitchClass == 0:
+    #             cRows += 1
+    #
+    #          if interval.notesToInterval(thisRow[0],
+    #                                 thisRow[6]).intervalClass == 6:
+    #           # between element 1 and element 7 is there a TriTone?
+    #           rowsWithTTRelations += 1
+
 
 # ------------------------------------------------------------------------------
 # define presented order in documentation
diff --git a/music21/sieve.py b/music21/sieve.py
index 999ec5360..544fe446f 100644
--- a/music21/sieve.py
+++ b/music21/sieve.py
@@ -2043,9 +2043,6 @@ class PitchSieve:
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testDummy(self):
         self.assertEqual(True, True)
 
diff --git a/music21/spanner.py b/music21/spanner.py
index 6a093cc1d..698f074a5 100644
--- a/music21/spanner.py
+++ b/music21/spanner.py
@@ -1790,9 +1790,6 @@ class Glissando(Spanner):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def setUp(self):
         from music21.musicxml.m21ToXml import GeneralObjectExporter
         self.GEX = GeneralObjectExporter()
diff --git a/music21/stream/__init__.py b/music21/stream/__init__.py
index 31873fa52..f6c491651 100644
--- a/music21/stream/__init__.py
+++ b/music21/stream/__init__.py
@@ -28,7 +28,7 @@ import warnings
 import sys
 
 from fractions import Fraction
-from typing import Union, List
+from typing import Union, List, Optional, Set
 
 from music21 import base
 
@@ -46,6 +46,7 @@ from music21 import key
 from music21 import metadata
 from music21 import meter
 from music21 import note
+from music21 import pitch  # for typing
 from music21 import spanner
 from music21 import tie
 from music21 import repeat
@@ -1770,6 +1771,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             self.isSorted = storeSorted
 
     def insertIntoNoteOrChord(self, offset, noteOrChord, chordsOnly=False):
+        # noinspection PyShadowingNames
         '''
         Insert a Note or Chord into an offset position in this Stream.
         If there is another Note or Chord in this position,
@@ -1840,7 +1842,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                     components = [noteOrChord]
                 elif 'Chord' in noteOrChord.classes:
                     pitches = list(noteOrChord.pitches)
-                    components = [c for c in noteOrChord]
+                    components = list(noteOrChord)
             if 'Note' in target.classes:
                 # if a note, make it into a chord
                 if 'Note' in noteOrChord.classes:
@@ -1848,15 +1850,15 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                     components = [target, noteOrChord]
                 elif 'Chord' in noteOrChord.classes:
                     pitches = [target.pitch] + list(noteOrChord.pitches)
-                    components = [target] + [c for c in noteOrChord]
+                    components = [target] + list(noteOrChord)
             if 'Chord' in target.classes:
                 # if a chord, make it into a chord
                 if 'Note' in noteOrChord.classes:
                     pitches = list(target.pitches) + [noteOrChord.pitch]
-                    components = [c for c in target] + [noteOrChord]
+                    components = list(target) + [noteOrChord]
                 elif 'Chord' in noteOrChord.classes:
                     pitches = list(target.pitches) + list(noteOrChord.pitches)
-                    components = [c for c in target] + [c for c in noteOrChord]
+                    components = list(target) + list(noteOrChord)
 
             if len(pitches) > 1 or chordsOnly is True:
                 finalTarget = chord.Chord(pitches)
@@ -2541,8 +2543,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                      addEndTimes=False,
                      useMixedNumerals=False):
         '''
-        Used by .show('text')
-
+        Used by .show('text') to display a stream's contents with offsets.
 
         >>> s1 = stream.Stream()
         >>> s2 = stream.Stream()
@@ -2555,21 +2556,23 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         >>> post
         '{0.0} <music21.stream.Stream ...> / {0.0} <...> / {0.0} <music21.note.Note C>'
         '''
-        def singleElement(element, indent, thisStream, addEndTimes, useMixedNumerals):
-            offGet = element.getOffsetBySite(thisStream)
+        def singleElement(in_element,
+                          in_indent,
+                          ) -> str:
+            offGet = in_element.getOffsetBySite(thisStream)
             if useMixedNumerals:
                 off = common.mixedNumeral(offGet)
             else:
                 off = common.strTrimFloat(offGet)
             if addEndTimes is False:
-                return indent + '{' + off + '} ' + repr(element)
+                return in_indent + '{' + off + '} ' + repr(in_element)
             else:
-                ql = offGet + element.duration.quarterLength
+                ql = offGet + in_element.duration.quarterLength
                 if useMixedNumerals:
                     qlStr = common.mixedNumeral(ql)
                 else:
                     qlStr = common.strTrimFloat(ql)
-                return indent + '{' + off + ' - ' + qlStr + '} ' + repr(element)
+                return in_indent + '{' + off + ' - ' + qlStr + '} ' + repr(in_element)
 
         msg = []
         insertSpaces = 4
@@ -2581,8 +2584,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
 
             # if isinstance(element, Stream):
             if element.isStream:
-                msg.append(singleElement(element, indent,
-                                         thisStream, addEndTimes, useMixedNumerals))
+                msg.append(singleElement(element, indent))
                 msg.append(self._recurseRepr(element,
                                              prefixSpaces + insertSpaces,
                                              addBreaks=addBreaks,
@@ -2590,12 +2592,11 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                                              addEndTimes=addEndTimes,
                                              useMixedNumerals=useMixedNumerals))
             else:
-                msg.append(singleElement(element, indent,
-                                         thisStream, addEndTimes, useMixedNumerals))
+                msg.append(singleElement(element, indent))
         if addBreaks:
             msg = '\n'.join(msg)
-        else:  # use slashes
-            msg = ' / '.join(msg)  # use space
+        else:  # use slashes with spaces
+            msg = ' / '.join(msg)
         return msg
 
     def _reprText(self, **keywords):
@@ -2624,7 +2625,20 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         This methods can be overridden by subclasses to
         provide alternative text representations.
         '''
-        return self._recurseRepr(self, addBreaks=False, addIndent=False)
+        if 'addEndTimes' in keywords:
+            addEndTimes = keywords['addEndTimes']
+        else:
+            addEndTimes = False
+        if 'useMixedNumerals' in keywords:
+            useMixedNumerals = keywords['useMixedNumerals']
+        else:
+            useMixedNumerals = False
+
+        return self._recurseRepr(self,
+                                 addEndTimes=addEndTimes,
+                                 useMixedNumerals=useMixedNumerals,
+                                 addBreaks=False,
+                                 addIndent=False)
 
     # --------------------------------------------------------------------------
     # display methods; in the same manner as show() and write()
@@ -4144,11 +4158,10 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         else:
             if self.hasMeasures():
                 return self.getElementsByClass('Measure')[-1].rightBarline
+            elif hasattr(self, 'rightBarline'):
+                return self.rightBarline
             else:
-                raise StreamException(
-                    f'cannot get a final barline on this Stream ({self})'
-                    + ' if no Measures are defined. Add Measures or call makeMeasures() first.'
-                )
+                return None
 
     def _setFinalBarline(self, value):
         # if we have part-like streams, process each part
@@ -4160,33 +4173,74 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                 bl = value[i % len(value)]
                 # environLocal.printDebug(['enumerating measures', i, p, 'setting barline', bl])
                 p._setFinalBarline(bl)
-        else:
-            # core routines for a single Stream
-            if self.hasMeasures():
-                self.getElementsByClass('Measure')[-1].rightBarline = value
-            else:
-                raise StreamException(
-                    f'cannot set a final barline on this Stream ({self})'
-                    + ' if no Measures are defined. Add Measures or call makeMeasures() first.'
-                )
+            return
+
+        # core routines for a single Stream
+        if self.hasMeasures():
+            self.getElementsByClass('Measure')[-1].rightBarline = value
+        elif hasattr(self, 'rightBarline'):
+            self.rightBarline = value  # pylint: disable=attribute-defined-outside-init
+        # do nothing for other streams
 
     finalBarline = property(_getFinalBarline, _setFinalBarline, doc='''
         Get or set the final barline of this Stream's Measures,
         if and only if there are Measures defined as elements in this Stream.
-        This method will not create Measures if non exist.
-        Setting a final barline to a Stream that does not have
-        Measure will raise an exception.
+        This method will not create Measures if none exist.
+
+        >>> p = stream.Part()
+        >>> m1 = stream.Measure()
+        >>> m1.rightBarline = bar.Barline('double')
+        >>> p.append(m1)
+        >>> p.finalBarline
+        <music21.bar.Barline type=double>
+        >>> m2 = stream.Measure()
+        >>> m2.rightBarline = bar.Barline('final')
+        >>> p.append(m2)
+        >>> p.finalBarline
+        <music21.bar.Barline type=final>
 
         This property also works on Scores that contain one or more Parts.
         In that case a list of barlines can be used to set the final barline.
 
         >>> s = corpus.parse('bwv66.6')
+        >>> s.finalBarline
+        [<music21.bar.Barline type=final>,
+         <music21.bar.Barline type=final>,
+         <music21.bar.Barline type=final>,
+         <music21.bar.Barline type=final>]
+
         >>> s.finalBarline = 'none'
         >>> s.finalBarline
         [<music21.bar.Barline type=none>,
          <music21.bar.Barline type=none>,
          <music21.bar.Barline type=none>,
          <music21.bar.Barline type=none>]
+
+
+        Getting or setting a final barline on a Measure (or another Stream
+        with a rightBarline attribute) is the same as getting or setting the rightBarline.
+
+        >>> m = stream.Measure()
+        >>> m.finalBarline is None
+        True
+        >>> m.finalBarline = 'final'
+        >>> m.finalBarline
+        <music21.bar.Barline type=final>
+        >>> m.rightBarline
+        <music21.bar.Barline type=final>
+
+        Getting on a generic Stream, Voice, or Opus always returns a barline of None,
+        and setting on a generic Stream, Voice, or Opus always returns None:
+
+        >>> s = stream.Stream()
+        >>> s.finalBarline is None
+        True
+        >>> s.finalBarline = 'final'
+        >>> s.finalBarline is None
+        True
+
+        Changed in v6.3 -- does not raise an exception if queried or set on a measure-less stream.
+        Previously raised a StreamException
         ''')
 
     @property
@@ -4300,6 +4354,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         return returnObj
 
     def toSoundingPitch(self, *, inPlace=False):
+        # noinspection PyShadowingNames
         '''
         If not at sounding pitch, transpose all Pitch
         elements to sounding pitch. The atSoundingPitch property
@@ -5124,7 +5179,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             tempComponents = []
             for n in subNotes:
                 if n.isChord:
-                    cSub = [subN for subN in n]
+                    cSub = list(n)
                 else:
                     cSub = [n]
 
@@ -5314,7 +5369,8 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                  addTies=True,
                  addPartIdAsGroup=False,
                  removeRedundantPitches=True,
-                 toSoundingPitch=True
+                 toSoundingPitch=True,
+                 copyPitches=True,
                  ):
         '''
         Create a chordal reduction of polyphonic music, where each
@@ -5440,6 +5496,32 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         >>> c.pitches[0].groups
         ['p0', 'p1']
 
+        With copyPitches = False, then the original pitches are retained, which
+        together with removeRedundantPitches=False can be a powerful tool for
+        working back to the original score:
+
+        >>> n00 = note.Note('C4')
+        >>> n01 = note.Note('E4')
+        >>> n10 = note.Note('E4', type='half')
+        >>> p0 = stream.Part(id='p0')
+        >>> p1 = stream.Part(id='p1')
+        >>> p0.append([n00, n01])
+        >>> p1.append(n10)
+        >>> s = stream.Score()
+        >>> s.insert(0, p0)
+        >>> s.insert(0, p1)
+        >>> ss = s.chordify(removeRedundantPitches=False, copyPitches=False, addPartIdAsGroup=True)
+        >>> ss.show('text')
+        {0.0} <music21.chord.Chord C4 E4>
+        {1.0} <music21.chord.Chord E4 E4>
+
+        >>> c1 = ss.recurse().notes[1]
+        >>> for p in c1.pitches:
+        ...     if 'p0' in p.groups:
+        ...         p.step = 'G'  # make a complete triad
+        >>> n01
+        <music21.note.Note G>
+
         Changes in v.5:
 
         Runs a little faster for small scores and run a TON faster for big scores
@@ -5447,6 +5529,9 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
 
         no longer supported: displayTiedAccidentals=False,
 
+        Changes in v.6.3:
+
+        Added copyPitches
 
         OMIT_FROM_DOCS
 
@@ -5483,6 +5568,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                                                addTies=addTies,
                                                addPartIdAsGroup=addPartIdAsGroup,
                                                removeRedundantPitches=removeRedundantPitches,
+                                               copyPitches=copyPitches,
                                                )
 
                 template.coreInsert(opFrac(offset), chordOrRest)
@@ -5558,6 +5644,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         return template
 
     def splitByClass(self, classObj, fx):
+        # noinspection PyShadowingNames
         '''
         Given a stream, get all objects of type classObj and divide them into
         two new streams depending on the results of fx.
@@ -5577,7 +5664,6 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         what these powerful tools are).
 
 
-
         >>> stream1 = stream.Stream()
         >>> for x in range(30, 81):
         ...     n = note.Note()
@@ -5775,17 +5861,17 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
     def makeAccidentals(
         self,
         *,
-        pitchPast=None,
-        pitchPastMeasure=None,
-        useKeySignature=True,
-        alteredPitches=None,
-        searchKeySignatureByContext=False,
-        cautionaryPitchClass=True,
-        cautionaryAll=False,
-        inPlace=True,
-        overrideStatus=False,
-        cautionaryNotImmediateRepeat=True,
-        tiePitchSet=None
+        pitchPast: Optional[List[pitch.Pitch]] = None,
+        pitchPastMeasure: Optional[List[pitch.Pitch]] = None,
+        useKeySignature: Union[bool, key.KeySignature] = True,
+        alteredPitches: Optional[List[pitch.Pitch]] = None,
+        searchKeySignatureByContext: bool = False,
+        cautionaryPitchClass: bool = True,
+        cautionaryAll: bool = False,
+        inPlace: bool = True,
+        overrideStatus: bool = False,
+        cautionaryNotImmediateRepeat: bool = True,
+        tiePitchSet: Optional[Set[str]] = None
     ):
         '''
         A method to set and provide accidentals given various conditions and contexts.
@@ -5821,6 +5907,9 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         to determine whether following accidentals should be shown because the last
         note of the same pitch had a start or continue tie.
 
+        If `searchKeySignatureByContext` is True then keySignatures from the context of the
+        stream will be used if none found.
+
         The :meth:`~music21.pitch.Pitch.updateAccidentalDisplay` method is used to determine if
         an accidental is necessary.
 
@@ -5848,7 +5937,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         # see if there is any key signatures to add to altered pitches
         if alteredPitches is None:
             alteredPitches = []
-        addAlteredPitches = []
+        addAlteredPitches: List[pitch.Pitch] = []
         if isinstance(useKeySignature, key.KeySignature):
             addAlteredPitches = useKeySignature.alteredPitches
         elif useKeySignature is True:  # get from defined contexts
@@ -5896,13 +5985,12 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                     tiePitchSet.add(e.pitch.nameWithOctave)
 
             elif isinstance(e, chord.Chord):
-                chordNotes = list(e)
                 # add all chord elements to past first
                 # when reading a chord, this will apply an accidental
                 # if pitches in the chord suggest an accidental
                 seenPitchNames = set()
 
-                for n in chordNotes:
+                for n in list(e):
                     p = n.pitch
                     if p.nameWithOctave in tiePitchSet:
                         lastNoteWasTied = True
@@ -6279,8 +6367,8 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             # but capture case where all chord members have a stop tie
             elif (hasattr(n, 'tie')
                     and 'Chord' in n.classes
-                    and None not in [p.tie for p in n.notes]
-                    and {p.tie.type for p in n.notes} == {'stop'}):
+                    and None not in [inner_p.tie for inner_p in n.notes]
+                    and {inner_p.tie.type for inner_p in n.notes} == {'stop'}):
                 return True
             # if we cannot find a stop tie, see if last note was connected
             # and this and the last note are the same pitch; this assumes
@@ -6412,8 +6500,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         The `pitchAttr` determines the pitch attribute that is
         used for comparison. Any valid pitch attribute name can be used.
         '''
-        def _getNextElements(srcStream, currentIndex, targetOffset,
-                             ignoreRests=ignoreRests):
+        def _getNextElements(srcStream, currentIndex, targetOffset):
             # need to find next event that start at the appropriate offset
             if currentIndex == len(srcStream) - 1:  # assume flat
                 # environLocal.printDebug(['_getNextElements: nothing to process',
@@ -6446,7 +6533,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             if 'Note' in e.classes:
                 pSrc = [e]
             elif 'Chord' in e.classes:
-                pSrc = [n for n in e]  # get components
+                pSrc = list(e)  # get components
             else:
                 continue
             # environLocal.printDebug(['examining', i, e])
@@ -6461,7 +6548,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                 if 'Note' in m.classes:
                     mSrc = [m]
                 elif 'Chord' in m.classes:
-                    mSrc = [n for n in m]  # get components
+                    mSrc = list(m)  # get components
                 # final note comparison
                 for q in mSrc:
                     if getattr(q.pitch, pitchAttr) == getattr(p.pitch, pitchAttr):
@@ -7336,9 +7423,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         ''')
 
     def _setSeconds(self, value):
-        # setting the seconds on a Stream for now is the same as on a
-        # Music21Object, which reassigns Duration
-        base.Music21Object._setSeconds(self, value)
+        pass
 
     def _getSeconds(self):
         getTempoFromContext = False
@@ -7364,9 +7449,10 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         if getTempoFromContext:
             ti = self.getContextByClass('TempoIndication')
             if ti is None:
-                raise StreamException(
-                    'cannot get a seconds duration when no TempoIndication classes '
-                    + 'are found in or before this Stream.')
+                if self.highestTime != 0.0:
+                    return float('nan')
+                else:
+                    return 0.0
             # insert at zero offset position, even though coming from
             # outside this stream
             mm = ti.getSoundingMetronomeMark()
@@ -7393,7 +7479,6 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         this object contains a :class:`~music21.tempo.MetronomeMark` or
         :class:`~music21.tempo.MetricModulation`.
 
-
         >>> s = corpus.parse('bwv66.6')  # piece without a tempo
         >>> sFlat = s.flat
         >>> t = tempo.MetronomeMark('adagio')
@@ -7404,6 +7489,29 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         >>> sFlat.replace(t, tFast)
         >>> sFlat.seconds
         16.363...
+
+        Setting seconds on streams is not supported.  Ideally it would instead
+        scale all elements to fit, but this is a long way off.
+
+        If a stream does not have a tempo-indication in it then the property
+        returns 0.0 if an empty Stream (or self.highestTime is 0.0) or 'nan'
+        if there are non-zero duration objects in the stream:
+
+        >>> s = stream.Stream()
+        >>> s.seconds
+        0.0
+        >>> s.insert(0, clef.TrebleClef())
+        >>> s.seconds
+        0.0
+        >>> s.append(note.Note(type='half'))
+        >>> s.seconds
+        nan
+        >>> import math
+        >>> math.isnan(s.seconds)
+        True
+
+        Changed in v6.3 -- return nan rather than raising an exception.  Do not
+        attempt to change seconds on a stream, as it did not do what you would expect.
         ''')
 
     def metronomeMarkBoundaries(self, srcObj=None):
@@ -8438,7 +8546,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             oEnd = opFrac(oEnd)
 
             for o in offsetList:
-                if o > oStart and o < oEnd:
+                if oStart < o < oEnd:
                     cutPoints.append(o)
             # environLocal.printDebug(['cutPoints', cutPoints, 'oStart', oStart, 'oEnd', oEnd])
             if cutPoints:
@@ -8656,6 +8764,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         return True
 
     def isWellFormedNotation(self):
+        # noinspection PyShadowingNames
         '''
         Return True if, given the context of this Stream or Stream subclass,
         contains what appears to be well-formed notation. This often means
@@ -9151,16 +9260,16 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         is set by the includeEndBoundary being True.
 
 
-        >>> a = stream.Stream()
-        >>> a._durSpanOverlap((0, 5), (4, 12), False)
+        >>> sc = stream.Stream()
+        >>> sc._durSpanOverlap((0, 5), (4, 12), False)
         True
-        >>> a._durSpanOverlap((0, 10), (11, 12), False)
+        >>> sc._durSpanOverlap((0, 10), (11, 12), False)
         False
-        >>> a._durSpanOverlap((11, 12), (0, 10), False)
+        >>> sc._durSpanOverlap((11, 12), (0, 10), False)
         False
-        >>> a._durSpanOverlap((0, 3), (3, 6), False)
+        >>> sc._durSpanOverlap((0, 3), (3, 6), False)
         False
-        >>> a._durSpanOverlap((0, 3), (3, 6), True)
+        >>> sc._durSpanOverlap((0, 3), (3, 6), True)
         True
         '''
         durSpans = [a, b]
@@ -9198,7 +9307,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         durSpanSorted = self._getDurSpan(flatStream)
         # According to the above comment, the spans may not be sorted
         # so we sort them to be sure, but keep track of their original indices
-        durSpanSortedIndex = [(v, i) for v, i in enumerate(durSpanSorted)]
+        durSpanSortedIndex = list(enumerate(durSpanSorted))
         durSpanSortedIndex.sort()
 
         # create a list with an entry for each element
@@ -9511,6 +9620,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
         # return sorted(sOuterTree.simultaneityDict().keys())
 
     def attachIntervalsBetweenStreams(self, cmpStream):
+        # noinspection PyShadowingNames
         '''
         For each element in self, creates an interval.Interval object in the element's
         editorial that is the interval between it and the element in cmpStream that
@@ -10663,6 +10773,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             return None
 
     def _insertReplacementVariant(self, v, matchBySpan=True):
+        # noinspection PyShadowingNames
         '''
         Helper function for activateVariants. Activates variants which are the same size there the
         region they replace.
@@ -11190,7 +11301,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
                 shiftsDict[startOffset] = (shiftDur, endOffset, includeEnd,
                                            exemptObjects, exemptShift)
 
-            for offset in sorted(shiftsDict, key=lambda offset: -1 * offset):
+            for offset in sorted(shiftsDict, key=lambda off: -1 * off):
                 shiftDur, endOffset, includeEnd, exemptObjects, exemptShift = shiftsDict[offset]
                 for e in returnObj.getElementsByOffset(offset,
                                                        endOffset,
@@ -11213,6 +11324,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             return returnObj
 
     def _fixMeasureNumbers(self, deletedMeasures, insertedMeasures):
+        # noinspection PyShadowingNames
         '''
         Corrects the measures numbers of a string of measures given a list of measure numbers
         that have been deleted and a
@@ -11376,6 +11488,7 @@ class Stream(core.StreamCoreMixin, base.Music21Object):
             previousBoundary = k
 
     def showVariantAsOssialikePart(self, containedPart, variantGroups, *, inPlace=False):
+        # noinspection PyShadowingNames
         '''
         Takes a part within the score and a list of variant groups within that part.
         Puts the variant object
@@ -12681,7 +12794,7 @@ class Score(Stream):
             # update based on last id, new object
             if e.sites.hasSpannerSite():
                 origin = e.derivation.origin
-                if (origin is not None and e.derivation.method == '__deepcopy__'):
+                if origin is not None and e.derivation.method == '__deepcopy__':
                     spannerBundle.replaceSpannedElement(id(origin), e)
         return post
 
@@ -12773,6 +12886,7 @@ class Score(Stream):
                       voiceAllocation: Union[int, List[Union[List, int]]] = 2,
                       permitOneVoicePerPart=False,
                       setStems=True):
+        # noinspection PyShadowingNames
         '''
         Given a multi-part :class:`~music21.stream.Score`,
         return a new Score that combines parts into voices.
@@ -13017,13 +13131,13 @@ class Opus(Stream):
         return post
 
     def getScoreByNumber(self, opusMatch):
+        # noinspection PyShadowingNames
         '''
         Get Score objects from this Stream by number.
         Performs title search using the
         :meth:`~music21.metadata.Metadata.search` method,
         and returns the first result.
 
-
         >>> o = corpus.parse('josquin/oVenusBant')
         >>> o.getNumbers()
         ['1', '2', '3']
@@ -13038,11 +13152,6 @@ class Opus(Stream):
             if match:
                 return s
 
-            # if s.metadata.number == opusMatch:
-            #     return s
-            # elif s.metadata.number == str(opusMatch):
-            #     return s
-
     # noinspection SpellCheckingInspection
     def getScoreByTitle(self, titleMatch):
         # noinspection SpellCheckingInspection
@@ -13076,13 +13185,13 @@ class Opus(Stream):
         return self.getElementsByClass(Score)
 
     def mergeScores(self):
+        # noinspection PyShadowingNames
         '''
         Some Opus objects represent numerous scores
         that are individual parts of the same work.
         This method will treat each contained Score as a Part,
         merging and returning a single Score with merged Metadata.
 
-
         >>> from music21 import corpus
         >>> o = corpus.parse('josquin/milleRegrets')
         >>> s = o.mergeScores()
diff --git a/music21/stream/core.py b/music21/stream/core.py
index cd3a1250e..6ee50efe2 100644
--- a/music21/stream/core.py
+++ b/music21/stream/core.py
@@ -570,9 +570,7 @@ class StreamCoreMixin:
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py
index d18db2d1d..7d745eef0 100644
--- a/music21/stream/iterator.py
+++ b/music21/stream/iterator.py
@@ -533,7 +533,7 @@ class StreamIterator(prebase.ProtoM21Object):
         savedRestoreActiveSites = self.restoreActiveSites
         self.restoreActiveSites = True
 
-        me = [x for x in self]
+        me = [x for x in self]  # pylint: disable=unnecessary-comprehension
 
         self.reset()
 
diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py
index ef15f3f3f..6dc0e9b75 100644
--- a/music21/stream/makeNotation.py
+++ b/music21/stream/makeNotation.py
@@ -1468,9 +1468,6 @@ class Test(unittest.TestCase):
     Note: all Stream tests are found in test/testStream.py
     '''
 
-    def runTest(self):
-        pass
-
     def testNotesToVoices(self):
         from music21 import stream
         s = stream.Stream()
@@ -1483,7 +1480,7 @@ class Test(unittest.TestCase):
         self.assertEqual(len(s), 1)
         self.assertEqual(s[0].classes[0], 'Voice')  # default is a Voice
         self.assertEqual(len(s[0]), 4)
-        self.assertEqual(str([n for n in s.voices[0].notesAndRests]),
+        self.assertEqual(str(list(s.voices[0].notesAndRests)),
                          '[<music21.note.Note C>, <music21.note.Note C>, '
                          + '<music21.note.Note C>, <music21.note.Note C>]')
 
diff --git a/music21/stream/streamStatus.py b/music21/stream/streamStatus.py
index cf822c87d..66b733177 100644
--- a/music21/stream/streamStatus.py
+++ b/music21/stream/streamStatus.py
@@ -201,9 +201,6 @@ class Test(unittest.TestCase):
     Note: all Stream tests are found in test/testStream.py
     '''
 
-    def runTest(self):
-        pass
-
     def testHaveBeamsBeenMadeAfterDeepcopy(self):
         import copy
         from music21 import stream, note
diff --git a/music21/tablature.py b/music21/tablature.py
index 0d891c13e..3bbbab7cc 100644
--- a/music21/tablature.py
+++ b/music21/tablature.py
@@ -349,8 +349,6 @@ class MandolinFretBoard(FretBoard):
 
 
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testFretNoteString(self):
         f = FretNote(4, 1, 2)
diff --git a/music21/tempo.py b/music21/tempo.py
index 1d60bd2fe..f15628255 100644
--- a/music21/tempo.py
+++ b/music21/tempo.py
@@ -1269,8 +1269,6 @@ def interpolateElements(element1, element2, sourceStream,
 
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
-    def runTest(self):
-        pass
 
     def testCopyAndDeepcopy(self):
         '''Test copying all objects defined in this module
diff --git a/music21/tie.py b/music21/tie.py
index 3930c828e..0285d012a 100644
--- a/music21/tie.py
+++ b/music21/tie.py
@@ -143,9 +143,7 @@ class Tie(prebase.ProtoM21Object, SlottedObjectMixin):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/tinyNotation.py b/music21/tinyNotation.py
index b8dfc122b..cd78014a9 100644
--- a/music21/tinyNotation.py
+++ b/music21/tinyNotation.py
@@ -1238,9 +1238,6 @@ class Converter:
 class Test(unittest.TestCase):
     parseTest = '1/4 trip{C8~ C~_hello C=mine} F~ F~ 2/8 F F# quad{g--16 a## FF(n) g#} g16 F0'
 
-    def runTest(self):
-        pass
-
     def testOne(self):
         c = Converter(self.parseTest)
         c.parse()
@@ -1261,8 +1258,6 @@ class Test(unittest.TestCase):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testOne(self):
         c = Converter(Test.parseTest)
diff --git a/music21/tree/analysis.py b/music21/tree/analysis.py
index 01976da03..20bae33b9 100644
--- a/music21/tree/analysis.py
+++ b/music21/tree/analysis.py
@@ -136,9 +136,7 @@ class Horizontality(collections.abc.Sequence):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/tree/node.py b/music21/tree/node.py
index 2d8d089cc..6587304f5 100644
--- a/music21/tree/node.py
+++ b/music21/tree/node.py
@@ -556,9 +556,7 @@ class OffsetNode(ElementNode):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/tree/spans.py b/music21/tree/spans.py
index 9bc539efd..73ceceb28 100644
--- a/music21/tree/spans.py
+++ b/music21/tree/spans.py
@@ -623,9 +623,7 @@ class PitchedTimespan(ElementTimespan):
 # -----------------------------------------------------------------------------
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 if __name__ == '__main__':
diff --git a/music21/tree/timespanTree.py b/music21/tree/timespanTree.py
index 9ae13b556..689892e4e 100644
--- a/music21/tree/timespanTree.py
+++ b/music21/tree/timespanTree.py
@@ -740,9 +740,6 @@ class TimespanTree(trees.OffsetTree):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testGetVerticalityAtWithKey(self):
         from music21 import stream, key, note
         s = stream.Stream()
@@ -771,7 +768,7 @@ class Test(unittest.TestCase):
                 tsTree.insert(timespan)
                 currentTimespansInList = list(sorted(tss[:i + 1],
                                                      key=lambda x: (x.offset, x.endTime)))
-                currentTimespansInTree = [x for x in tsTree]
+                currentTimespansInTree = list(tsTree)
                 currentPosition = min(x.offset for x in currentTimespansInList)
                 currentEndTime = max(x.endTime for x in currentTimespansInList)
 
@@ -794,7 +791,7 @@ class Test(unittest.TestCase):
                 currentTimespansInList = sorted(tss,
                                                 key=lambda x: (x.offset, x.endTime))
                 tsTree.removeTimespan(timespan)
-                currentTimespansInTree = [x for x in tsTree]
+                currentTimespansInTree = list(tsTree)
                 self.assertEqual(currentTimespansInTree,
                                  currentTimespansInList,
                                  (attempt, currentTimespansInTree, currentTimespansInList))
diff --git a/music21/tree/trees.py b/music21/tree/trees.py
index 06706fa38..1f601eb74 100644
--- a/music21/tree/trees.py
+++ b/music21/tree/trees.py
@@ -1162,7 +1162,7 @@ class OffsetTree(ElementTree):
         '''
         newTree = type(self)()
         # this is just as efficient as ._insertCore, since it's given a list.
-        newTree.insert([x for x in self])
+        newTree.insert(list(self))
         newTree.source = self.source
         newTree.parentTrees = self.parentTrees.copy()
         return newTree
@@ -1474,9 +1474,6 @@ class OffsetTree(ElementTree):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testGetPositionAfterOffset(self):
         '''
         test that get position after works with
diff --git a/music21/tree/verticality.py b/music21/tree/verticality.py
index 2fe156dd5..579b06e23 100644
--- a/music21/tree/verticality.py
+++ b/music21/tree/verticality.py
@@ -539,7 +539,8 @@ class Verticality(prebase.ProtoM21Object):
                     addPartIdAsGroup=False,
                     removeRedundantPitches=True,
                     gatherArticulations='single',
-                    gatherExpressions='single'
+                    gatherExpressions='single',
+                    copyPitches=True,
                     ):
         r'''
         Makes a Chord or Rest from this verticality and quarterLength.
@@ -589,6 +590,28 @@ class Verticality(prebase.ProtoM21Object):
         >>> c
         <music21.chord.Chord C4 C4>
 
+        Generally the pitches of the new element are not connected to the original pitch:
+
+        >>> c[0].pitch.name = 'E'
+        >>> c[1].pitch.name = 'F'
+        >>> (n1.name, n2.name)
+        ('C', 'C')
+
+        But if `copyPitches` is False then the original pitch will be used:
+
+        >>> n1.name = 'D'
+        >>> n2.name = 'E'
+        >>> c = verticality.makeElement(0.5, removeRedundantPitches=False, copyPitches=False)
+        >>> c
+        <music21.chord.Chord D4 E4>
+
+        >>> c[0].pitch.name = 'F'
+        >>> c[1].pitch.name = 'G'
+        >>> (n1.name, n2.name)
+        ('F', 'G')
+
+
+
         gatherArticulations and gatherExpressions can be True, False, or (default) 'single'.
 
         * If False, no articulations (or expressions) are transferred to the chord.
@@ -672,6 +695,19 @@ class Verticality(prebase.ProtoM21Object):
          <...AllAttachArticulation>,
          <...OtherAllAttachArticulation>]
 
+        Added in v6.3:  copyPitches option
+
+        OMIT_FROM_DOCS
+
+        Test that copyPitches works with expressions:
+
+        >>> c = verticality.makeElement(0.5, copyPitches=False)
+        >>> c
+        <music21.chord.Chord D4>
+        >>> c.pitches[0].accidental = pitch.Accidental('sharp')
+        >>> n2
+        <music21.note.Note D#>
+
         '''
         if not self.pitchSet:
             r = note.Rest()
@@ -696,6 +732,9 @@ class Verticality(prebase.ProtoM21Object):
             '''
             nNew = copy.deepcopy(n)
             nNew.duration = dur
+            if not copyPitches:
+                nNew.pitch = n.pitch
+
             if nNew.stemDirection != 'noStem':
                 nNew.stemDirection = None
             if not addTies:
@@ -796,6 +835,8 @@ class Verticality(prebase.ProtoM21Object):
                     firstSubEl = copy.deepcopy(el[0])  # this makes an additional deepcopy
                     firstSubEl.articulations += el.articulations
                     firstSubEl.expressions += el.expressions
+                    if not copyPitches:
+                        firstSubEl.pitch = el[0].pitch
                 else:
                     firstSubEl = el[0]
                 conditionalAdd(ts, firstSubEl)
@@ -1070,9 +1111,7 @@ class VerticalitySequence(prebase.ProtoM21Object, collections.abc.Sequence):
 # -----------------------------------------------------------------------------
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 # -----------------------------------------------------------------------------
 
diff --git a/music21/variant.py b/music21/variant.py
index 2b281ed01..889329a2f 100644
--- a/music21/variant.py
+++ b/music21/variant.py
@@ -851,7 +851,7 @@ def mergePartAsOssia(mainPart, ossiaPart, ossiaName,
 def addVariant(
     s: stream.Stream,
     startOffset: Union[int, float],
-    sVariant: 'Variant',
+    sVariant: Union[stream.Stream, 'Variant'],
     variantName=None,
     variantGroups=None,
     replacementDuration=None
@@ -1068,7 +1068,7 @@ def refineVariant(s, sVariant, *, inPlace=False):
 
     # associating measures in variantRegion to those in returnRegion ->
     #       This is done via 0 indexed lists corresponding to measures
-    returnRegionMeasureList = [i for i in range(len(returnRegion))]
+    returnRegionMeasureList = list(range(len(returnRegion)))
     badnessDict = {}
     listDict = {}
     variantMeasureList, unused_badness = _getBestListAndScore(returnRegion,
@@ -1147,7 +1147,7 @@ def _mergeVariantMeasureStreamsCarefully(streamX, streamY, variantName, *, inPla
 
     # associating measures in variantRegion to those in returnRegion ->
     #    This is done via 0 indexed lists corresponding to measures
-    returnObjectMeasureList = [i for i in range(len(returnObject.getElementsByClass('Measure')))]
+    returnObjectMeasureList = list(range(len(returnObject.getElementsByClass('Measure'))))
     badnessDict = {}
     listDict = {}
     variantObjectMeasureList, unused_badness = _getBestListAndScore(
@@ -2515,9 +2515,6 @@ class Variant(base.Music21Object):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def pitchOut(self, listIn):
         out = '['
         for p in listIn:
@@ -2597,22 +2594,21 @@ class Test(unittest.TestCase):
 
         # normal in-place variant functionality
         s.insert(5, v1)
-        self.assertEqual(self.pitchOut([p for p in s.pitches]),
+        self.assertEqual(self.pitchOut(s.pitches),
             '[G4, G4, G4, G4, G4, G4, G4, G4]')
         sv = s.activateVariants(inPlace=False)
-        self.assertEqual(self.pitchOut([p for p in sv.pitches]),
+        self.assertEqual(self.pitchOut(sv.pitches),
             '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]')
 
         # test functionality on a deepcopy
         sCopy = copy.deepcopy(s)
         self.assertEqual(len(sCopy.variants), 1)
-        self.assertEqual(self.pitchOut([p for p in sCopy.pitches]),
+        self.assertEqual(self.pitchOut(sCopy.pitches),
             '[G4, G4, G4, G4, G4, G4, G4, G4]')
         sCopy.activateVariants(inPlace=True)
-        self.assertEqual(self.pitchOut([p for p in sCopy.pitches]),
+        self.assertEqual(self.pitchOut(sCopy.pitches),
             '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]')
 
-
     def testDeepCopyVariantB(self):
         s = stream.Stream()
         s.repeatAppend(note.Note('G4'), 8)
@@ -2624,11 +2620,11 @@ class Test(unittest.TestCase):
         # as we deepcopy the elements in the variants, we have new Notes
         sCopy = copy.deepcopy(s)
         sCopy.activateVariants(inPlace=True)
-        self.assertEqual(self.pitchOut([p for p in sCopy.pitches]),
+        self.assertEqual(self.pitchOut(sCopy.pitches),
             '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]')
         # can transpose the note in place
         sCopy.notes[5].transpose(12, inPlace=True)
-        self.assertEqual(self.pitchOut([p for p in sCopy.pitches]),
+        self.assertEqual(self.pitchOut(sCopy.pitches),
             '[G4, G4, G4, G4, G4, F#5, A-4, G4, G4]')
 
         # however, if the Variant deepcopy still references the original
@@ -2636,16 +2632,12 @@ class Test(unittest.TestCase):
         # in original Stream, we would get unexpected results (the octave shift)
 
         s.activateVariants(inPlace=True)
-        self.assertEqual(self.pitchOut([p for p in s.pitches]),
+        self.assertEqual(self.pitchOut(s.pitches),
             '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]')
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
-
     def testMergeJacopoVariants(self):
         from music21 import corpus
         j1 = corpus.parse('trecento/PMFC_06-Jacopo-03a')
diff --git a/music21/vexflow/toMusic21j.py b/music21/vexflow/toMusic21j.py
index 045922374..1bb48e156 100644
--- a/music21/vexflow/toMusic21j.py
+++ b/music21/vexflow/toMusic21j.py
@@ -275,19 +275,11 @@ class VexflowToM21JException(Music21Exception):
 
 
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
-
-    def testDummy(self):
-        pass
+    pass
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
 
-    def runTest(self):
-        pass
-
     def testCuthbertLocal(self):
         '''
         test a local version of this mess...
diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py
index 470729301..258dc0213 100644
--- a/music21/voiceLeading.py
+++ b/music21/voiceLeading.py
@@ -2362,9 +2362,6 @@ class TwoChordLinearSegment(NChordLinearSegment):
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testInstantiateEmptyObject(self):
         '''
         test instantiating an empty VoiceLeadingQuartet
@@ -2425,8 +2422,7 @@ class Test(unittest.TestCase):
 
 
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
+    pass
 
 
 # -----------------------------------------------------------------------------
diff --git a/music21/volume.py b/music21/volume.py
index a500a535c..3186e6aa7 100644
--- a/music21/volume.py
+++ b/music21/volume.py
@@ -497,9 +497,6 @@ def realizeVolume(srcStream,
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         import gc
         from music21 import volume, note


Test patch:
diff --git a/documentation/source/developerReference/testing.ipynb b/documentation/source/developerReference/testing.ipynb
index 17514e245..bc2c0ebf8 100644
--- a/documentation/source/developerReference/testing.ipynb
+++ b/documentation/source/developerReference/testing.ipynb
@@ -34,9 +34,6 @@
     "\n",
     "class Test(unittest.TestCase):\n",
     "\n",
-    "    def runTest(self):\n",
-    "        pass\n",
-    "\n",
     "    def testNotesHaveStemDirection(self):\n",
     "        from music21 import note\n",
     "        n = note.Note('C#')\n",
diff --git a/music21/abcFormat/testFiles.py b/music21/abcFormat/testFiles.py
index 6c2b257f8..5398ab84e 100644
--- a/music21/abcFormat/testFiles.py
+++ b/music21/abcFormat/testFiles.py
@@ -663,9 +663,6 @@ def get(contentRequest):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         from music21 import abcFormat
         from music21.abcFormat import translate
diff --git a/music21/corpus/testCorpus.py b/music21/corpus/testCorpus.py
index 20b73ea3e..0f004ecdd 100644
--- a/music21/corpus/testCorpus.py
+++ b/music21/corpus/testCorpus.py
@@ -16,9 +16,6 @@ from music21 import corpus
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testGetPaths(self):
         for known in [
             'schumann_clara/opus17/movement3.xml',
diff --git a/music21/mei/test_base.py b/music21/mei/test_base.py
index b79e86039..4e6acc73d 100644
--- a/music21/mei/test_base.py
+++ b/music21/mei/test_base.py
@@ -3347,7 +3347,7 @@ class Test(unittest.TestCase):
         '''
         scaleToTuplet(): with three objects, the "tuplet search" attributes are set properly.
         '''
-        objs = [mock.MagicMock(spec=note.Note()) for _ in range(3)]
+        objs = [note.Note() for _ in range(3)]
         elem = ETree.Element('tupletDef',
                              attrib={'m21TupletNum': '12',
                                      'm21TupletNumbase': '400',
@@ -3366,7 +3366,7 @@ class Test(unittest.TestCase):
         scaleToTuplet(): with three objects, their
         duration is scaled properly. (With @m21TupletType).
         '''
-        objs = [mock.MagicMock(spec=note.Note()) for _ in range(3)]
+        objs = [note.Note() for _ in range(3)]
         for obj in objs:
             obj.duration = mock.MagicMock()
             obj.duration.type = 'duration type'
@@ -3845,7 +3845,7 @@ class Test(unittest.TestCase):
         # prepare mock of _correctMRestDurs()
         mockCorrectDurs.return_value = None
         # prepare mock of staffFromElement(), which just needs to return several unique things
-        staffFEreturns = [i for i in range(len(innerStaffs))]
+        staffFEreturns = list(range(len(innerStaffs)))
         mockStaffFE.side_effect = lambda *x, **y: staffFEreturns.pop(0)
         # prepare mock of stream.Voice
         mockVoice.return_value = mock.MagicMock(name='Voice')
@@ -3970,7 +3970,7 @@ class Test(unittest.TestCase):
         # prepare mock of _correctMRestDurs()
         mockCorrectDurs.return_value = None
         # prepare mock of staffFromElement(), which just needs to return several unique things
-        staffFEreturns = [i for i in range(len(innerStaffs))]
+        staffFEreturns = list(range(len(innerStaffs)))
         mockStaffFE.side_effect = lambda *x, **y: staffFEreturns.pop(0)
         # prepare mock of stream.Voice
         mockVoice.return_value = mock.MagicMock(name='Voice')
diff --git a/music21/metadata/testMetadata.py b/music21/metadata/testMetadata.py
index 7d0a70967..e8df5cb43 100644
--- a/music21/metadata/testMetadata.py
+++ b/music21/metadata/testMetadata.py
@@ -8,9 +8,6 @@ class Test(unittest.TestCase):
     # When `maxDiff` is None, `assertMultiLineEqual()` provides better errors.
     maxDiff = None
 
-    def runTest(self):
-        pass
-
     def testMetadataLoadCorpus(self):
         from music21 import converter
         from music21.musicxml import testFiles as mTF
diff --git a/music21/musicxml/testFiles.py b/music21/musicxml/testFiles.py
index d878ee715..7db1db6c0 100644
--- a/music21/musicxml/testFiles.py
+++ b/music21/musicxml/testFiles.py
@@ -16103,9 +16103,6 @@ def get(contentRequest):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         from music21 import converter
         for testMaterial in ALL[:1]:
diff --git a/music21/musicxml/testPrimitive.py b/music21/musicxml/testPrimitive.py
index 00a9d6181..66ec96f6c 100644
--- a/music21/musicxml/testPrimitive.py
+++ b/music21/musicxml/testPrimitive.py
@@ -17972,9 +17972,6 @@ def get(contentRequest):
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testBasic(self):
         # a basic test to make sure each parse
         from music21 import converter
diff --git a/music21/romanText/testFiles.py b/music21/romanText/testFiles.py
index 78952049e..957bbea20 100644
--- a/music21/romanText/testFiles.py
+++ b/music21/romanText/testFiles.py
@@ -694,9 +694,7 @@ ALL = [monteverdi_3_13, riemenschneider001, swv23, mozartK279, testSetMinorRootP
 
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
-
-    def runTest(self):
-        pass
+    pass
 
 
 # ------------------------------------------------------------------------------
diff --git a/music21/test/commonTest.py b/music21/test/commonTest.py
index 3192cb74b..1e5180d85 100644
--- a/music21/test/commonTest.py
+++ b/music21/test/commonTest.py
@@ -34,6 +34,27 @@ with warnings.catch_warnings():
 environLocal = environment.Environment('test.commonTest')
 
 
+# noinspection PyPackageRequirements
+def testImports():
+    '''
+    Test that all optional packages needed for test suites are installed
+    '''
+    try:
+        import scipy  # pylint: disable=unused-import
+    except ImportError as e:
+        raise ImportError('pip install scipy : needed for running test suites') from e
+
+    try:
+        from Levenshtein import StringMatcher  # pylint: disable=unused-import
+    except ImportError as e:
+        raise ImportError('pip install python-Levenshtein : needed for running test suites') from e
+
+    from music21.lily.translate import LilypondConverter, LilyTranslateException
+    try:
+        LilypondConverter()
+    except LilyTranslateException as e:
+        raise ImportError('lilypond must be installed to run test suites') from e
+
 def defaultDoctestSuite(name=None):
     globs = __import__('music21').__dict__.copy()
     docTestOptions = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
diff --git a/music21/test/multiprocessTest.py b/music21/test/multiprocessTest.py
index d605605d7..2ae245409 100644
--- a/music21/test/multiprocessTest.py
+++ b/music21/test/multiprocessTest.py
@@ -111,6 +111,8 @@ def mainPoolRunner(testGroup=('test',), restoreEnvironmentDefaults=False, leaveO
     '''
     Run all tests. Group can be test and/or external
     '''
+    commonTest.testImports()
+
     normalStdError = sys.stderr
 
     timeStart = time.time()
diff --git a/music21/test/testPerformance.py b/music21/test/testPerformance.py
index 9d6c42391..5e6e40600 100644
--- a/music21/test/testPerformance.py
+++ b/music21/test/testPerformance.py
@@ -32,9 +32,6 @@ environLocal = environment.Environment(_MOD)
 
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def runStreamIterationByIterator(self):
         '''Stream iteration by iterator
         '''
@@ -185,7 +182,7 @@ class Test(unittest.TestCase):
             p.transpose('p5', inPlace=True)
 
     def runParseABC(self):
-        '''Creating loading a large multiwork abc file
+        '''Creating loading a large multiple work abc file (han1)
         '''
         dummy = corpus.parse('essenFolksong/han1')
 
diff --git a/music21/test/testSerialization.py b/music21/test/testSerialization.py
index 1e5c17a28..89dddf7a7 100644
--- a/music21/test/testSerialization.py
+++ b/music21/test/testSerialization.py
@@ -24,11 +24,6 @@ environLocal = environment.Environment(_MOD)
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        '''Need a comment
-        '''
-        pass
-
     def testBasicC(self):
         from music21 import stream, note, converter
 
diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py
index 0e658b0f2..9b6ae5300 100644
--- a/music21/test/testSingleCoreAll.py
+++ b/music21/test/testSingleCoreAll.py
@@ -43,6 +43,7 @@ def main(testGroup=('test',), restoreEnvironmentDefaults=False, limit=None, verb
     >>> print(None)
     None
     '''
+    commonTest.testImports()
     s1 = commonTest.defaultDoctestSuite(__name__)
 
     modGather = commonTest.ModuleGather()
diff --git a/music21/test/testStream.py b/music21/test/testStream.py
index b729b168f..b468682d0 100644
--- a/music21/test/testStream.py
+++ b/music21/test/testStream.py
@@ -46,8 +46,6 @@ environLocal = environment.Environment(_MOD)
 
 # ------------------------------------------------------------------------------
 class TestExternal(unittest.TestCase):  # pragma: no cover
-    def runTest(self):
-        pass
 
     def testLilySimple(self):
         a = Stream()
@@ -278,9 +276,6 @@ class TestExternal(unittest.TestCase):  # pragma: no cover
 # ------------------------------------------------------------------------------
 class Test(unittest.TestCase):
 
-    def runTest(self):
-        pass
-
     def testAdd(self):
         a = Stream()
         for dummy in range(5):
@@ -1335,20 +1330,17 @@ class Test(unittest.TestCase):
         p1 = a.parts[0]
         # get measure by class; this will not manipulate the measure
         mExRaw = p1.getElementsByClass('Measure')[5]
-        self.assertEqual(str([n for n in mExRaw.notes]),
-                         '[<music21.note.Note B>, <music21.note.Note D>]')
+        self.assertEqual(str(list(mExRaw.notes)), '[<music21.note.Note B>, <music21.note.Note D>]')
         self.assertEqual(len(mExRaw.flat), 3)
 
         # get measure by using method; this will add elements
         mEx = p1.measure(6)
-        self.assertEqual(str([n for n in mEx.notes]),
-                         '[<music21.note.Note B>, <music21.note.Note D>]')
+        self.assertEqual(str(list(mEx.notes)), '[<music21.note.Note B>, <music21.note.Note D>]')
         self.assertEqual(len(mEx.flat), 3)
 
         # make sure source has not changed
         mExRaw = p1.getElementsByClass('Measure')[5]
-        self.assertEqual(str([n for n in mExRaw.notes]),
-                         '[<music21.note.Note B>, <music21.note.Note D>]')
+        self.assertEqual(str(list(mExRaw.notes)), '[<music21.note.Note B>, <music21.note.Note D>]')
         self.assertEqual(len(mExRaw.flat), 3)
 
         # test measures with no measure numbers
@@ -1896,7 +1888,7 @@ class Test(unittest.TestCase):
 
         # m2.show()
 
-        match = str([n for n in s.flat.notesAndRests])
+        match = str(list(s.flat.notesAndRests))
         self.assertEqual(match, '[<music21.note.Rest rest>, <music21.note.Note C>, '
                                   + '<music21.note.Rest rest>, <music21.note.Rest rest>, '
                                   + '<music21.note.Note C>, <music21.note.Rest rest>]')
@@ -5439,13 +5431,13 @@ class Test(unittest.TestCase):
         s.insert(0, v2)
 
         sPost = s.makeRests(fillGaps=True, inPlace=False)
-        self.assertEqual(str([n for n in sPost.voices[0].notesAndRests]),
+        self.assertEqual(str(list(sPost.voices[0].notesAndRests)),
                          '[<music21.note.Rest rest>, <music21.note.Note C>, '
                          + '<music21.note.Rest rest>, <music21.note.Note C>, '
                          + '<music21.note.Rest rest>, <music21.note.Note C>, '
                          + '<music21.note.Rest rest>, <music21.note.Note C>, '
                          + '<music21.note.Rest rest>]')
-        self.assertEqual(str([n for n in sPost.voices[1].notesAndRests]),
+        self.assertEqual(str(list(sPost.voices[1].notesAndRests)),
                          '[<music21.note.Rest rest>, <music21.note.Note C>, '
                          + '<music21.note.Rest rest>, <music21.note.Note C>, '
                          + '<music21.note.Rest rest>, <music21.note.Note C>, '
@@ -5596,23 +5588,23 @@ class Test(unittest.TestCase):
         # make sure we have what we started with
         self.assertEqual(len(s2.parts[0].flat.notesAndRests),
                          len(s0.parts[0].flat.notesAndRests))
-        self.assertEqual(str([n for n in s2.parts[0].flat.notesAndRests]),
-                         str([n for n in s0.parts[0].flat.notesAndRests]))
+        self.assertEqual(str(list(s2.parts[0].flat.notesAndRests)),
+                         str(list(s0.parts[0].flat.notesAndRests)))
 
         self.assertEqual(len(s2.parts[1].flat.notesAndRests),
                          len(s0.parts[1].flat.notesAndRests))
-        self.assertEqual(str([n for n in s2.parts[1].flat.notesAndRests]),
-                         str([n for n in s0.parts[1].flat.notesAndRests]))
+        self.assertEqual(str(list(s2.parts[1].flat.notesAndRests)),
+                         str(list(s0.parts[1].flat.notesAndRests)))
 
         self.assertEqual(len(s2.parts[2].flat.notesAndRests),
                          len(s0.parts[2].flat.notesAndRests))
-        self.assertEqual(str([n for n in s2.parts[2].flat.notesAndRests]),
-                         str([n for n in s0.parts[2].flat.notesAndRests]))
+        self.assertEqual(str(list(s2.parts[2].flat.notesAndRests)),
+                         str(list(s0.parts[2].flat.notesAndRests)))
 
         self.assertEqual(len(s2.parts[3].flat.notesAndRests),
                          len(s0.parts[3].flat.notesAndRests))
-        self.assertEqual(str([n for n in s2.parts[3].flat.notesAndRests]),
-                         str([n for n in s0.parts[3].flat.notesAndRests]))
+        self.assertEqual(str(list(s2.parts[3].flat.notesAndRests)),
+                         str(list(s0.parts[3].flat.notesAndRests)))
 
         # try on a built Stream that has no Measures
         # build a stream
@@ -5630,13 +5622,13 @@ class Test(unittest.TestCase):
         s1 = s0.voicesToParts()
         self.assertEqual(len(s1.parts), 3)
         # self.assertEqual(len(s1.parts[0].flat), len(v1.flat))
-        self.assertEqual([e for e in s1.parts[0].flat], [e for e in v1.flat])
+        self.assertEqual(list(s1.parts[0].flat), list(v1.flat))
 
         self.assertEqual(len(s1.parts[1].flat), len(v2.flat))
-        self.assertEqual([e for e in s1.parts[1].flat], [e for e in v2.flat])
+        self.assertEqual(list(s1.parts[1].flat), list(v2.flat))
 
         self.assertEqual(len(s1.parts[2].flat), len(v3.flat))
-        self.assertEqual([e for e in s1.parts[2].flat], [e for e in v3.flat])
+        self.assertEqual(list(s1.parts[2].flat), list(v3.flat))
 
         # s1.show()
 
@@ -7822,7 +7814,7 @@ class Test(unittest.TestCase):
         self.assertEqual(len(s.notes), 12)
         self.assertEqual(len(s.variants), 1)
 
-        self.assertEqual(str([p for p in s.variants[0].elements]),
+        self.assertEqual(str(list(s.variants[0].elements)),
                          '[<music21.note.Note D>, <music21.note.Note D>]')
 
     def testActivateVariantsBySpanA(self):
diff --git a/music21/test/timeGraphImportStar.py b/music21/test/timeGraphImportStar.py
index 592b9a92b..b70fd0320 100644
--- a/music21/test/timeGraphImportStar.py
+++ b/music21/test/timeGraphImportStar.py
@@ -162,7 +162,6 @@
 #                 # on MacOS, fd returns an int, like 3, when this is called
 #                 # in some context (specifically, programmatically in a
 #                 # TestExternal class. the fp is still valid and works
-#                 # TODO: this did not work on MacOS 10.6.8 w/ py 2.7
 #                     pass
 #                 else:
 #                     fd.close()
diff --git a/music21/test/timeGraphs.py b/music21/test/timeGraphs.py
index 90cd19605..76c46aacf 100644
--- a/music21/test/timeGraphs.py
+++ b/music21/test/timeGraphs.py
@@ -611,7 +611,6 @@
 #                 # on MacOS, fd returns an int, like 3, when this is called
 #                 # in some context (specifically, programmatically in a
 #                 # TestExternal class. the fp is still valid and works
-#                 # TODO: this did not work on MacOS 10.6.8 w/ py 2.7
 #                     pass
 #                 else:
 #                     fd.close()
diff --git a/music21/test/warningMultiprocessTest.py b/music21/test/warningMultiprocessTest.py
index 46dee4ca0..6f403b469 100644
--- a/music21/test/warningMultiprocessTest.py
+++ b/music21/test/warningMultiprocessTest.py
@@ -12,7 +12,7 @@ import sys
 def warn_with_traceback(message, category, filename, lineno, file=None, line=None):
     log = file if hasattr(file, 'write') else sys.stderr
     if 'music21' in filename:
-        # do not give stack trace for matplotlib pendingdeprecation, etc.
+        # do not give stack trace for matplotlib PendingDeprecation, etc.
         traceback.print_stack(file=log)
     log.write(warnings.formatwarning(message, category, filename, lineno, line))