Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@ source =
music21/

omit =
music21/test/timeGraph*
music21/test/*
music21/configure.py
music21/figuredBass/examples.py
music21/alpha/*
dist/dist.py

[report]
exclude_lines =
import music21
music21.mainTest()
if TYPE_CHECKING:
if t.TYPE_CHECKING:
if __name__ == .__main__.:
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/maincheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ on:
branches:
- '*'

env:
# We run coverage on a middle supported Python so that failures on newest and
# oldest versions return first.
PY_VERSION_WITH_COVERAGE: '3.13'

jobs:
run_tests:
runs-on: ubuntu-latest
Expand All @@ -35,10 +40,14 @@ jobs:
- name: Setup Lilypond
run: uv run python -c 'from music21 import environment; environment.UserSettings()["lilypondPath"] = "/home/runner/bin/lilypond"'
- name: Run Main Test script
if: ${{ matrix.python-version != env.PY_VERSION_WITH_COVERAGE }}
run: uv run python -c 'from music21.test.testSingleCoreAll import ciMain as ci; ci()'
- name: Run Main Test script with coverage
if: ${{ matrix.python-version == env.PY_VERSION_WITH_COVERAGE }}
run: uv run coverage run -m music21.test.testSingleCoreAll ci
- name: Coveralls
if: ${{ matrix.python-version == '3.13' }}
env: # when changing number above also change coverageM21.getCoverage
if: ${{ matrix.python-version == env.PY_VERSION_WITH_COVERAGE }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_SERVICE_NAME: github
run: uv run coveralls
Expand Down
8 changes: 3 additions & 5 deletions music21/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -1390,20 +1390,18 @@ def testNonTraditional(self):
'''
AI-assisted (Claude).
'''
from music21 import key

ks = key.KeySignature(3)
ks = KeySignature(3)
self.assertFalse(ks.isNonTraditional)

ks = key.KeySignature()
ks = KeySignature()
ks.isNonTraditional = True
ks.alteredPitches = [pitch.Pitch('E`')]
self.assertEqual(repr(ks), '<music21.key.KeySignature of pitches: [E`]>')
self.assertEqual(ks.accidentalByStep('E'), pitch.Accidental('half-flat'))

# a non-traditional key signature is not equal to the C-major signature
# it shares a `sharps` count with.
self.assertNotEqual(ks, key.KeySignature())
self.assertNotEqual(ks, KeySignature())

def testSharpsNoneDeprecated(self):
'''
Expand Down
28 changes: 2 additions & 26 deletions music21/test/commonTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
import copy
import doctest
import importlib
import importlib.util
import os
import sys
import typing
import types
import unittest.runner
Expand Down Expand Up @@ -61,28 +59,6 @@ def testCopyAll(testInstance: unittest.TestCase, globals_: typing.Dict[str, typi
testInstance.fail(f'Could not deepcopy obj {part}: {e}')


def load_source(name: str, path: str) -> types.ModuleType:
'''
Replacement for deprecated imp.load_source()

Thanks to:
https://github.com/epfl-scitas/spack for pointing out the
important missing "spec.loader.exec_module(module)" line.
'''
spec = importlib.util.spec_from_file_location(name, path)
if spec is None or spec.loader is None:
raise FileNotFoundError(f'No such file or directory: {path!r}')
if name in sys.modules:
module = sys.modules[name]
else:
module = importlib.util.module_from_spec(spec)
if module is None:
raise FileNotFoundError(f'No such file or directory: {path!r}')
sys.modules[name] = module
spec.loader.exec_module(module)

return module

# noinspection PyPackageRequirements
def testImports():
'''
Expand Down Expand Up @@ -417,11 +393,11 @@ def getModule(self, fp, restoreEnvironmentDefaults=False):
if skip:
return None

name = self._getNamePeriod(fp, addM21=False)
name = self._getNamePeriod(fp, addM21=True)

try:
with warnings.catch_warnings():
mod = load_source(name, fp)
mod = importlib.import_module(name)
except Exception as excp: # pylint: disable=broad-exception-caught
environLocal.warn(['failed import:', name, '\t', fp, '\n',
'\tEXCEPTION:', str(excp).strip()])
Expand Down
70 changes: 0 additions & 70 deletions music21/test/coverageM21.py

This file was deleted.

21 changes: 6 additions & 15 deletions music21/test/testSingleCoreAll.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,11 @@
from music21 import environment

from music21.test import commonTest
from music21.test import coverageM21
from music21.test import testRunner

environLocal = environment.Environment('test.testSingleCoreAll')


# this is designed to be None for all but one system and a Coverage() object
# for one system.
cov = coverageM21.getCoverage()


def main(testGroup: Sequence[str] = ('test',),
restoreEnvironmentDefaults=False,
limit: bool|None = None,
Expand Down Expand Up @@ -118,8 +112,6 @@ def main(testGroup: Sequence[str] = ('test',),
runner = unittest.TextTestRunner(verbosity=verbosity)
finalTestResults = runner.run(s1)

coverageM21.stopCoverage(cov)

if (finalTestResults.errors
or finalTestResults.failures
or finalTestResults.unexpectedSuccesses):
Expand All @@ -136,18 +128,17 @@ def ciMain():
# and TestExternal (without doctests) with show=False
# exits with the aggregated returnCode
returnCodeTest = main(testGroup=('test',), verbosity=1)
# restart coverage if running main() twice
coverageM21.startCoverage(cov)
returnCodeExternal = main(testGroup=('external',), verbosity=1, show=False)
sys.exit(returnCodeTest + returnCodeExternal)


# ------------------------------------------------------------------------------
if __name__ == '__main__':
# if optional command line arguments are given, assume they are
# test group arguments
if len(sys.argv) >= 2:
unused_returnCode = main(sys.argv[1:])
# 'ci' runs what GitHub Actions runs; other arguments are test group names.
if len(sys.argv) < 2:
main()
elif sys.argv[1] == 'ci':
ciMain()
else:
unused_returnCode = main()
main(sys.argv[1:])

Loading