From 93846fe7e9f3a8f7deb9e3e392b9fc1a509d4491 Mon Sep 17 00:00:00 2001 From: hill Date: Fri, 7 Feb 2025 18:51:20 +0000 Subject: [PATCH 001/312] remove type ignore --- music21/pitch.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index eb22baf9b5..ba3ad23e73 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1981,8 +1981,7 @@ def __init__(self, else: self.microtone = Microtone(microtone) if pitchClass is not None: - # type ignore until https://github.com/python/mypy/issues/3004 resolved - self.pitchClass = pitchClass # type: ignore + self.pitchClass = pitchClass if fundamental is not None: self.fundamental = fundamental if midi is not None: From 5dadb65f8a11458bc4f340445bd313c7e1bb5c17 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 25 May 2026 23:31:31 -1000 Subject: [PATCH 002/312] next dist system for 10.5 --- RELEASING.md | 128 +++++++++++++++++++++++++++ dist/README.txt | 14 +++ dist/dist.py | 171 ------------------------------------ music21/__init__.py | 3 + music21/test/coverageM21.py | 2 +- scripts/buildNoCorpus.py | 79 +++++++++++++++++ 6 files changed, 225 insertions(+), 172 deletions(-) create mode 100644 RELEASING.md create mode 100644 dist/README.txt delete mode 100644 dist/dist.py create mode 100644 scripts/buildNoCorpus.py diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000000..141a62b53e --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,128 @@ +# Releasing music21 + +These are the steps for making a new music21 release. + +## Setup + +Run `uv sync`. This installs all the devtools the steps below need. + +Install separately: + +- `pandoc` — brew install pandoc +- `markdown` — `uv pip install markdown` (or `pip install markdown`). + +## Steps + +1. Update the `VERSION` in `music21/_version.py` and the single test case in + `base.py`. + +2. Run `corpus.corpora.CoreCorpus().cacheMetadata()`. + For a major change that affects parsing, run + `corpus.corpora.CoreCorpus().rebuildMetadataCache()` instead + (~2 min on an M4). Either of these *may* change a lot of tests in `corpus`, + `metadata`, etc., so do not skip the next step. + +3. Run `uv run documentation/testDocumentation.py`, then fix errors. + - You cannot check whether fixed tests work while it is still running. + - This takes a while, runs single-core, and then almost always needs code + patches, so allocate time (~2 min on an M4). Start working on the + announcement while it runs. + - You cannot run steps 2 and 3 in parallel, since metadata changes often + cause documentation changes. + +4. `uv run music21/test/warningMultiprocessTest.py` for the lowest and highest + supported Python versions — fix all warnings. + +5. Run `from music21.test import treeYield`, then: + - `treeYield.find_all_non_hashable_m21objects()` and check that the returned + set is empty. (It prints a bunch of module names, but only the final + `set()` being empty matters.) + - Then the same for `treeYield.find_all_non_default_instantiation_m21objects()`. + If that one isn't empty it is not a bug, but it's nice to keep it so that + `Music21Object`s don't need args. + +6. Commit and wait for results on GitHub Actions. + (Normally not necessary — it's slower and mostly duplicates + `multiprocessTest` — but should be done before making a release.) + +7. `uv run documentation/make.py clean` (skip on minor version changes). + You may need to create a `documentation/build` directory first. + +8. `uv run documentation/make.py linkcheck`. + Some persistent errors that actually work are listed in `conf.py` under + `linkcheck_ignore`. + +9. `uv run documentation/make.py` -- build the documentation HTML files and supports. + +10. `uv run documentation/upload.py` — zips `documentation/build/html` and + uploads it to `music21.org/music21docs/`. Requires MSAC's private key. + +11. Zip up `documentation/build/html` and get ready to upload/delete it (your + desktop or wherever is fine). Rename to `music21-X.Y.Z-docs.zip` + (skip for Alpha/Beta). + +12. From the music21 main folder (not a subfolder) run `uv run hatch build`. + This builds `dist/music21-X.Y.Z.tar.gz` and + `dist/music21-X.Y.Z-py3-none-any.whl`. + +13. Run `uv run scripts/buildNoCorpus.py`: it builds the no-corpus version of + music21 (needs Python 3.12 or higher). + **Do not run this on a PC**, or the Mac `.tar.gz` might end up with incorrect + permissions. (TODO: To lift this restriction, rewrite removeCorpus() to run tar-to-tar with + `tarfile`, copying each TarInfo so Unix modes survive) + +14. PR and commit to GitHub at this point w/ a commit comment of the new + version, then don't change anything until the next step is done. Merge to + main/master. (`.gitignore` avoids uploading the large files created in `dist/`.) + +15. Switch back to the master/main branch (or whatever main version you are + releasing). + +16. Tag the commit: `git tag -a vX.Y.Z -m "music21 vX.Y.Z"`. + Don't forget the `v` in the release tag. Sanity-check that the correct commit + was tagged: `git log`. + +17. Push that tag: `git push origin vX.Y.Z`. + +18. Create a new release on GitHub (using the tag just created) and upload the + non-wheel files created here and the docs. + Drag in this order: `.tar.gz` (wait to finish), `-docs.zip` (wait), + `no-corpus.tar.gz`. Finish this before doing the next step, even though it + looks like it could be done in parallel. + +19. Upload the tar.gz file to PyPI: `uv run twine upload music21-X.Y.Z.tar.gz`. + +20. Do the same for the `.whl` file (but not for the no-corpus file). + + - You will need a `~/.pypirc` file: + + ```ini + [distutils] + index-servers = + pypi + + [pypi] + username:__token__ + password:pypi-API_TOKEN + ``` + + The "password" is the API token you just created — if you lose this file you + also lose the API token and have to create it again. This is all very poorly + documented. It is not the "Unique identifier" you see in the "API Tokens" + tab on the "Your Account" page — super confusing. + +21. Delete the two `.tar.gz` files and the `.whl` file in `dist/` (and the docs). + +22. For starting a new major release, create a GitHub branch to preserve the old + one for patches, etc., especially during beta releases. + +23. Immediately increment the number in `_version.py` and run tests on it to + prepare for the next release. + +24. Announce on the blog and to the list. + +--- + +*History: these steps and `scripts/buildNoCorpus.py` were a single `dist/dist.py` +until May 2026, split out so they would stop living in the disposable `dist/` +directory.* diff --git a/dist/README.txt b/dist/README.txt new file mode 100644 index 0000000000..106d9898dd --- /dev/null +++ b/dist/README.txt @@ -0,0 +1,14 @@ +This directory holds throwaway build output (.tar.gz, .whl) and is in +.gitignore. It is safe to `trash dist` -- nothing here is source. + +The old dist/dist.py was split and moved out so it would stop living in a +directory people routinely delete: + + - The release runbook (how to cut and upload a release) is now: + RELEASING.md (repository root) + + - The no-corpus distribution builder (removeCorpus) is now: + scripts/buildNoCorpus.py + run with: uv run scripts/buildNoCorpus.py + +Moved 2026-05-25. diff --git a/dist/dist.py b/dist/dist.py deleted file mode 100644 index e3b85cb72b..0000000000 --- a/dist/dist.py +++ /dev/null @@ -1,171 +0,0 @@ -# ------------------------------------------------------------------------------ -# Name: dist.py -# Purpose: Distribution and uploading script -# -# Authors: Christopher Ariza -# Michael Scott Asato Cuthbert -# -# Copyright: Copyright © 2010-2025 Michael Scott Asato Cuthbert -# License: BSD, see license.txt -# ------------------------------------------------------------------------------ -''' -Builds various kinds of music21 distribution files and uploads them to PyPI and GoogleCode. - -To do a release, - -1. update the VERSION in _version.py and the single test case in base.py. -2. run `corpus.corpora.CoreCorpus().cacheMetadata()`. - for a major change that affects parsing run corpus.corpora.CoreCorpus().rebuildMetadataCache() - (2 min on M4) -- either of these MAY change a - lot of tests in corpus, metadata, etc. so don't skip the next step! -3. IMPORTANT: `uv run documentation/testDocumentation.py` and afterwards fix errors [*] - -[*] you will need pytest, docutils, nbval installed (along with ipython and jupyter), -you cannot check to see if fixed tests work while it is running. -This takes a while and runs single core, and then almost always needs code patches -so allocate time (2 min on M4). Start working on the announcement while it's running. -Cannot run 2 and 3 in parallel, since often metadata changes cause documentation changes. - -4. `uv run music21/test/warningMultiprocessTest.py` for lowest and highest Py version -- fix all warnings! -5. run `from music21.test import treeYield - and then run `treeYield.find_all_non_hashable_m21objects()` and check that the set returned is - empty. Note -- it will print a bunch of module names, but only the final `set()` being empty matters. - Then do the same for `treeYield.find_all_non_default_instantiation_m21objects()`. If that one isn't - empty it is not a bug, but it would be nice to keep is so Music21Objects don't need args. -6. commit and wait for results on GitHub Actions - (normally not necessary, because it's slower and mostly duplicates multiprocessTest, - but should be done before making a release). -7. `uv run documentation/make.py clean` (skip on minor version changes) -- you may need to make a - documentation/build directory first. -8. `uv run documentation/make.py linkcheck` [*] - some persistent errors that actually work are in the conf.py file under linkcheck_ignore -9. `uv run documentation/make.py` [*] -9b. `uv run documentation/upload.py` - -[*] you will need sphinx, Jupyter (uv will install it), markdown, and pandoc (.dmg) installed - -10. move music21 documentation/build/html to music21.org/music21docs/ - via Amazon S3 (contact MSAC for authentication if need be) (MSAC has a program: - combine_sync/deploy.py that will do this automatically. - -11. zip up documentation/build/html and get ready to upload/delete it (you can put on your - desktop or wherever you like). - Rename to music21-9.5.0-docs.zip (skip for Alpha/Beta) - -12. From the music21 main folder (not subfolder) run "hatch build" -- - requires hatch to be installed "pip install hatch" -- brew version of hatch - was giving Environment `default` is incompatible messages recently. (mysql? why relevant?) - - This builds the dist/music21-9.3.0.tar.gz and dist/music21-9.3.0-py3-none-any.whl - files. That used to be what *this* script did, but now hatch does it better! - -13. Run this (dist.py) file: it builds the no-corpus version of music21. - (need Python 3.12 or higher) - DO NOT RUN THIS ON A PC or the Mac .tar.gz might have an incorrect permission if you do. - -14. PR and Commit to GitHub at this point w/ commit comment of the new version, - then don't change anything until the next step is done. Merge to main/master - (.gitignore will avoid uploading the large files created here.) - -15. Switch back to master/main branch (or whatever main version we are releasing) - -16. Tag the commit: git tag -a vX.Y.Z -m "music21 vX.Y.Z" - Don't forget the "v" in the release tag. - Sanity check that the correct commit was tagged: git log - -17. Push that tag: git push origin vX.Y.Z - -18. Create a new release on GitHub (using the tag just created) and upload the - non-wheel files created here and docs. - - Drag in this order: .tar.gz (wait to finish), -docs.zip (wait), no-corpus.tar.gz - - Finish this before doing the next step, even though it looks like it could be done in parallel. - -19a. Upload the tar.gz file to PyPI with "twine upload music21-9.3.0.tar.gz" [*] - -19b. Do the same for the whl file (but not for the no-corpus file) [*] - - [*] Requires twine to be installed and up-to-date (pip install --upgrade twine) - - You will need a file called ~/.pypirc with - - [distutils] - index-servers = - pypi - - [pypi] - username:__token__ - password:pypi-API_TOKEN - - The "password" is the API token you've just created -- if you lose the file you - will also lose the API token. and have to create it again. This is all very - poorly documented. This is Not the "Unique identifier" that you see in the - "API Tokens" tab on the "Your Account" page -- super confusing. - -20. Delete the two .tar.gz files and .whl file in dist. (and the docs) - -21. For starting a new major release create a GitHub branch to preserve the old one - for patches, etc. esp. during beta releases. - -22. Immediately increment the number in _version.py and run tests on it here - to prepare for next release. - -23. Announce on the blog and to the list. -''' -import os -import shutil -import tarfile - -from music21._version import __version__ as version -from music21.common.pathTools import getRootFilePath, getCorpusContentDirs - -def removeCorpus(): - ''' - Remove the corpus from a compressed file (.tar.gz) and - create a new music21-noCorpus version. - - Return the completed file path of the newly created edition. - - NOTE: this function works only with Posix systems. - ''' - fp = getRootFilePath() / 'dist' / ('music21-' + version + '.tar.gz') - fpDir, fn = os.path.split(str(fp)) - - # this has .tar.gz extension; this is the final completed package - fnDst = fn.replace('music21', 'music21-noCorpus') - fpDst = os.path.join(fpDir, fnDst) - # remove file extensions - fnDstDir = fnDst.replace('.tar.gz', '') - fpDstDir = os.path.join(fpDir, fnDstDir) - - file = tarfile.open(fp) - file.extractall(fpDir, filter='data') # note -- this requires 3.12+ but that's okay - file.close() - - os.rename(fpDstDir.replace('-noCorpus', ''), fpDstDir) - - # remove files, updates manifest - for fn in getCorpusContentDirs(): - fp = os.path.join(fpDstDir, 'music21', 'corpus', fn) - shutil.rmtree(fp) - - fp = os.path.join(fpDstDir, 'music21', 'corpus', '_metadataCache') - shutil.rmtree(fp) - - # compress dst dir to dst file path name - # need the -C flag to set relative dir - # just name of dir - cmd = f'tar -C {fpDir} -czf {fpDst} {fnDstDir}/' - os.system(cmd) - - # # remove directory that was compressed - if os.path.exists(fpDstDir): - shutil.rmtree(fpDstDir) - - return fpDst # full path with extension - - -# ------------------------------------------------------------------------------ -if __name__ == '__main__': - removeCorpus() diff --git a/music21/__init__.py b/music21/__init__.py index 7cbd1faa5c..e743363d7d 100644 --- a/music21/__init__.py +++ b/music21/__init__.py @@ -81,6 +81,8 @@ __all__ = [ + '__version__', + # testing routines 'mainTest', @@ -177,6 +179,7 @@ 'Music21Object', 'ElementWrapper', 'VERSION', + 'VERSION_STR', ] # ------------------------------------------------------------------------------ diff --git a/music21/test/coverageM21.py b/music21/test/coverageM21.py index 2ea878894e..7d628ca687 100644 --- a/music21/test/coverageM21.py +++ b/music21/test/coverageM21.py @@ -13,7 +13,7 @@ import sys omit_modules = [ - 'dist/dist.py', + 'scripts/buildNoCorpus.py', 'music21/test/*', 'music21/configure.py', 'music21/figuredBass/examples.py', diff --git a/scripts/buildNoCorpus.py b/scripts/buildNoCorpus.py new file mode 100644 index 0000000000..f058152224 --- /dev/null +++ b/scripts/buildNoCorpus.py @@ -0,0 +1,79 @@ +# ------------------------------------------------------------------------------ +# Name: buildNoCorpus.py +# Purpose: Build the no-corpus music21 distribution (.tar.gz) +# +# Authors: Christopher Ariza +# Michael Scott Asato Cuthbert +# +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert +# License: BSD, see license.txt +# ------------------------------------------------------------------------------ +''' +Build the "no-corpus" edition of music21: take a built `.tar.gz` and +strip out the corpus (which carries licenses such as +CC-SA or old copyrighted files are not fully public domain in the entire world +like music from 1935 but in Mexico and other life+100 countries) +to produce a `music21-noCorpus-*.tar.gz` suitable for Linux distros that +require a fully PD package. + +Part of `RELEASING.md` at the repository root:: + + uv run scripts/buildNoCorpus.py + +This file and `removeCorpus()` were reorganized out of the old `dist/dist.py`. +''' +import os +import shutil +import tarfile + +from music21 import __version__ as version +from music21.common.pathTools import getRootFilePath, getCorpusContentDirs + +def removeCorpus(): + ''' + Remove the corpus from a compressed file (.tar.gz) and + create a new music21-noCorpus version. + + Return the completed file path of the newly created edition. + + NOTE: this function works only with Posix systems. + ''' + fp = getRootFilePath() / 'dist' / ('music21-' + version + '.tar.gz') + fpDir, fn = os.path.split(str(fp)) + + # this has .tar.gz extension; this is the final completed package + fnDst = fn.replace('music21', 'music21-noCorpus') + fpDst = os.path.join(fpDir, fnDst) + # remove file extensions + fnDstDir = fnDst.replace('.tar.gz', '') + fpDstDir = os.path.join(fpDir, fnDstDir) + + with tarfile.open(fp) as file: + file.extractall(fpDir, filter='data') # note -- this requires 3.12+ but that's okay + + os.rename(fpDstDir.replace('-noCorpus', ''), fpDstDir) + + # remove files, updates manifest + for fn in getCorpusContentDirs(): + fp = os.path.join(fpDstDir, 'music21', 'corpus', fn) + shutil.rmtree(fp) + + fp = os.path.join(fpDstDir, 'music21', 'corpus', '_metadataCache') + shutil.rmtree(fp) + + # compress dst dir to dst file path name + # need the -C flag to set relative dir + # just name of dir + cmd = f'tar -C {fpDir} -czf {fpDst} {fnDstDir}/' + os.system(cmd) + + # # remove directory that was compressed + if os.path.exists(fpDstDir): + shutil.rmtree(fpDstDir) + + return fpDst # full path with extension + + +# ------------------------------------------------------------------------------ +if __name__ == '__main__': + removeCorpus() From dd34c99c249aaa492d0a7df5e03eed97679312dd Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 25 May 2026 23:43:42 -1000 Subject: [PATCH 003/312] update version to 10.4 beta --- music21/_version.py | 2 +- music21/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index 86688210ff..5d13a713d8 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '10.3.0' +__version__ = '10.4.0b1' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 1927faaab2..d208ae7b70 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'10.3.0' +'10.4.0b1' Alternatively, after doing a complete import, these classes are available under the module "base": From cbcd56c91f5a8f1016753255d11208bb8cd02185 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 26 May 2026 00:12:20 -1000 Subject: [PATCH 004/312] improve RELEASING.md --- RELEASING.md | 54 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 141a62b53e..3ed452dba4 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -23,23 +23,23 @@ Install separately: `metadata`, etc., so do not skip the next step. 3. Run `uv run documentation/testDocumentation.py`, then fix errors. - - You cannot check whether fixed tests work while it is still running. - - This takes a while, runs single-core, and then almost always needs code - patches, so allocate time (~2 min on an M4). Start working on the - announcement while it runs. - - You cannot run steps 2 and 3 in parallel, since metadata changes often - cause documentation changes. + - You cannot check whether fixed tests work while it is still running. + - This takes a while, runs single-core, and then almost always needs code + patches, so allocate time (~2 min on an M4). Start working on the + announcement while it runs. + - You cannot run steps 2 and 3 in parallel, since metadata changes often + cause documentation changes. 4. `uv run music21/test/warningMultiprocessTest.py` for the lowest and highest supported Python versions — fix all warnings. 5. Run `from music21.test import treeYield`, then: - - `treeYield.find_all_non_hashable_m21objects()` and check that the returned - set is empty. (It prints a bunch of module names, but only the final - `set()` being empty matters.) - - Then the same for `treeYield.find_all_non_default_instantiation_m21objects()`. - If that one isn't empty it is not a bug, but it's nice to keep it so that - `Music21Object`s don't need args. + - `treeYield.find_all_non_hashable_m21objects()` and check that the returned + set is empty. (It prints a bunch of module names, but only the final + `set()` being empty matters.) + - Then the same for `treeYield.find_all_non_default_instantiation_m21objects()`. + If that one isn't empty it is not a bug, but it's nice to keep it so that + `Music21Object`s don't need args. 6. Commit and wait for results on GitHub Actions. (Normally not necessary — it's slower and mostly duplicates @@ -86,7 +86,7 @@ Install separately: 18. Create a new release on GitHub (using the tag just created) and upload the non-wheel files created here and the docs. - Drag in this order: `.tar.gz` (wait to finish), `-docs.zip` (wait), + Drag in this order: `.tar.gz` (wait to finish), `-docs.zip` (wait), `.whl` (wait), and `no-corpus.tar.gz`. Finish this before doing the next step, even though it looks like it could be done in parallel. @@ -94,29 +94,29 @@ Install separately: 20. Do the same for the `.whl` file (but not for the no-corpus file). - - You will need a `~/.pypirc` file: + - You will need a `~/.pypirc` file: - ```ini - [distutils] - index-servers = - pypi + ```ini + [distutils] + index-servers = + pypi - [pypi] - username:__token__ - password:pypi-API_TOKEN - ``` + [pypi] + username:__token__ + password:pypi-API_TOKEN + ``` - The "password" is the API token you just created — if you lose this file you - also lose the API token and have to create it again. This is all very poorly - documented. It is not the "Unique identifier" you see in the "API Tokens" - tab on the "Your Account" page — super confusing. + - The "password" is the API token you just created — if you lose this file you + also lose the API token and have to create it again. This is all very poorly + documented. It is not the "Unique identifier" you see in the "API Tokens" + tab on the "Your Account" page — super confusing. 21. Delete the two `.tar.gz` files and the `.whl` file in `dist/` (and the docs). 22. For starting a new major release, create a GitHub branch to preserve the old one for patches, etc., especially during beta releases. -23. Immediately increment the number in `_version.py` and run tests on it to +23. Immediately increment the number in `_version.py` and run test on VERSION_STR in base.py to prepare for the next release. 24. Announce on the blog and to the list. From 2cacfca64eb257947c3f69470b66b31fa70335ec Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Tue, 30 Jun 2026 11:41:20 +0200 Subject: [PATCH 005/312] Preserve microtones through Interval transposition Pitch.transpose() corrupted, or crashed on, pitches carrying a microtone. _diatonicTransposePitch measured interval2 from the microtonal source pitch, so the source cents leaked into halfStepsToFix and were dumped onto the accidental: e.g. C4(+30c) transposed by a perfect unison became C~4(-20c), and G#5(+50c) by an augmented unison raised AccidentalException ('2.5 is not a supported accidental type'). The dead 'centsOrigin' comment in the method showed this was an unfinished intent. Peel the source microtone off halfStepsToFix before it sets the accidental, then restore it as a microtone, guarded by 'if centsOrigin' so the common non-microtonal (performance-critical) path is unchanged. --- music21/interval.py | 14 +++++++++-- music21/test/test_interval.py | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/music21/interval.py b/music21/interval.py index e90ad75a7e..d145c23c45 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -3460,7 +3460,7 @@ def _diatonicTransposePitch(self, pitch1 = p pitch2 = copy.deepcopy(pitch1) oldDiatonicNum = pitch1.diatonicNoteNum - # centsOrigin = pitch1.microtone.cents # unused!! + centsOrigin = pitch1.microtone.cents distanceToMove = self.diatonic.generic.staffDistance newDiatonicNumber = oldDiatonicNum + distanceToMove @@ -3475,8 +3475,14 @@ def _diatonicTransposePitch(self, # We have the right note name but not the right accidental interval2 = Interval(pitch1, pitch2) - # halfStepsToFix already has any microtones + # interval2 is measured from the microtonal pitch1, so halfStepsToFix + # carries the source microtone. Peel it off so it adjusts only the + # accidental; it is restored as a microtone once the accidental is set. + # Without this, the leaked cents corrupt the spelling (e.g. a +30c pitch + # transposed by P1 becomes a half-sharp) or raise AccidentalException. halfStepsToFix = self.chromatic.semitones - interval2.chromatic.semitones + if centsOrigin: + halfStepsToFix = round(halfStepsToFix - centsOrigin / 100.0, 9) # environLocal.printDebug(['self', self, 'halfStepsToFix', halfStepsToFix, # 'centsOrigin', centsOrigin, 'interval2', interval2]) @@ -3527,6 +3533,10 @@ def _diatonicTransposePitch(self, and oldPitch2Accidental.name == 'natural'): pitch2.accidental = oldPitch2Accidental + # restore the source microtone that was peeled off halfStepsToFix above + if centsOrigin: + pitch2.microtone = pitch2.microtone.cents + centsOrigin + if useImplicitOctave: pitch2.octave = None diff --git a/music21/test/test_interval.py b/music21/test/test_interval.py index da17bb5b35..a747bb41a4 100644 --- a/music21/test/test_interval.py +++ b/music21/test/test_interval.py @@ -350,6 +350,51 @@ def testIntervalMicrotonesB(self): i = interval.Interval(note.Note('c4'), note.Note('c~4')) self.assertEqual(str(i), '') + def testTransposeMicrotonePreserved(self): + # A microtone (cents offset) must survive transposition unchanged and + # must not be turned into a spurious quarter-tone accidental. Formerly + # the source microtone leaked into the accidental computation, so e.g. + # C4(+30c) transposed by a unison became C~4(-20c), or larger microtones + # raised AccidentalException. + + # Transposing by a perfect unison is the identity. + p = pitch.Pitch('C4') + p.microtone = 30 + out = p.transpose(interval.Interval('P1')) + self.assertEqual(out.nameWithOctave, 'C4') + self.assertEqual(out.microtone.cents, 30) + + # A formerly-crashing case: G#5(+50c) transposed by an augmented unison. + p = pitch.Pitch('G#5') + p.microtone = 50 + out = p.transpose(interval.Interval('A1')) + self.assertEqual(out.nameWithOctave, 'G##5') + self.assertEqual(out.microtone.cents, 50) + + # A quarter-tone accidental and a microtone both survive a unison. + p = pitch.Pitch('C~4') + p.microtone = 30 + out = p.transpose(interval.Interval('P1')) + self.assertEqual(out.name, 'C~') + self.assertEqual(out.microtone.cents, 30) + + # Across intervals and microtone magnitudes: the cents are preserved, + # the sounding pitch moves by exactly the interval, and no quarter-tone + # accidental is introduced on an integer-semitone interval. + for iName in ['P1', 'm2', 'M3', 'P5', 'm7', 'P8', 'A4', 'd5']: + iv = interval.Interval(iName) + for cents in (-49, -25, 26, 49): + with self.subTest(interval=iName, cents=cents): + src = pitch.Pitch('C4') + src.microtone = cents + psBefore = src.ps + out = src.transpose(iv) + self.assertAlmostEqual(out.microtone.cents, cents) + self.assertAlmostEqual(out.ps - psBefore, + iv.chromatic.semitones) + if out.accidental is not None: + self.assertEqual(out.accidental.alter % 1, 0.0) + def testDescendingAugmentedUnison(self): ns = note.Note('C4') ne = note.Note('C-4') From 46ae89de8559f655f5dc151ea14047ebf65b050a Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 30 Jun 2026 14:06:33 -1000 Subject: [PATCH 006/312] memory: plan to re-apply meter immutability fresh on master meter-speed is 879 commits stale; merging master produced 39 deep semantic conflicts (str->tuple + immutable rewrite vs master's evolved string/mutable code). Decided to abandon the merge and re-apply FrozenDuration + immutable MeterTerminal/MeterSequence + tuple NumDenom fresh on master instead. AI-assisted (Claude) --- .agents/memory/MEMORY.md | 1 + .agents/memory/meter-immutability-reapply.md | 34 ++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 .agents/memory/meter-immutability-reapply.md diff --git a/.agents/memory/MEMORY.md b/.agents/memory/MEMORY.md index 2bcdd6844c..0edaf6970f 100644 --- a/.agents/memory/MEMORY.md +++ b/.agents/memory/MEMORY.md @@ -6,3 +6,4 @@ Project-shared agent memory for music21. One file per fact; see frontmatter for - [sortedcontainers for streams](sortedcontainers-for-streams.md) — tried twice to make streams always-sorted via SortedList; abandoned, too slow for tiny streams - [Stream subclass generic defaults](stream-subclass-generic-defaults.md) — how `inherit-generics` should work once PEP 696 default type params land; maybe add typing_extensions - [BFS-flatten-iterator](bfs-flatten-iterator.md) — lazy breadth-first flatten is writable (maybe for tree first), but must not reuse the flat/flatten name tied to offsetInHierarchy +- [Meter immutability re-apply](meter-immutability-reapply.md) — meter-speed is too stale to merge; re-apply FrozenDuration + immutable MeterTerminal + tuple NumDenom fresh on master diff --git a/.agents/memory/meter-immutability-reapply.md b/.agents/memory/meter-immutability-reapply.md new file mode 100644 index 0000000000..753319e585 --- /dev/null +++ b/.agents/memory/meter-immutability-reapply.md @@ -0,0 +1,34 @@ +--- +name: meter-immutability-reapply +description: Plan to re-apply the meter-speed immutability rewrite fresh on master instead of merging the stale branch +metadata: + type: project +--- + +The `origin/meter-speed` branch (16 commits, Oct 2022) is a two-part meter rewrite: + +1. **Representation change** — meter's internal form goes from strings (`'1/4'`, + `f'1/{d}'`) to tuples: `NumDenom = tuple[int, int]` (i.e. `(1, 4)`), with + `NumDenomTuple` / `MeterOptions` aliases in `meter/tools.py`. +2. **Immutability** — `MeterTerminal` / `MeterSequence` become slotted and + immutable (`SlottedObjectMixin` / `common.FrozenObject`, `FrozenDuration`, + `_durationFromNumeratorDenominator` cached with `lru_cache`). Immutable meter + terminals can be shared/cached with no defensive copies. + +**Why not just merge master in (decided 2026-06-30):** the branch is 879 commits +behind. Merging `master` produced deep *semantic* conflicts — 31 hunks in +`meter/core.py`, 8 in `meter/tools.py` — because master independently evolved the +*string-based, mutable* code for a year+ (added typing like `-> None`, a `weight` +property, parse fast-paths, and a public→private rename `addTerminal` → +`_addTerminal`). Almost every hunk is two different implementations of the same +method, and the str→tuple change ripples into callers. Resolving that merge would +mean finishing+rebasing the rewrite through hundreds of collisions — not worth it. + +**How to apply:** treat `origin/meter-speed` as a *reference*, not a mergeable +branch. Start a clean branch off current `master` and re-apply only the wanted +pieces as a fresh, reviewable change: `FrozenDuration`, slotted/immutable +`MeterTerminal` & `MeterSequence`, and the tuple `NumDenom` internal +representation. Note `common.FrozenObject` is **already in master** (typed for +3.12+), so only the meter-specific immutability needs porting. Validate against the +full `music21/meter` test suite and fix downstream callers. Keep `origin/meter-speed` +until the re-apply lands; delete it afterward. From 4d06d88bcd749e76e513c7fb4b630c553caf4440 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 1 Jul 2026 10:45:00 -1000 Subject: [PATCH 007/312] sorting: collapse SortTuple into a single typed NamedTuple MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define SortTuple directly as a class-based typing.NamedTuple, dropping the _SortTupleBase functional-form base and the no-op __new__ (a pure passthrough since 2015). The split existed only to allow overriding __new__, which mypy forbids on a class-syntax NamedTuple. - priority is now typed int|float to reflect the ±inf sentinels (the loose __new__ used to hide this); classSortOrder stays int|float. - Comparison dunders call tuple.__eq__/__lt__/__gt__ directly: bare super() in a class-syntax NamedTuple body triggers a __classcell__ RuntimeError at class creation. - modify() now delegates to _replace() (== copy.replace() on 3.13+); a bad field name raises ValueError instead of being silently ignored. - base.py: fix docstring prose that misattributed priority -inf to classSortOrder. AI-assisted (Claude) --- music21/base.py | 2 +- music21/sorting.py | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/music21/base.py b/music21/base.py index 03ae15ed2c..b5fc735fba 100644 --- a/music21/base.py +++ b/music21/base.py @@ -1895,7 +1895,7 @@ def contextSites( (, '9.5 <0.20...>', ) Streams have themselves as the first element in their context sites, at position - zero and classSortOrder negative infinity. + zero and priority negative infinity. This example shows the context sites for Measure 3 of the Alto part. We will get the measure object using direct access to diff --git a/music21/sorting.py b/music21/sorting.py index 626f6ccc18..f8d14bc2b0 100644 --- a/music21/sorting.py +++ b/music21/sorting.py @@ -37,19 +37,14 @@ class SortingException(exceptions21.Music21Exception): pass -_SortTupleBase = t.NamedTuple('SortTuple', [ # type: ignore[name-match] - ('atEnd', int), - ('offset', OffsetQL), - ('priority', int), - ('classSortOrder', int), - ('isNotGrace', int), - ('insertIndex', int), -]) - -class SortTuple(_SortTupleBase): +class SortTuple(t.NamedTuple): ''' Derived class of namedTuple which allows for comparisons with pure ints/fractions. + * Changed in v11: defined directly as a typed NamedTuple (no separate base class); + ``priority`` is now typed ``int | float`` to reflect the ``±inf`` sentinels. + (AI-assisted.) + >>> n = note.Note() >>> s = stream.Stream() >>> s.insert(4, n) @@ -103,13 +98,18 @@ class SortTuple(_SortTupleBase): '1.0 <0.20.323>' ''' - def __new__(cls, *tupEls, **keywords): - # noinspection PyTypeChecker - return super(SortTuple, cls).__new__(cls, *tupEls, **keywords) + atEnd: int + offset: OffsetQL + priority: int | float + classSortOrder: int | float + isNotGrace: int + insertIndex: int def __eq__(self, other): if isinstance(other, tuple): - return super().__eq__(other) + # NB: tuple.__eq__ not super() -- bare super() in a class-syntax NamedTuple + # body triggers a __classcell__ RuntimeError at class creation. + return tuple.__eq__(self, other) try: if self.atEnd == 1 and other != INFINITY: return False @@ -122,7 +122,7 @@ def __eq__(self, other): def __lt__(self, other): if isinstance(other, tuple): - return super().__lt__(other) + return tuple.__lt__(self, other) try: if self.atEnd == 1: return False @@ -133,7 +133,7 @@ def __lt__(self, other): def __gt__(self, other): if isinstance(other, tuple): - return super().__gt__(other) + return tuple.__gt__(self, other) try: if self.atEnd == 1 and other != INFINITY: return True @@ -184,11 +184,14 @@ def shortRepr(self): reprParts.append('>') return ''.join(reprParts) - def modify(self, **keywords): + def modify(self, **keywords) -> SortTuple: ''' Return a new SortTuple identical to the previous, except with the given keyword modified. Works only with keywords. + Identical to ``_replace()`` and ``copy.replace()`` on Py 3.13+; + called out to document it. + >>> st = sorting.SortTuple(atEnd=0, offset=1.0, priority=0, classSortOrder=20, ... isNotGrace=1, insertIndex=32) >>> st2 = st.modify(offset=2.0) @@ -207,10 +210,11 @@ def modify(self, **keywords): 1.0 Changing offset, but nothing else, helps in creating .flatten() positions. + + * Changed in v11: delegates to the namedtuple ``_replace``; a bad field + name now raises ValueError instead of being silently ignored. ''' - # _fields are the namedtuple attributes - outList = [keywords.get(attr, getattr(self, attr)) for attr in self._fields] - return self.__class__(*outList) + return self._replace(**keywords) def add(self, other): ''' From 805c927556ab1a0191f67d751b93a791f64d17fa Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 1 Jul 2026 11:03:57 -1000 Subject: [PATCH 008/312] update docs --- music21/sorting.py | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/music21/sorting.py b/music21/sorting.py index f8d14bc2b0..eed6e0e4c7 100644 --- a/music21/sorting.py +++ b/music21/sorting.py @@ -1,10 +1,10 @@ # ----------------------------------------------------------------------------- # Name: sorting.py -# Purpose: Music21 class for sorting +# Purpose: Music21 class for sorting Music21Objects # # Authors: Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2014-2015 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2014-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ----------------------------------------------------------------------------- ''' @@ -41,10 +41,6 @@ class SortTuple(t.NamedTuple): ''' Derived class of namedTuple which allows for comparisons with pure ints/fractions. - * Changed in v11: defined directly as a typed NamedTuple (no separate base class); - ``priority`` is now typed ``int | float`` to reflect the ``±inf`` sentinels. - (AI-assisted.) - >>> n = note.Note() >>> s = stream.Stream() >>> s.insert(4, n) @@ -97,6 +93,7 @@ class SortTuple(t.NamedTuple): >>> st.shortRepr() '1.0 <0.20.323>' + * Changed in v11: priority and classSortOrder can be floats. ''' atEnd: int offset: OffsetQL @@ -187,10 +184,8 @@ def shortRepr(self): def modify(self, **keywords) -> SortTuple: ''' Return a new SortTuple identical to the previous, except with - the given keyword modified. Works only with keywords. - - Identical to ``_replace()`` and ``copy.replace()`` on Py 3.13+; - called out to document it. + the given keyword modified. Works only with keywords. (Identical to + ``_replace()`` and ``copy.replace()`` on Py 3.13+) >>> st = sorting.SortTuple(atEnd=0, offset=1.0, priority=0, classSortOrder=20, ... isNotGrace=1, insertIndex=32) @@ -204,15 +199,14 @@ def modify(self, **keywords) -> SortTuple: >>> st3.shortRepr() 'End <0.20.[Grace].32>' - The original tuple is never modified (hence tuple): + The original tuple is never modified (hence why it's a tuple): >>> st.offset 1.0 - Changing offset, but nothing else, helps in creating .flatten() positions. + `Stream.flatten()` uses `.modify` to change offsets while keeping other values identical. - * Changed in v11: delegates to the namedtuple ``_replace``; a bad field - name now raises ValueError instead of being silently ignored. + * Changed in v11: a bad field name now raises ValueError ''' return self._replace(**keywords) @@ -239,28 +233,31 @@ def add(self, other): raise SortingException('Cannot add attributes from a different class') outList = [max(getattr(self, attr), getattr(other, attr)) - if attr in ('atEnd', 'isNotGrace') - else (getattr(self, attr) + getattr(other, attr)) - for attr in self._fields] # _fields are the namedtuple attributes + if attr in ('atEnd', 'isNotGrace') + else (getattr(self, attr) + getattr(other, attr)) + for attr in self._fields] # _fields are the namedtuple attributes return self.__class__(*outList) def sub(self, other): ''' - Subtract all attributes from to another. atEnd and isNotGrace take the min value of either. + Subtract all attributes from another SortTuple. + `atEnd` and `isNotGrace` take the min value of either. >>> n = note.Note() >>> n.offset = 10 >>> s = stream.Stream() >>> s.offset = 10 - >>> n.sortTuple() + >>> nt = n.sortTuple() + >>> nt SortTuple(atEnd=0, offset=10.0, priority=0, classSortOrder=20, isNotGrace=1, insertIndex=0) - >>> s.sortTuple() + >>> st = s.sortTuple() + >>> st SortTuple(atEnd=0, offset=10.0, priority=0, classSortOrder=-20, isNotGrace=1, insertIndex=0) - >>> s.sortTuple().sub(n.sortTuple()) + >>> st.sub(nt) SortTuple(atEnd=0, offset=0.0, priority=0, classSortOrder=-40, isNotGrace=1, insertIndex=0) - Note that atEnd and isNotGrace are lower bounded at 0. + Note that `atEnd` and `isNotGrace` are lower bounded at 0. ''' if not isinstance(other, self.__class__): raise SortingException('Cannot add attributes from a different class') From 28d70b110f2282db714b9e4960bd1867c084f2a7 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 1 Jul 2026 11:06:11 -1000 Subject: [PATCH 009/312] One more doc to clear up --- music21/sorting.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/music21/sorting.py b/music21/sorting.py index eed6e0e4c7..835c8bf75f 100644 --- a/music21/sorting.py +++ b/music21/sorting.py @@ -219,14 +219,16 @@ def add(self, other): >>> n.offset = 10 >>> s = stream.Stream() >>> s.offset = 10 - >>> n.sortTuple() + >>> nt = n.sortTuple() + >>> nt SortTuple(atEnd=0, offset=10.0, priority=0, classSortOrder=20, isNotGrace=1, insertIndex=0) - >>> s.sortTuple() + >>> st = s.sortTuple() + >>> st SortTuple(atEnd=0, offset=10.0, priority=0, classSortOrder=-20, isNotGrace=1, insertIndex=0) - >>> s.sortTuple().add(n.sortTuple()) + >>> st.add(nt) SortTuple(atEnd=0, offset=20.0, priority=0, classSortOrder=0, isNotGrace=1, insertIndex=0) - Note that atEnd and isNotGrace are equal to other's value. are upper bounded at 1 and + Note that `atEnd` and `isNotGrace` are upper bounded at 1 and take the maxValue of either. ''' if not isinstance(other, self.__class__): From 5bd60deefeb99ec73fb1075d624a49952107e951 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 1 Jul 2026 11:39:41 -1000 Subject: [PATCH 010/312] sorting: silence pylint no-member for NamedTuple _replace/_fields Class-syntax typing.NamedTuple does not get its _replace/_fields/_asdict members recognized by astroid, so pylint raises false-positive E1101 no-member. Add inline disables. Known upstream bug, open since 2020: https://github.com/pylint-dev/pylint/issues/4070 AI-assisted (Claude) --- music21/sorting.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/music21/sorting.py b/music21/sorting.py index 835c8bf75f..0e2c48efd1 100644 --- a/music21/sorting.py +++ b/music21/sorting.py @@ -208,7 +208,7 @@ def modify(self, **keywords) -> SortTuple: * Changed in v11: a bad field name now raises ValueError ''' - return self._replace(**keywords) + return self._replace(**keywords) # pylint: disable=no-member def add(self, other): ''' @@ -237,7 +237,7 @@ def add(self, other): outList = [max(getattr(self, attr), getattr(other, attr)) if attr in ('atEnd', 'isNotGrace') else (getattr(self, attr) + getattr(other, attr)) - for attr in self._fields] # _fields are the namedtuple attributes + for attr in self._fields] # pylint: disable=no-member return self.__class__(*outList) @@ -267,7 +267,7 @@ def sub(self, other): outList = [min(getattr(self, attr), getattr(other, attr)) if attr in ('atEnd', 'isNotGrace') else (getattr(self, attr) - getattr(other, attr)) - for attr in self._fields] # _fields are the namedtuple attributes + for attr in self._fields] # pylint: disable=no-member return self.__class__(*outList) From a50d7692cd01cac567e52ea20c251c6d9494e60c Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Sun, 5 Jul 2026 10:09:44 +0200 Subject: [PATCH 011/312] Address review: gate centsOrigin on isTwelveTone and trim comments --- music21/interval.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/music21/interval.py b/music21/interval.py index d145c23c45..8f47d1bd1f 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -3460,7 +3460,10 @@ def _diatonicTransposePitch(self, pitch1 = p pitch2 = copy.deepcopy(pitch1) oldDiatonicNum = pitch1.diatonicNoteNum - centsOrigin = pitch1.microtone.cents + if not pitch1.isTwelveTone(): + centsOrigin = pitch1.microtone.cents + else: + centsOrigin = 0.0 distanceToMove = self.diatonic.generic.staffDistance newDiatonicNumber = oldDiatonicNum + distanceToMove @@ -3475,13 +3478,9 @@ def _diatonicTransposePitch(self, # We have the right note name but not the right accidental interval2 = Interval(pitch1, pitch2) - # interval2 is measured from the microtonal pitch1, so halfStepsToFix - # carries the source microtone. Peel it off so it adjusts only the - # accidental; it is restored as a microtone once the accidental is set. - # Without this, the leaked cents corrupt the spelling (e.g. a +30c pitch - # transposed by P1 becomes a half-sharp) or raise AccidentalException. halfStepsToFix = self.chromatic.semitones - interval2.chromatic.semitones if centsOrigin: + # peel the source microtone off so it adjusts only the accidental halfStepsToFix = round(halfStepsToFix - centsOrigin / 100.0, 9) # environLocal.printDebug(['self', self, 'halfStepsToFix', halfStepsToFix, @@ -3533,8 +3532,8 @@ def _diatonicTransposePitch(self, and oldPitch2Accidental.name == 'natural'): pitch2.accidental = oldPitch2Accidental - # restore the source microtone that was peeled off halfStepsToFix above if centsOrigin: + # restore the cents peeled off halfStepsToFix above pitch2.microtone = pitch2.microtone.cents + centsOrigin if useImplicitOctave: From 8c8242478909ecedbd0141a38fbd656bf8c4b148 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Sun, 5 Jul 2026 10:23:15 +0200 Subject: [PATCH 012/312] Fix microtone preservation breaking scale realization Only carry the source microtone through when the applied interval is not itself microtonal; a microtonal interval already supplies the result's cents (scale realization chains these), so preserving would double-count. Also measure the accidental from a microtone-free copy of pitch1 so the interval construction does not hit a fractional chromatic with no specifier. --- music21/interval.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/music21/interval.py b/music21/interval.py index 8f47d1bd1f..df3357e4f9 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -3460,7 +3460,11 @@ def _diatonicTransposePitch(self, pitch1 = p pitch2 = copy.deepcopy(pitch1) oldDiatonicNum = pitch1.diatonicNoteNum - if not pitch1.isTwelveTone(): + # Only carry pitch1's microtone through when the interval is not itself + # microtonal: a microtonal interval already supplies the result's cents + # (e.g. scale realization), so preserving here would double-count. + intervalIsMicrotonal = self.chromatic.semitones != int(self.chromatic.semitones) + if not pitch1.isTwelveTone() and not intervalIsMicrotonal: centsOrigin = pitch1.microtone.cents else: centsOrigin = 0.0 @@ -3476,12 +3480,16 @@ def _diatonicTransposePitch(self, # if this is not set to None then terrible things happen pitch2.microtone = None # type: ignore - # We have the right note name but not the right accidental - interval2 = Interval(pitch1, pitch2) - halfStepsToFix = self.chromatic.semitones - interval2.chromatic.semitones + # We have the right note name but not the right accidental. Measure it + # from pitch1 without its microtone (reapplied to the result below); a + # microtonal endpoint here yields a fractional chromatic with no + # interval specifier. + pitch1ForAccidental = pitch1 if centsOrigin: - # peel the source microtone off so it adjusts only the accidental - halfStepsToFix = round(halfStepsToFix - centsOrigin / 100.0, 9) + pitch1ForAccidental = copy.deepcopy(pitch1) + pitch1ForAccidental.microtone = None + interval2 = Interval(pitch1ForAccidental, pitch2) + halfStepsToFix = self.chromatic.semitones - interval2.chromatic.semitones # environLocal.printDebug(['self', self, 'halfStepsToFix', halfStepsToFix, # 'centsOrigin', centsOrigin, 'interval2', interval2]) From 2e9fd8b10a1d99d9f5249b1763fc977e77d9349d Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 5 Jul 2026 15:22:36 -0400 Subject: [PATCH 013/312] Remove Path -> str -> Path roundtrip in uncompressMXL() --- music21/musicxml/archiveTools.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/music21/musicxml/archiveTools.py b/music21/musicxml/archiveTools.py index ccb9f07ed0..bbac112b01 100644 --- a/music21/musicxml/archiveTools.py +++ b/music21/musicxml/archiveTools.py @@ -121,7 +121,7 @@ def uncompressMXL(filename: str|pathlib.Path, fp: pathlib.Path = common.pathTools.cleanpath(filename, returnPathlib=True) if not silent: # pragma: no cover environLocal.warn(f'Updating file: {fp}') - extractPath = str(fp.parent) + extractPath = fp.parent unarchivedName = fp.with_suffix('.musicxml').name # Export container and original xml file to system as a compressed XML. with zipfile.ZipFile(filename, 'r', compression=zipfile.ZIP_DEFLATED) as myZip: @@ -139,9 +139,8 @@ def uncompressMXL(filename: str|pathlib.Path, myZip.extract(member=storedName, path=extractPath) if not found_one_file: # only rename one file: hope it is the right one. - extractPath_pathlib = pathlib.Path(extractPath) - wrongName = extractPath_pathlib / storedName - correctName = extractPath_pathlib / unarchivedName + wrongName = extractPath / storedName + correctName = extractPath / unarchivedName wrongName.rename(correctName) found_one_file = True From 18fbeb7c4bb9bfed7709213cc06976e44fefa2cd Mon Sep 17 00:00:00 2001 From: Pablo Pupo <145598901+pablopupo@users.noreply.github.com> Date: Sun, 5 Jul 2026 12:31:09 -0700 Subject: [PATCH 014/312] Fix invalid MusicXML for an unpitched grace note in a voice (#1973) `unpitchedToXml()` inserted `` before the earliest of `` or ``, but a grace note has no ``, so anything `noteToXml()` had appended in between (voice, tie, instrument, footnote, level) came out ahead of ``, and the file failed schema validation. --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/musicxml/helpers.py | 41 +++++++++++++++++++++++++++++++ music21/musicxml/m21ToXml.py | 25 ++++++++++++++++++- music21/musicxml/test_m21ToXml.py | 15 +++++++++++ 5 files changed, 82 insertions(+), 3 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index eed25cb593..d45d6c0cd6 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b6' +__version__ = '11.0.0b7' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index b5fc735fba..85ae8bbe72 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b6' +'11.0.0b7' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/musicxml/helpers.py b/music21/musicxml/helpers.py index 804cb0c7c7..9722295cc6 100644 --- a/music21/musicxml/helpers.py +++ b/music21/musicxml/helpers.py @@ -151,6 +151,47 @@ def insertBeforeElements(root, insert, tagList=None): root.insert(min(insertIndices), insert) +def insertAfterElements(root, insert, tagList=None): + # noinspection PyShadowingNames + ''' + Insert element `insert` into element `root` directly after the last + instance of any child tag given in `tagList`. Insert at the beginning + if `tagList` is `None` or no instance is found. + + >>> from xml.etree.ElementTree import fromstring as EL + >>> from music21.musicxml.helpers import insertAfterElements, dump + >>> root = EL('1') + >>> insert = EL('') + + >>> insertAfterElements(root, insert, tagList=['grace']) + >>> dump(root) + + + + 1 + + + Insert at the beginning when nothing in `tagList` matches: + + >>> insert2 = EL('') + >>> insertAfterElements(root, insert2, tagList=['bar']) + >>> dump(root) + + + + + 1 + + ''' + insertIndex = 0 + if tagList: + # Iterate children only, not grandchildren + for i, child in enumerate(root.findall('*')): + if child.tag in tagList: + insertIndex = i + 1 + root.insert(insertIndex, insert) + + def measureNumberComesBefore(mNum1: str, mNum2: str) -> bool: ''' Determine whether `measureNumber1` strictly precedes diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 5c525dacee..5098428eb1 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -4622,6 +4622,22 @@ def unpitchedToXml(self, quarter + + must come before , even when there is + no , as on a grace note in a voice: + + >>> MEX.currentVoiceId = 1 + >>> mxUnpitched = MEX.unpitchedToXml(graceUp) + >>> MEX.dump(mxUnpitched) + + + + D + 5 + + 1 + quarter + ''' mxNote = self.noteToXml(up, noteIndexInChord=noteIndexInChord, chordParent=chordParent) @@ -4629,7 +4645,14 @@ def unpitchedToXml(self, _setTagTextFromAttribute(up, mxUnpitched, 'display-step') _setTagTextFromAttribute(up, mxUnpitched, 'display-octave') - helpers.insertBeforeElements(mxNote, mxUnpitched, tagList=['duration', 'type']) + # within , only , , and may precede + # , so insert after those rather than trying to list + # everything that can follow it (#1732) + helpers.insertAfterElements( + mxNote, + mxUnpitched, + tagList=['grace', 'cue', 'chord'] + ) return mxNote diff --git a/music21/musicxml/test_m21ToXml.py b/music21/musicxml/test_m21ToXml.py index 5e0e47fc41..fc98f8798f 100644 --- a/music21/musicxml/test_m21ToXml.py +++ b/music21/musicxml/test_m21ToXml.py @@ -229,6 +229,21 @@ def testLowVoiceNumbers(self): xmlOut = self.getXml(m) self.assertIn('hello', xmlOut) + def testUnpitchedGraceInVoice(self): + ''' + must precede even on a grace note, which has + no element to insert before. + ''' + # https://github.com/cuthbertLab/music21/issues/1732 + grace = note.Unpitched(duration=duration.GraceDuration()) + v1 = stream.Voice([grace]) + v2 = stream.Voice([note.Note()]) + m = stream.Measure([v1, v2]) + + tree = self.getET(m) + mxNote = tree.find('part/measure/note') + self.assertEqual([el.tag for el in mxNote], ['grace', 'unpitched', 'voice', 'type']) + def testVoiceNumberOffsetsThreeStaffsInGroup(self): n1_1 = note.Note() v1_1 = stream.Voice([n1_1]) From 28bbde58b82980399b4435e4de0704df392dd92f Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Sun, 5 Jul 2026 16:08:40 -0400 Subject: [PATCH 015/312] Avoid flipping displayStatus None -> True on explicit naturals --- music21/pitch.py | 1 + music21/test/test_pitch.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/music21/pitch.py b/music21/pitch.py index 2a3c63efe7..3cc56dfe02 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -5145,6 +5145,7 @@ def set_displayStatus(newDisplayStatus: bool) -> None: # and it is not a natural, it should always be set to display if (pPastInMeasure is False and acc is not None + and acc.name != 'natural' and not self._nameInKeySignature(alteredPitches)): acc.displayStatus = True return # do not search past diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 3e92d0479d..80cfedfe0c 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -407,6 +407,18 @@ def testImplicitToExplicitNatural(self): p.makeAccidentals(inPlace=True) self.assertIs(last_note.pitch.accidental.displayStatus, False) + def testExplicitNaturalDisplayStatusNone(self): + p = converter.parse('tinyNotation: d1 d1') + first_note = p.recurse().notes.first() + last_note = p.recurse().notes.last() + first_note.pitch.accidental = pitch.Accidental('natural') + last_note.pitch.accidental = pitch.Accidental('natural') + self.assertIsNone(first_note.pitch.accidental.displayStatus) + self.assertIsNone(last_note.pitch.accidental.displayStatus) + p.makeAccidentals(inPlace=True) + self.assertIs(first_note.pitch.accidental.displayStatus, False) + self.assertIs(last_note.pitch.accidental.displayStatus, False) + def testIfAbsolutelyNecessary(self): ''' Beginning of test cases for if-absolutely-necessary. From 51d5c739100f4f5ea538d3791fc42d3f0cc6d071 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sun, 5 Jul 2026 18:55:22 -1000 Subject: [PATCH 016/312] Duration.__eq__: only quarterLength matters when expression is inferred Fixes #1970 When both Durations have expressionIsInferred=True, their type, dots, tuplets, and components can be freely re-expressed according to context, so only quarterLength needs to match for equality. Also corrects the expressionIsInferred docstring ("according to complex" -> "context"). AI-assisted (Claude) --- music21/duration.py | 87 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/music21/duration.py b/music21/duration.py index 02af148b03..9cd927380b 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -1604,7 +1604,7 @@ class Duration(prebase.ProtoM21Object, SlottedObjectMixin): expression. For instance the duration of 0.5 is generally an eighth note, but in the middle of a triplet group might be better written as a dotted-eighth triplet. If expressionIsInferred - is True then `music21` can change it according to complex. If + is True then `music21` can change it according to context. If False, then the type, dots, and tuplets are considered immutable. >>> d = duration.Duration(0.5) @@ -1736,10 +1736,47 @@ def __eq__(self, other): >>> tupDur1.linked = False >>> tupDur1 == tupDur2 False + + If both durations have :attr:`expressionIsInferred` set to True, then + their expression (type, dots, tuplets, and components) can be freely + changed according to context, so only the `quarterLength` needs to + match. Here a plain quarter note is equal to a half note in a 2:1 + tuplet, since both span one quarter length and both are free to be + re-expressed: + + >>> plainDur = duration.Duration(1.0) + >>> plainDur.type + 'quarter' + >>> altDur = duration.Duration('half') + >>> altDur.appendTuplet(duration.Tuplet(2, 1)) + >>> altDur.quarterLength + 1.0 + >>> altDur.expressionIsInferred = True + >>> plainDur == altDur + True + + But if either duration's expression is *not* inferred, then type, dots, + tuplets, and components must all match as before: + + >>> altDur.expressionIsInferred = False + >>> plainDur == altDur + False + + * Changed in v11: if both durations have `expressionIsInferred` True, + only `quarterLength` must match. ''' if type(other) is not type(self): return False + if self.linked != other.linked: + return False + + # When both expressions are inferred from quarterLength, callers are + # free to re-express the type, dots, tuplets, and components, so only + # the quarterLength is meaningful for equality. + if self.expressionIsInferred and other.expressionIsInferred: + return self.quarterLength == other.quarterLength + if self.isComplex != other.isComplex: return False if len(self.components) != len(other.components): @@ -1756,8 +1793,6 @@ def __eq__(self, other): return False if self.quarterLength != other.quarterLength: return False - if self.linked != other.linked: - return False return True @@ -4027,6 +4062,52 @@ def testExpressionIsInferred(self): # attribute assignments that could occur in any order self.assertEqual(d.expressionIsInferred, False) + def testEqualityExpressionIsInferred(self): + ''' + When both durations infer their expression from quarterLength, only + quarterLength must match for equality (GH #1970). + ''' + # A plain quarter note and a half note in a 2:1 tuplet both span one + # quarter length but differ in type and tuplets. + plainDur = Duration(1.0) + altDur = Duration('half') + altDur.appendTuplet(Tuplet(2, 1)) + self.assertEqual(altDur.quarterLength, 1.0) + self.assertNotEqual(plainDur.type, altDur.type) + self.assertNotEqual(plainDur.tuplets, altDur.tuplets) + + # altDur's expression is not inferred (built from a type), so strict + # comparison applies and they are not equal. + self.assertFalse(altDur.expressionIsInferred) + self.assertNotEqual(plainDur, altDur) + + # Marking altDur's expression as inferred relaxes the comparison to + # quarterLength only. + altDur.expressionIsInferred = True + self.assertEqual(plainDur, altDur) + self.assertEqual(altDur, plainDur) # symmetric + + # Differing quarterLength is still unequal even when both are inferred. + altDur2 = Duration(2.0) + self.assertTrue(altDur2.expressionIsInferred) + self.assertNotEqual(plainDur, altDur2) + + # Link status is still checked before the inference shortcut: an + # unlinked duration with the same quarterLength is never equal. + unlinkedDur = Duration(1.0) + unlinkedDur.linked = False + self.assertEqual(unlinkedDur.quarterLength, 1.0) + self.assertNotEqual(plainDur, unlinkedDur) + + # Inference must be mutual: one inferred, one not -> strict. + eighthDur = Duration(0.5) + typedQuarter = Duration(type='quarter') + self.assertFalse(typedQuarter.expressionIsInferred) + # Same type/dots/QL, so equal even without inference relaxation. + self.assertEqual(plainDur, typedQuarter) + # But an inferred eighth is not equal to a typed quarter. + self.assertNotEqual(eighthDur, typedQuarter) + def testExceptions(self): ''' These errors are user errors, so they raise generic exceptions From f8a08f54957b0913aa188908c4118dfc0f86ff7a Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 06:55:22 +0200 Subject: [PATCH 017/312] Fix dropped sharp on raised 6/7 major chords from romanNumeralFromChord Major-quality chords on the raised sixth and seventh scale degrees of a minor key now keep their sharp prefix (e.g. #VI in c minor instead of VI#63), so the generated figure round-trips through RomanNumeral() and .romanNumeral distinguishes them from the chords on the lowered (natural minor) degrees. Adds the keyword-only isMajorThird argument to correctRNAlterationForMinor, documents the semantic contract of the .romanNumeral property, and removes the now-unneeded workaround in romanText/tsvConverter.localKeyAsRn. Addresses the main bug in #1349 (the sixthMinor/seventhMinor parameters for romanNumeralFromChord and the QUALITY default flip remain open). AI-assisted (Claude) --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/roman.py | 120 +++++++++++++++++++++++++++++- music21/romanText/tsvConverter.py | 7 -- 4 files changed, 119 insertions(+), 12 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index d45d6c0cd6..240ab9a1f5 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b7' +__version__ = '11.0.0b8' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 85ae8bbe72..3a3c407d2c 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b7' +'11.0.0b8' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/roman.py b/music21/roman.py index 733261bd29..16f9f21852 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -675,10 +675,12 @@ def romanInversionName(inChord: chord.Chord, inv: int|None = None) -> str: def correctRNAlterationForMinor( figureTuple: FigureTuple, - keyObj: key.Key + keyObj: key.Key, + *, + isMajorThird: bool = False, ) -> FigureTuple: ''' - (This will become a private function in version 10) + (This will become a private function in a future version) Takes in a FigureTuple and a Key object and returns the same or a new FigureTuple correcting for the fact that, for instance, Ab in c minor @@ -699,10 +701,22 @@ def correctRNAlterationForMinor( >>> roman.correctRNAlterationForMinor(ft6, key.Key('c')) FigureTuple(aboveBass=6, alter=0, prefix='b') + For a chord whose quality is minor, diminished, or half-diminished, a raised + root is implied by the lowercase numeral, so the sharp prefix is removed: + >>> ft7 = roman.FigureTuple(aboveBass=7, alter=1, prefix='#') >>> roman.correctRNAlterationForMinor(ft7, key.Key('c')) FigureTuple(aboveBass=7, alter=0, prefix='') + But if the chord has a major third, pass `isMajorThird=True` so that the + sharp is kept: an uppercase VI or VII in minor already refers to the chord + on the *lowered* (natural minor) degree, so a major chord on the raised + degree needs its sharp to be distinguished from it: + + >>> ft8 = roman.FigureTuple(aboveBass=7, alter=1, prefix='#') + >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), isMajorThird=True) + FigureTuple(aboveBass=7, alter=1, prefix='#') + Does nothing for major and passes in the original Figure Tuple unchanged: >>> ft1 = roman.FigureTuple(aboveBass=6, alter=-1, prefix='b') @@ -720,11 +734,21 @@ def correctRNAlterationForMinor( FigureTuple(aboveBass=4, alter=-1, prefix='b') >>> ft3 is ft4 True + + * Changed in v11: the keyword-only argument `isMajorThird` was added, so that + major-quality chords on raised ^6 and ^7 in minor keep their sharp prefix + (issue #1349). This fix was AI-assisted (Claude). ''' if keyObj.mode != 'minor': return figureTuple if figureTuple.aboveBass not in (6, 7): return figureTuple + if isMajorThird and figureTuple.alter >= 1.0: + # Keep the sharp(s): plain VI or VII in minor means the chord on the + # lowered (natural minor) degree under both the QUALITY and CAUTIONARY + # conventions, so stripping the prefix would move the root down a + # semitone once the figure is parsed again (issue #1349). + return figureTuple alter = figureTuple.alter rootAlterationString = figureTuple.prefix @@ -922,6 +946,24 @@ def romanNumeralFromChord( >>> romanNumeral11 + A major triad on the raised sixth or seventh degree of a minor key keeps + its sharp, so that it is distinguished from the major triad on the lowered + (natural minor) degree, both in the figure and in + :attr:`~music21.roman.RomanNumeral.romanNumeral`: + + >>> majorOnRaisedSix = roman.romanNumeralFromChord( + ... chord.Chord('A3 C#4 E4'), key.Key('c')) + >>> majorOnRaisedSix + + >>> majorOnRaisedSix.romanNumeral + '#VI' + >>> majorOnLoweredSix = roman.romanNumeralFromChord( + ... chord.Chord('A-3 C4 E-4'), key.Key('c')) + >>> majorOnLoweredSix + + >>> majorOnLoweredSix.romanNumeral + 'VI' + >>> roman.romanNumeralFromChord(chord.Chord('A3 C4 E-4 G4'), key.Key('c')) @@ -1046,6 +1088,12 @@ def romanNumeralFromChord( iv6, V, I. This kind of context-sensitivity is not currently included. + * Changed in v11: chords with a major third built on the raised sixth or + seventh scale degrees of a minor key keep their sharp prefix (previously + the alteration was buried in the inversion figures, e.g. `VI#63`, and + lost entirely from `.romanNumeral`; issue #1349). + This fix was AI-assisted (Claude). + OMIT_FROM_DOCS Note that this should be III+642 gives III+#642 (# before 6 is unnecessary) @@ -1165,7 +1213,7 @@ def romanNumeralFromChord( keyObj = key.Key(keyObj) ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple - ft = correctRNAlterationForMinor(ft, keyObj) + ft = correctRNAlterationForMinor(ft, keyObj, isMajorThird=isMajorThird) if ft.alter == 0: tonicPitch = keyObj.tonic @@ -3353,6 +3401,30 @@ def romanNumeral(self) -> str: >>> rn.romanNumeral 'bbII' + Note that the alteration returned is the *sounding* alteration of the + root relative to the diatonic scale of the key -- for minor keys, the + natural minor scale -- which is not necessarily the accidental written + in the figure. So a scale degree that is chromatically raised gains a + sharp even if none was written, because of the interpretation of + `sixthMinor` and `seventhMinor` (see :class:`~music21.roman.Minor67Default`): + + >>> roman.RomanNumeral('vi', 'c').romanNumeral + '#vi' + + while a merely cautionary accidental (one that does not change the + sounding pitch) is dropped: + + >>> rn = roman.RomanNumeral('bVI', 'c', + ... sixthMinor=roman.Minor67Default.CAUTIONARY) + >>> ' '.join(p.name for p in rn.pitches) + 'A- C E-' + >>> rn.romanNumeral + 'VI' + + For the notation as originally written, use + :attr:`~music21.roman.RomanNumeral.figure` or combine + `frontAlterationString` with `romanNumeralAlone`. + OMIT_FROM_DOCS >>> rn.romanNumeral = 'V' @@ -4276,6 +4348,48 @@ def p(c): rn = roman.RomanNumeral('VI+', k) self.assertEqual(p(rn), 'A-4 C5 E5') + def testRomanNumeralFromChordRaised67(self): + ''' + Major-quality chords on the raised sixth and seventh scale degrees of + a minor key keep their sharp prefix, so their figures round-trip and + `.romanNumeral` distinguishes them from the chords on the lowered + (natural minor) degrees. + https://github.com/cuthbertLab/music21/issues/1349 + + This test was AI-assisted (Claude). + ''' + from music21 import roman + + k = key.Key('c') + for pitchNames, expectedFigure, expectedRN in [ + (('A-4', 'C5', 'E-5'), 'bVI', 'VI'), + (('A4', 'C#5', 'E5'), '#VI', '#VI'), + (('C#4', 'E4', 'A4'), '#VI6', '#VI'), + (('E4', 'A4', 'C#5'), '#VI64', '#VI'), + (('B-4', 'D5', 'F5'), 'bVII', 'VII'), + (('B4', 'D#5', 'F#5'), '#VII', '#VII'), + # unchanged: lowercase quality already implies the raised degree + (('A4', 'C5', 'E5'), 'vi', '#vi'), + (('B4', 'D5', 'F5'), 'viio', '#vii'), + ]: + with self.subTest(pitches=pitchNames): + c = chord.Chord(pitchNames) + rn = roman.romanNumeralFromChord(c, k) + self.assertEqual(rn.figure, expectedFigure) + self.assertEqual(rn.romanNumeral, expectedRN) + # the figure must round-trip to the same pitch names under + # the convention that romanNumeralFromChord itself uses. + roundTrip = roman.RomanNumeral( + rn.figure, + k, + sixthMinor=roman.Minor67Default.CAUTIONARY, + seventhMinor=roman.Minor67Default.CAUTIONARY, + ) + self.assertEqual( + [p_.name for p_ in roundTrip.pitches], + [p_.name for p_ in c.pitches], + ) + def testAugmented(self): from music21 import roman diff --git a/music21/romanText/tsvConverter.py b/music21/romanText/tsvConverter.py index 6ec96eed58..6f3b628755 100644 --- a/music21/romanText/tsvConverter.py +++ b/music21/romanText/tsvConverter.py @@ -1070,13 +1070,6 @@ def localKeyAsRn(local_key: key.Key, global_key: key.Key) -> str: 'i' if letter.islower() else 'I', keyOrScale=local_key ) r = roman.romanNumeralFromChord(chord.Chord(rn.pitches), keyObj=global_key) - # Temporary hack: for some reason this gives VI and VII instead of #VI and #VII *only* - # when local_key is major and global_key is minor. - # see issue at https://github.com/cuthbertLab/music21/issues/1349#issue-1327713452 - if (local_key.mode == 'major' and global_key.mode == 'minor' - and r.romanNumeral in ('VI', 'VII') - and (r.pitchClasses[0] - global_key.pitches[0].pitchClass) % 12 in (9, 11)): - return '#' + r.romanNumeral return r.romanNumeral def isMinor(test_key: str) -> bool: From e919a55990e9872c26a7f577d3f5f044dbcd08e1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sun, 5 Jul 2026 19:02:06 -1000 Subject: [PATCH 018/312] Duration.__eq__ doctest: use eighth vs dotted-eighth-triplet example Use a more standard case (a plain eighth vs a dotted eighth under a 3:2 triplet, both 0.5 QL) and note that inferred expressions arise e.g. when parsing from MIDI. AI-assisted (Claude) --- music21/duration.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/music21/duration.py b/music21/duration.py index 9cd927380b..de957f688a 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -1737,20 +1737,20 @@ def __eq__(self, other): >>> tupDur1 == tupDur2 False - If both durations have :attr:`expressionIsInferred` set to True, then - their expression (type, dots, tuplets, and components) can be freely - changed according to context, so only the `quarterLength` needs to - match. Here a plain quarter note is equal to a half note in a 2:1 - tuplet, since both span one quarter length and both are free to be - re-expressed: - - >>> plainDur = duration.Duration(1.0) + If both durations have :attr:`expressionIsInferred` set to True (such as + if they were parsed from MIDI), then their expression (type, dots, + tuplets, and components) can be freely changed according to context, so + only the `quarterLength` needs to match. Here a plain eighth note is + equal to a dotted eighth under a 3:2 triplet, since both span half a + quarter length and both are free to be re-expressed: + + >>> plainDur = duration.Duration(0.5) >>> plainDur.type - 'quarter' - >>> altDur = duration.Duration('half') - >>> altDur.appendTuplet(duration.Tuplet(2, 1)) + 'eighth' + >>> altDur = duration.Duration(type='eighth', dots=1) + >>> altDur.appendTuplet(duration.Tuplet(3, 2)) >>> altDur.quarterLength - 1.0 + 0.5 >>> altDur.expressionIsInferred = True >>> plainDur == altDur True From 1e01c8cf3c96dd0c41e843c0410279b71ea9b586 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:05:07 +0200 Subject: [PATCH 019/312] Convert FigureTuple/ChordFigureTuple to documented typed NamedTuples Documents the previously implicit semantics: aboveBass is reckoned from whatever reference pitch was passed (the tonic, not the sounding bass, in romanNumeralFromChord); alter is measured against the natural-minor scale; and prefix deliberately diverges from alter after correctRNAlterationForMinor applies cautionary conventions. Also adds a maintenance note listing the three places that implement the minor ^6/^7 accidental convention. No behavior change. AI-assisted (Claude) --- music21/roman.py | 99 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index 16f9f21852..3b01c10807 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -19,8 +19,6 @@ import typing as t import unittest -from collections import namedtuple - from music21 import chord from music21 import common from music21 import defaults @@ -34,8 +32,87 @@ from music21 import pitch from music21 import scale -FigureTuple = namedtuple('FigureTuple', ['aboveBass', 'alter', 'prefix']) -ChordFigureTuple = namedtuple('ChordFigureTuple', ['aboveBass', 'alter', 'prefix', 'pitch']) +class FigureTuple(t.NamedTuple): + ''' + A namedtuple of a scale step above a reference pitch, a chromatic + alteration, and the accidental string to print before a figure. + + Produced by :func:`~music21.roman.figureTupleSolo` and consumed by + :func:`~music21.roman.romanNumeralFromChord` and + :func:`~music21.roman.correctRNAlterationForMinor`. + + >>> ft = roman.FigureTuple(aboveBass=6, alter=1.0, prefix='#') + >>> ft + FigureTuple(aboveBass=6, alter=1.0, prefix='#') + >>> ft.alter + 1.0 + + `aboveBass` is the generic (diatonic) interval, mod 7, above whatever + reference pitch was supplied. Despite the name, that reference is not + always the sounding bass: `romanNumeralFromChord` passes the *tonic* of + the key, so that there `aboveBass` means "scale degree of the chord's + root" (which is how `correctRNAlterationForMinor` reads it). + + `alter` is the chromatic alteration in semitones (a float) of the pitch + relative to the diatonic scale of the key. For minor keys the natural + minor scale is always the reference, so in c minor, A-flat has + `alter=0.0` and A-natural has `alter=1.0`. It is an alteration of a + scale step, never an interval above the bass. + + `prefix` is the string of accidentals ('#', '##', 'b', 'bb', ...) to + print before the figure. It is a figured-bass-style alteration in + music21's Roman-numeral spelling: it says how the pitch differs from + the pitches implied by the key signature (the key's diatonic scale), + with '#' meaning raised a semitone and 'b' lowered, regardless of what + accidental would appear on the note in staff notation. So A-natural + on ^6 in c minor gets `prefix='#'` even though the note itself would + be printed with a natural sign. No notational context is consulted -- + not the measure's accidental state, not courtesy accidentals, only + pitch versus key. + + The `sixthMinor`/`seventhMinor` conventions + (:class:`~music21.roman.Minor67Default`) are likewise *not* consulted + when a FigureTuple is created: `figureTupleSolo` renders `prefix` + purely from `alter`. A convention enters only afterwards, when + :func:`~music21.roman.correctRNAlterationForMinor` rewrites the tuple + for a chord root on ^6 or ^7 in minor -- and that function hardcodes + the CAUTIONARY style (it takes no Minor67Default argument; see issue + #1349). That rewrite is where `prefix` and `alter` deliberately + diverge: for instance, `FigureTuple(aboveBass=6, alter=0.0, prefix='b')` + means "diatonic in natural minor, but print a cautionary flat anyway." + Do not assume that `prefix` can be recomputed from `alter`. + + * Changed in v11: became a typed NamedTuple (was a collections.namedtuple). + This documentation was AI-assisted (Claude). + ''' + aboveBass: int + alter: float + prefix: str + + +class ChordFigureTuple(t.NamedTuple): + ''' + A :class:`~music21.roman.FigureTuple` that also carries the pitch it + describes, one entry per pitch of a chord. + + Produced by :func:`~music21.roman.figureTuples`, where -- unlike the + tonic-based use of `FigureTuple` in `romanNumeralFromChord` -- + `aboveBass` really is reckoned above the sounding bass of the chord. + See `FigureTuple` for the meaning of the shared fields. (NamedTuples + cannot inherit fields, so the first three are repeated here rather + than subclassed.) + + >>> cft = roman.ChordFigureTuple(6, 0.0, '', pitch.Pitch('A-4')) + >>> cft.pitch + + + * Changed in v11: became a typed NamedTuple (was a collections.namedtuple). + This documentation was AI-assisted (Claude). + ''' + aboveBass: int + alter: float + prefix: str + pitch: 'pitch.Pitch' environLocal = environment.Environment('roman') @@ -511,7 +588,7 @@ def figureTupleSolo( B FigureTuple(aboveBass=7, alter=1.0, prefix='#') B# FigureTuple(aboveBass=7, alter=2.0, prefix='##') - Returns a namedtuple called a FigureTuple. + Returns a :class:`~music21.roman.FigureTuple`. ''' unused_scaleStep, scaleAccidental = keyObj.getScaleDegreeAndAccidentalFromPitch(pitchObj) @@ -686,6 +763,11 @@ def correctRNAlterationForMinor( new FigureTuple correcting for the fact that, for instance, Ab in c minor is VI not vi. Works properly only if the note is the root of the chord. + The convention applied is the cautionary style of + :class:`~music21.roman.Minor67Default` `.CAUTIONARY` -- hardcoded: the + function does not (yet) take `sixthMinor`/`seventhMinor` arguments + (see issue #1349). + Used in RomanNumeralFromChord These return new FigureTuple objects @@ -735,6 +817,13 @@ def correctRNAlterationForMinor( >>> ft3 is ft4 True + Maintenance note: this is one of three places that implement the + "what does an accidental mean on ^6/^7 in minor, given chord quality" + convention, each with its own rules. The other two are the prefix + suppression for triads in `_postFigureFromChordAndKey` (generation of the + upper figures) and `RomanNumeral._adjustMinorVIandVIIByQuality` (parsing, + in the reverse direction). Change one and check the others. + * Changed in v11: the keyword-only argument `isMajorThird` was added, so that major-quality chords on raised ^6 and ^7 in minor keep their sharp prefix (issue #1349). This fix was AI-assisted (Claude). From 7bbd59c47f6d2bcdd96dfd5b037fbb8205c352a7 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:31:44 +0200 Subject: [PATCH 020/312] Rename FigureTuple field aboveBass and class ChordFigureTuple aboveBass becomes degFromRefPitch: the reference pitch is not always the bass (romanNumeralFromChord passes the tonic, measuring the chord's root), and the value is a 1-based simple-interval degree, not a zero-based distance above. ChordFigureTuple becomes PitchFigureTuple: one tuple describes a single pitch; a list of them describes a chord. Renames the matching local variable in figureTupleSolo and updates all docs and doctests. AI-assisted (Claude) --- music21/roman.py | 185 ++++++++++++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 84 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index 3b01c10807..b5f0ab2650 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -41,17 +41,23 @@ class FigureTuple(t.NamedTuple): :func:`~music21.roman.romanNumeralFromChord` and :func:`~music21.roman.correctRNAlterationForMinor`. - >>> ft = roman.FigureTuple(aboveBass=6, alter=1.0, prefix='#') + >>> ft = roman.FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') >>> ft - FigureTuple(aboveBass=6, alter=1.0, prefix='#') + FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') >>> ft.alter 1.0 - `aboveBass` is the generic (diatonic) interval, mod 7, above whatever - reference pitch was supplied. Despite the name, that reference is not - always the sounding bass: `romanNumeralFromChord` passes the *tonic* of - the key, so that there `aboveBass` means "scale degree of the chord's - root" (which is how `correctRNAlterationForMinor` reads it). + `degFromRefPitch` is the generic (diatonic) degree of the pitch counted + from whatever reference pitch was supplied, reduced to a simple + interval: always 1-7, 1-based as intervals and scale degrees are (the + reference itself is 1, not 0; an octave and a third is 3; an octave and + a seventh is 7). + Descending intervals invert, so a pitch a second *below* the reference + registers as 7. The reference is the chord's sounding bass + in :func:`~music21.roman.figureTuples`, but the *tonic* of the key in + `romanNumeralFromChord` -- there the pitch measured is the chord's + root, so `degFromRefPitch` is the scale degree of the root (which is + how `correctRNAlterationForMinor` reads it). `alter` is the chromatic alteration in semitones (a float) of the pitch relative to the diatonic scale of the key. For minor keys the natural @@ -78,41 +84,49 @@ class FigureTuple(t.NamedTuple): for a chord root on ^6 or ^7 in minor -- and that function hardcodes the CAUTIONARY style (it takes no Minor67Default argument; see issue #1349). That rewrite is where `prefix` and `alter` deliberately - diverge: for instance, `FigureTuple(aboveBass=6, alter=0.0, prefix='b')` + diverge: for instance, `FigureTuple(degFromRefPitch=6, alter=0.0, prefix='b')` means "diatonic in natural minor, but print a cautionary flat anyway." Do not assume that `prefix` can be recomputed from `alter`. - * Changed in v11: became a typed NamedTuple (was a collections.namedtuple). - This documentation was AI-assisted (Claude). + * Changed in v11: became a typed NamedTuple (was a collections.namedtuple), + and the first field was renamed from `aboveBass` to `degFromRefPitch`, + since the reference pitch is not always the bass and the count is + 1-based. This documentation was AI-assisted (Claude). ''' - aboveBass: int + degFromRefPitch: int alter: float prefix: str -class ChordFigureTuple(t.NamedTuple): +class PitchFigureTuple(t.NamedTuple): ''' - A :class:`~music21.roman.FigureTuple` that also carries the pitch it - describes, one entry per pitch of a chord. + Like a :class:`~music21.roman.FigureTuple` (not a subclass -- NamedTuples + cannot inherit fields, so the first three are repeated here) but also + carrying the pitch it describes, one entry per pitch of a chord. Produced by :func:`~music21.roman.figureTuples`, where -- unlike the - tonic-based use of `FigureTuple` in `romanNumeralFromChord` -- - `aboveBass` really is reckoned above the sounding bass of the chord. - See `FigureTuple` for the meaning of the shared fields. (NamedTuples - cannot inherit fields, so the first three are repeated here rather - than subclassed.) - - >>> cft = roman.ChordFigureTuple(6, 0.0, '', pitch.Pitch('A-4')) - >>> cft.pitch + tonic-based use of `FigureTuple` in `romanNumeralFromChord` -- the + reference pitch for `degFromRefPitch` is the sounding bass of the chord. + See `FigureTuple` for the meaning of the shared fields. + + Here is an A-flat a sixth above a C bass in c minor: diatonic there + (`alter=0.0`), so no accidental prefix is needed: + + >>> pft = roman.PitchFigureTuple(6, 0.0, '', pitch.Pitch('A-4')) + >>> pft.pitch - * Changed in v11: became a typed NamedTuple (was a collections.namedtuple). + * Changed in v11: became a typed NamedTuple (was a collections.namedtuple); + renamed from `ChordFigureTuple`, since one tuple describes a single + pitch (a list of them describes a chord); and the first field was + renamed from `aboveBass` to `degFromRefPitch`, since the reference + pitch is not always the bass and the count is 1-based. This documentation was AI-assisted (Claude). ''' - aboveBass: int + degFromRefPitch: int alter: float prefix: str - pitch: 'pitch.Pitch' + pitch: 'pitch.Pitch' # needs quotes since 'pitch' is redefined by this field environLocal = environment.Environment('roman') @@ -438,21 +452,21 @@ def _postFigureFromChordAndKey(chordObj: chord.Chord, keyObj: key.Key) -> str: ) for ft in sorted(chordFigureTuples, - key=lambda tup: (-1 * tup.aboveBass, tup.alter, tup.pitch.ps)): + key=lambda tup: (-1 * tup.degFromRefPitch, tup.alter, tup.pitch.ps)): # (diatonicIntervalNum, alter, alterStr, pitchObj) = figureTuple prefix = ft.prefix - if ft.aboveBass != 1 and ft.pitch is third: + if ft.degFromRefPitch != 1 and ft.pitch is third: if isMajorTriad or isMinorTriad: prefix = '' # alterStr[1:] # elif isMinorTriad and ft.alter > 0: # prefix = '' # alterStr[1:] - elif (ft.aboveBass != 1 + elif (ft.degFromRefPitch != 1 and ft.pitch is fifth and chordObjIsStandardTriad): prefix = '' # alterStr[1:] - if ft.aboveBass == 1: + if ft.degFromRefPitch == 1: if ft.alter != bassFigureAlter and prefix != '': # mark altered octaves as 8 not 1 figureString = prefix + '8' @@ -460,7 +474,7 @@ def _postFigureFromChordAndKey(chordObj: chord.Chord, keyObj: key.Key) -> str: # filter duplicates and put at beginning allFigureStringList.insert(0, figureString) else: - figureString = prefix + str(ft.aboveBass) + figureString = prefix + str(ft.degFromRefPitch) # filter out duplicates. if figureString not in allFigureStringList: allFigureStringList.append(figureString) @@ -484,11 +498,12 @@ def _postFigureFromChordAndKey(chordObj: chord.Chord, keyObj: key.Key) -> str: return allFigureString -def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[ChordFigureTuple]: +def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigureTuple]: ''' - Return a set of tuplets for each pitch showing the presence of a note, its - interval above the bass its alteration (float) from a step in the given - key, an `alterationString`, and the pitch object. + Return a list of :class:`~music21.roman.PitchFigureTuple` objects, one + per pitch of the chord, each showing the 1-based degree of the note + counted from the chord's bass, its alteration (float) from a step in the + given key, an accidental prefix string, and the pitch object. Note though that for roman numerals, the applicable key is almost always the root. @@ -499,10 +514,10 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[ChordFigu ... chord.Chord(['F#2', 'D3', 'A-3', 'C#4']), ... key.Key('C'), ... ) - [ChordFigureTuple(aboveBass=1, alter=1.0, prefix='#', pitch=), - ChordFigureTuple(aboveBass=6, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=3, alter=-1.0, prefix='b', pitch=), - ChordFigureTuple(aboveBass=5, alter=1.0, prefix='#', pitch=)] + [PitchFigureTuple(degFromRefPitch=1, alter=1.0, prefix='#', pitch=), + PitchFigureTuple(degFromRefPitch=6, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b', pitch=), + PitchFigureTuple(degFromRefPitch=5, alter=1.0, prefix='#', pitch=)] In c-minor, the A- is a normal note, so the prefix is '' not 'b'. The natural minor is used exclusively. @@ -511,10 +526,10 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[ChordFigu ... chord.Chord(['F#2', 'D3', 'A-3', 'C#4']), ... key.Key('c'), ... ) - [ChordFigureTuple(aboveBass=1, alter=1.0, prefix='#', pitch=), - ChordFigureTuple(aboveBass=6, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=3, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=5, alter=1.0, prefix='#', pitch=)] + [PitchFigureTuple(degFromRefPitch=1, alter=1.0, prefix='#', pitch=), + PitchFigureTuple(degFromRefPitch=6, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=5, alter=1.0, prefix='#', pitch=)] A C dominant-seventh chord in c minor alters the bass but not the 7th degree. @@ -522,25 +537,25 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[ChordFigu ... chord.Chord(['E3', 'C4', 'G4', 'B-5']), ... key.Key('c'), ... ) - [ChordFigureTuple(aboveBass=1, alter=1.0, prefix='#', pitch=), - ChordFigureTuple(aboveBass=6, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=3, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=5, alter=0.0, prefix='', pitch=)] + [PitchFigureTuple(degFromRefPitch=1, alter=1.0, prefix='#', pitch=), + PitchFigureTuple(degFromRefPitch=6, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=5, alter=0.0, prefix='', pitch=)] >>> roman.figureTuples( ... chord.Chord(['C4', 'E4', 'G4', 'C#4']), ... key.Key('C'), ... ) - [ChordFigureTuple(aboveBass=1, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=3, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=5, alter=0.0, prefix='', pitch=), - ChordFigureTuple(aboveBass=1, alter=1.0, prefix='#', pitch=)] + [PitchFigureTuple(degFromRefPitch=1, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=5, alter=0.0, prefix='', pitch=), + PitchFigureTuple(degFromRefPitch=1, alter=1.0, prefix='#', pitch=)] ''' result = [] bass = chordObject.bass() for thisPitch in chordObject.pitches: shortTuple = figureTupleSolo(thisPitch, keyObject, bass) - appendTuple = ChordFigureTuple(shortTuple.aboveBass, + appendTuple = PitchFigureTuple(shortTuple.degFromRefPitch, shortTuple.alter, shortTuple.prefix, thisPitch) @@ -554,9 +569,11 @@ def figureTupleSolo( bass: pitch.Pitch ) -> FigureTuple: ''' - Return a single tuple for a pitch and key showing the interval above - the bass, its alteration from a step in the given key, an alteration - string, and the pitch object. + Return a single :class:`~music21.roman.FigureTuple` for a pitch and key, + showing the 1-based degree of the pitch counted from the reference pitch + given as `bass`, its alteration from a step in the given key, and an + accidental prefix string. (The reference need not be a sounding bass: + `romanNumeralFromChord` passes the tonic of the key here.) For instance, in C major, an A-3 above an F# bass would be: @@ -565,7 +582,7 @@ def figureTupleSolo( ... key.Key('C'), ... pitch.Pitch('F#2'), ... ) - FigureTuple(aboveBass=3, alter=-1.0, prefix='b') + FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') These figures can be more complex in minor, so this is a good reference, showing that natural minor is always used. @@ -575,25 +592,25 @@ def figureTupleSolo( >>> for name in ('E--', 'E-', 'E', 'E#', 'A--', 'A-', 'A', 'A#', 'B--', 'B-', 'B', 'B#'): ... ft = roman.figureTupleSolo(pitch.Pitch(name + '4'), c, c_as_bass) ... print(f'{name:4s} {ft}') - E-- FigureTuple(aboveBass=3, alter=-1.0, prefix='b') - E- FigureTuple(aboveBass=3, alter=0.0, prefix='') - E FigureTuple(aboveBass=3, alter=1.0, prefix='#') - E# FigureTuple(aboveBass=3, alter=2.0, prefix='##') - A-- FigureTuple(aboveBass=6, alter=-1.0, prefix='b') - A- FigureTuple(aboveBass=6, alter=0.0, prefix='') - A FigureTuple(aboveBass=6, alter=1.0, prefix='#') - A# FigureTuple(aboveBass=6, alter=2.0, prefix='##') - B-- FigureTuple(aboveBass=7, alter=-1.0, prefix='b') - B- FigureTuple(aboveBass=7, alter=0.0, prefix='') - B FigureTuple(aboveBass=7, alter=1.0, prefix='#') - B# FigureTuple(aboveBass=7, alter=2.0, prefix='##') + E-- FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') + E- FigureTuple(degFromRefPitch=3, alter=0.0, prefix='') + E FigureTuple(degFromRefPitch=3, alter=1.0, prefix='#') + E# FigureTuple(degFromRefPitch=3, alter=2.0, prefix='##') + A-- FigureTuple(degFromRefPitch=6, alter=-1.0, prefix='b') + A- FigureTuple(degFromRefPitch=6, alter=0.0, prefix='') + A FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') + A# FigureTuple(degFromRefPitch=6, alter=2.0, prefix='##') + B-- FigureTuple(degFromRefPitch=7, alter=-1.0, prefix='b') + B- FigureTuple(degFromRefPitch=7, alter=0.0, prefix='') + B FigureTuple(degFromRefPitch=7, alter=1.0, prefix='#') + B# FigureTuple(degFromRefPitch=7, alter=2.0, prefix='##') Returns a :class:`~music21.roman.FigureTuple`. ''' unused_scaleStep, scaleAccidental = keyObj.getScaleDegreeAndAccidentalFromPitch(pitchObj) thisInterval = interval.Interval(bass, pitchObj) - aboveBass = thisInterval.diatonic.generic.mod7 + degFromRefPitch = thisInterval.diatonic.generic.mod7 if scaleAccidental is None: rootAlterationString = '' alterDiff = 0.0 @@ -607,7 +624,7 @@ def figureTupleSolo( else: rootAlterationString = '' - appendTuple = FigureTuple(aboveBass, alterDiff, rootAlterationString) + appendTuple = FigureTuple(degFromRefPitch, alterDiff, rootAlterationString) return appendTuple @@ -772,48 +789,48 @@ def correctRNAlterationForMinor( These return new FigureTuple objects - >>> ft5 = roman.FigureTuple(aboveBass=6, alter=-1, prefix='') + >>> ft5 = roman.FigureTuple(degFromRefPitch=6, alter=-1, prefix='') >>> ft5a = roman.correctRNAlterationForMinor(ft5, key.Key('c')) >>> ft5a - FigureTuple(aboveBass=6, alter=-1, prefix='b') + FigureTuple(degFromRefPitch=6, alter=-1, prefix='b') >>> ft5a is ft5 False - >>> ft6 = roman.FigureTuple(aboveBass=6, alter=0, prefix='') + >>> ft6 = roman.FigureTuple(degFromRefPitch=6, alter=0, prefix='') >>> roman.correctRNAlterationForMinor(ft6, key.Key('c')) - FigureTuple(aboveBass=6, alter=0, prefix='b') + FigureTuple(degFromRefPitch=6, alter=0, prefix='b') For a chord whose quality is minor, diminished, or half-diminished, a raised root is implied by the lowercase numeral, so the sharp prefix is removed: - >>> ft7 = roman.FigureTuple(aboveBass=7, alter=1, prefix='#') + >>> ft7 = roman.FigureTuple(degFromRefPitch=7, alter=1, prefix='#') >>> roman.correctRNAlterationForMinor(ft7, key.Key('c')) - FigureTuple(aboveBass=7, alter=0, prefix='') + FigureTuple(degFromRefPitch=7, alter=0, prefix='') But if the chord has a major third, pass `isMajorThird=True` so that the sharp is kept: an uppercase VI or VII in minor already refers to the chord on the *lowered* (natural minor) degree, so a major chord on the raised degree needs its sharp to be distinguished from it: - >>> ft8 = roman.FigureTuple(aboveBass=7, alter=1, prefix='#') + >>> ft8 = roman.FigureTuple(degFromRefPitch=7, alter=1, prefix='#') >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), isMajorThird=True) - FigureTuple(aboveBass=7, alter=1, prefix='#') + FigureTuple(degFromRefPitch=7, alter=1, prefix='#') Does nothing for major and passes in the original Figure Tuple unchanged: - >>> ft1 = roman.FigureTuple(aboveBass=6, alter=-1, prefix='b') + >>> ft1 = roman.FigureTuple(degFromRefPitch=6, alter=-1, prefix='b') >>> ft2 = roman.correctRNAlterationForMinor(ft1, key.Key('C')) >>> ft2 - FigureTuple(aboveBass=6, alter=-1, prefix='b') + FigureTuple(degFromRefPitch=6, alter=-1, prefix='b') >>> ft1 is ft2 True Does nothing for steps other than 6 or 7: - >>> ft3 = roman.FigureTuple(aboveBass=4, alter=-1, prefix='b') + >>> ft3 = roman.FigureTuple(degFromRefPitch=4, alter=-1, prefix='b') >>> ft4 = roman.correctRNAlterationForMinor(ft3, key.Key('c')) >>> ft4 - FigureTuple(aboveBass=4, alter=-1, prefix='b') + FigureTuple(degFromRefPitch=4, alter=-1, prefix='b') >>> ft3 is ft4 True @@ -830,7 +847,7 @@ def correctRNAlterationForMinor( ''' if keyObj.mode != 'minor': return figureTuple - if figureTuple.aboveBass not in (6, 7): + if figureTuple.degFromRefPitch not in (6, 7): return figureTuple if isMajorThird and figureTuple.alter >= 1.0: # Keep the sharp(s): plain VI or VII in minor means the chord on the @@ -855,7 +872,7 @@ def correctRNAlterationForMinor( elif alter < 0.0: rootAlterationString = 'b' + rootAlterationString - return FigureTuple(figureTuple.aboveBass, alter, rootAlterationString) + return FigureTuple(figureTuple.degFromRefPitch, alter, rootAlterationString) def romanNumeralFromChord( @@ -1323,7 +1340,7 @@ def romanNumeralFromChord( alteredKeyObj = _getKeyFromCache(tonicPitchName) - stepRoman = common.toRoman(ft.aboveBass) + stepRoman = common.toRoman(ft.degFromRefPitch) if isMajorThird: pass elif not isMajorThird: From 9dafd5d2e19a569d9545b93f79b03db875d9e94e Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:39:14 +0200 Subject: [PATCH 021/312] Move maintenance note from public docstring to code comment Public docstrings are for library users; maintenance notes and references to private methods belong in comments for maintainers. AI-assisted (Claude) --- music21/roman.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index b5f0ab2650..b3054c0caf 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -834,17 +834,16 @@ def correctRNAlterationForMinor( >>> ft3 is ft4 True - Maintenance note: this is one of three places that implement the - "what does an accidental mean on ^6/^7 in minor, given chord quality" - convention, each with its own rules. The other two are the prefix - suppression for triads in `_postFigureFromChordAndKey` (generation of the - upper figures) and `RomanNumeral._adjustMinorVIandVIIByQuality` (parsing, - in the reverse direction). Change one and check the others. - * Changed in v11: the keyword-only argument `isMajorThird` was added, so that major-quality chords on raised ^6 and ^7 in minor keep their sharp prefix (issue #1349). This fix was AI-assisted (Claude). ''' + # Maintenance note: this is one of three places that implement the + # "what does an accidental mean on ^6/^7 in minor, given chord quality" + # convention, each with its own rules. The other two are the prefix + # suppression for triads in _postFigureFromChordAndKey (generation of the + # upper figures) and RomanNumeral._adjustMinorVIandVIIByQuality (parsing, + # in the reverse direction). Change one and check the others. if keyObj.mode != 'minor': return figureTuple if figureTuple.degFromRefPitch not in (6, 7): From c7175433281edb53f7e5e0273b7a46693732d8e5 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:44:11 +0200 Subject: [PATCH 022/312] Rename correctRNAlterationForMinor param isMajorThird to uppercaseNumeral The function corrects Roman-numeral alterations, so its parameter should speak in numeral terms, not chord anatomy: what it consumes is whether the numeral will be written in uppercase (lowercase numerals already imply the raised ^6/^7 in minor). Major third and uppercase numeral are the same bit; romanNumeralFromChord translates at the call site. AI-assisted (Claude) --- music21/roman.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index b3054c0caf..253b9af1c7 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -771,7 +771,7 @@ def correctRNAlterationForMinor( figureTuple: FigureTuple, keyObj: key.Key, *, - isMajorThird: bool = False, + uppercaseNumeral: bool = False, ) -> FigureTuple: ''' (This will become a private function in a future version) @@ -807,13 +807,14 @@ def correctRNAlterationForMinor( >>> roman.correctRNAlterationForMinor(ft7, key.Key('c')) FigureTuple(degFromRefPitch=7, alter=0, prefix='') - But if the chord has a major third, pass `isMajorThird=True` so that the - sharp is kept: an uppercase VI or VII in minor already refers to the chord - on the *lowered* (natural minor) degree, so a major chord on the raised - degree needs its sharp to be distinguished from it: + But when the numeral will be written in uppercase (major and augmented + qualities), pass `uppercaseNumeral=True` so that the sharp is kept: an + uppercase VI or VII in minor already refers to the chord on the *lowered* + (natural minor) degree, so a major chord on the raised degree needs its + sharp to be distinguished from it: >>> ft8 = roman.FigureTuple(degFromRefPitch=7, alter=1, prefix='#') - >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), isMajorThird=True) + >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), uppercaseNumeral=True) FigureTuple(degFromRefPitch=7, alter=1, prefix='#') Does nothing for major and passes in the original Figure Tuple unchanged: @@ -834,9 +835,10 @@ def correctRNAlterationForMinor( >>> ft3 is ft4 True - * Changed in v11: the keyword-only argument `isMajorThird` was added, so that - major-quality chords on raised ^6 and ^7 in minor keep their sharp prefix - (issue #1349). This fix was AI-assisted (Claude). + * Changed in v11: the keyword-only argument `uppercaseNumeral` was added, + so that chords whose numerals are written in uppercase keep their sharp + prefix on raised ^6 and ^7 in minor (issue #1349). + This fix was AI-assisted (Claude). ''' # Maintenance note: this is one of three places that implement the # "what does an accidental mean on ^6/^7 in minor, given chord quality" @@ -848,7 +850,7 @@ def correctRNAlterationForMinor( return figureTuple if figureTuple.degFromRefPitch not in (6, 7): return figureTuple - if isMajorThird and figureTuple.alter >= 1.0: + if uppercaseNumeral and figureTuple.alter >= 1.0: # Keep the sharp(s): plain VI or VII in minor means the chord on the # lowered (natural minor) degree under both the QUALITY and CAUTIONARY # conventions, so stripping the prefix would move the root down a @@ -1318,7 +1320,7 @@ def romanNumeralFromChord( keyObj = key.Key(keyObj) ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple - ft = correctRNAlterationForMinor(ft, keyObj, isMajorThird=isMajorThird) + ft = correctRNAlterationForMinor(ft, keyObj, uppercaseNumeral=isMajorThird) if ft.alter == 0: tonicPitch = keyObj.tonic From 5f53f2c0c868effec431a4ca6776aefc3ba617ce Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:45:02 +0200 Subject: [PATCH 023/312] Add shared agent memories: doc-style rules and issue #1349 direction Checks session-learned working knowledge into the repo-shared memory so it survives across machines: public docs vs maintainer comments, no untaken-paths prose in docstrings, and the status/open questions on the romanNumeral raised-6/7 work. AI-assisted (Claude) --- .agents/memory/MEMORY.md | 3 ++ .agents/memory/docs-audience-separation.md | 20 +++++++++++ .agents/memory/docs-no-untaken-paths.md | 21 ++++++++++++ .../issue-1349-romanNumeral-direction.md | 34 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 .agents/memory/docs-audience-separation.md create mode 100644 .agents/memory/docs-no-untaken-paths.md create mode 100644 .agents/memory/issue-1349-romanNumeral-direction.md diff --git a/.agents/memory/MEMORY.md b/.agents/memory/MEMORY.md index 2bcdd6844c..cc2ef6f580 100644 --- a/.agents/memory/MEMORY.md +++ b/.agents/memory/MEMORY.md @@ -6,3 +6,6 @@ Project-shared agent memory for music21. One file per fact; see frontmatter for - [sortedcontainers for streams](sortedcontainers-for-streams.md) — tried twice to make streams always-sorted via SortedList; abandoned, too slow for tiny streams - [Stream subclass generic defaults](stream-subclass-generic-defaults.md) — how `inherit-generics` should work once PEP 696 default type params land; maybe add typing_extensions - [BFS-flatten-iterator](bfs-flatten-iterator.md) — lazy breadth-first flatten is writable (maybe for tree first), but must not reuse the flat/flatten name tied to offsetInHierarchy +- [Docs: audience separation](docs-audience-separation.md) — maintenance notes and private-method references go in code comments, never public docstrings +- [Docs: no untaken paths](docs-no-untaken-paths.md) — docstrings state what the code does, never contrast with rejected designs +- [Issue #1349 romanNumeral direction](issue-1349-romanNumeral-direction.md) — raised-^6/^7 fix landed; CAUTIONARY default kept; items 2+4 open with pablopupo/Malcolm diff --git a/.agents/memory/docs-audience-separation.md b/.agents/memory/docs-audience-separation.md new file mode 100644 index 0000000000..4abeb19997 --- /dev/null +++ b/.agents/memory/docs-audience-separation.md @@ -0,0 +1,20 @@ +--- +name: docs-audience-separation +description: Public docstrings are for library users; maintenance notes and private-symbol references go in code comments +metadata: + type: feedback +--- + +Never put maintenance notes ("change one and check the others", refactoring +caveats, cross-references between internal implementations) in music21 public +docstrings, and never reference private (underscore) methods from public +docstrings. Put that material in `#` code comments instead. + +**Why:** docstrings render into the published docs and serve library *users*; +comments serve *maintainers*. Myke: "there are different users with different +roles." A user reading the API docs should never see internal plumbing they +cannot call. + +**How to apply:** before finishing a doc pass, scan new docstring text for +underscore-prefixed names and maintainer-facing advice; relocate to a comment +just below the docstring. Related style rule: [[docs-no-untaken-paths]]. diff --git a/.agents/memory/docs-no-untaken-paths.md b/.agents/memory/docs-no-untaken-paths.md new file mode 100644 index 0000000000..dfddf3f2d0 --- /dev/null +++ b/.agents/memory/docs-no-untaken-paths.md @@ -0,0 +1,21 @@ +--- +name: docs-no-untaken-paths +description: Docstrings and doctests describe what the code does, never the designs it avoided +metadata: + type: feedback +--- + +When writing docstrings and doctests for music21, do not spend words (or +doctest lines) explaining what the code *doesn't* do or contrasting with +rejected alternatives — e.g. cut "not literally 'mod 7', which would make it +0" from a description that already states the value is always 1-7. Myke: "we +don't spend doctest time reinforcing paths we didn't take." + +**Why:** every sentence in a docstring is maintained forever and read by users +who never saw the alternative; contrast-with-the-wrong-design is +reviewer-facing noise, like comments justifying a change. + +**How to apply:** state the positive contract with examples of real values. If +a name is misleading (like `GenericInterval.mod7`, which actually returns +simple-interval values 1-7), document the actual behavior — don't argue with +the name in prose. Related style rule: [[docs-audience-separation]]. diff --git a/.agents/memory/issue-1349-romanNumeral-direction.md b/.agents/memory/issue-1349-romanNumeral-direction.md new file mode 100644 index 0000000000..5bb9a024e7 --- /dev/null +++ b/.agents/memory/issue-1349-romanNumeral-direction.md @@ -0,0 +1,34 @@ +--- +name: issue-1349-romanNumeral-direction +description: Status and direction for RomanNumeral.romanNumeral vs romanNumeralFromChord (GitHub issues #1349 and #1249) +metadata: + type: project +--- + +As of 2026-07-06 (branch `fix-1349-raised-67`): the core raised-^6/^7 bug in +GitHub issue #1349 is FIXED — `correctRNAlterationForMinor` gained a +keyword-only `uppercaseNumeral` argument so major-quality chords on raised +^6/^7 in minor keep their sharp (figure `#VI`, `.romanNumeral` `'#VI'`), and +the tsvConverter `localKeyAsRn` workaround was removed. Myke decided NOT to +flip the `romanNumeralFromChord` default from CAUTIONARY to QUALITY (her 2022 +idea in the issue) for now. Still open, to discuss with contributor pablopupo +and Malcolm Sailor on the issue: (2) adding `sixthMinor`/`seventhMinor` +parameters to `romanNumeralFromChord` and possibly flipping its default, and +(4) issue #1249 (secondary-numeral `#vii` prefixing). + +**Why:** `.romanNumeral` is the *semantic* root-degree token (alteration +relative to the key's natural scale, read from `frontAlterationAccidental`), +while `.figure` is notational. `harmonicFunction.romanToFunction` and the +tsvConverter DCML export depend on the semantic reading, so the property +should never be deprecated. Known remaining wart: seventh chords on raised +^6/^7 in minor still don't round-trip their figures (e.g. `#VIb753` reparses +with G-flat) — the CAUTIONARY generation convention computes inversion figures +against an "altered key" while parsing measures against the original scale, +and the mismatch differs per degree. + +**How to apply:** when returning to items 2+4, the minor-^6/^7 accidental +convention lives in three places that must stay in sync (see the maintenance +comment in `correctRNAlterationForMinor`). The same session renamed +`FigureTuple.aboveBass` to `degFromRefPitch` and `ChordFigureTuple` to +`PitchFigureTuple`; the `bass` parameter of `figureTupleSolo` is still +unrenamed, an open question. From 8e24962653c7d4aefc60955ce44154769382f64d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:46:57 +0200 Subject: [PATCH 024/312] Rename local isMajorThird to chordHasMajorThird; trim PitchFigureTuple doc The variable names a fact about the chord, not about any single interval or figure entry. Also cut the not-a-subclass parenthetical from the PitchFigureTuple docstring. AI-assisted (Claude) --- music21/roman.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index 253b9af1c7..8d80e533a9 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -100,9 +100,8 @@ class FigureTuple(t.NamedTuple): class PitchFigureTuple(t.NamedTuple): ''' - Like a :class:`~music21.roman.FigureTuple` (not a subclass -- NamedTuples - cannot inherit fields, so the first three are repeated here) but also - carrying the pitch it describes, one entry per pitch of a chord. + Like a :class:`~music21.roman.FigureTuple` but also carrying the pitch + it describes, one entry per pitch of a chord. Produced by :func:`~music21.roman.figureTuples`, where -- unlike the tonic-based use of `FigureTuple` in `romanNumeralFromChord` -- the @@ -1305,13 +1304,13 @@ def romanNumeralFromChord( root = chordObj.root() thirdType = chordObj.semitonesFromChordStep(3) if thirdType == 4: - isMajorThird = True + chordHasMajorThird = True else: - isMajorThird = False + chordHasMajorThird = False if keyObj is None: - if isMajorThird: + if chordHasMajorThird: rootKeyObj = _getKeyFromCache(root.name.upper()) else: rootKeyObj = _getKeyFromCache(root.name.lower()) @@ -1320,7 +1319,7 @@ def romanNumeralFromChord( keyObj = key.Key(keyObj) ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple - ft = correctRNAlterationForMinor(ft, keyObj, uppercaseNumeral=isMajorThird) + ft = correctRNAlterationForMinor(ft, keyObj, uppercaseNumeral=chordHasMajorThird) if ft.alter == 0: tonicPitch = keyObj.tonic @@ -1342,20 +1341,20 @@ def romanNumeralFromChord( alteredKeyObj = _getKeyFromCache(tonicPitchName) stepRoman = common.toRoman(ft.degFromRefPitch) - if isMajorThird: + if chordHasMajorThird: pass - elif not isMajorThird: + elif not chordHasMajorThird: stepRoman = stepRoman.lower() inversionString = _postFigureFromChordAndKey(chordObj, alteredKeyObj) rnString = ft.prefix + stepRoman + inversionString - if (not isMajorThird + if (not chordHasMajorThird and inversionString in minorSeventhSubs # only do expensive call in case it might be possible and chordObj.isSeventhOfType((0, 3, 7, 10))): rnString = ft.prefix + stepRoman + minorSeventhSubs[inversionString] - elif (not isMajorThird + elif (not chordHasMajorThird and inversionString in minorMajorSeventhSubs and chordObj.isSeventhOfType((0, 3, 7, 11))): rnString = ft.prefix + stepRoman + minorMajorSeventhSubs[inversionString] From 3c75fc4d7911c9f2a525c0b761654045cb9a5d67 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:49:34 +0200 Subject: [PATCH 025/312] Rename correctRNAlterationForMinor param to chordHasMajorThird Name the parameter for the chord-level fact it carries, not for its consequence: no numeral exists yet when the function runs, so uppercaseNumeral described something outside its inputs. One concept now has one name in caller and callee alike; the docstring explains the case convention that makes the fact matter. AI-assisted (Claude) --- .../issue-1349-romanNumeral-direction.md | 7 ++++-- music21/roman.py | 24 +++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.agents/memory/issue-1349-romanNumeral-direction.md b/.agents/memory/issue-1349-romanNumeral-direction.md index 5bb9a024e7..47fe7d46ae 100644 --- a/.agents/memory/issue-1349-romanNumeral-direction.md +++ b/.agents/memory/issue-1349-romanNumeral-direction.md @@ -7,9 +7,12 @@ metadata: As of 2026-07-06 (branch `fix-1349-raised-67`): the core raised-^6/^7 bug in GitHub issue #1349 is FIXED — `correctRNAlterationForMinor` gained a -keyword-only `uppercaseNumeral` argument so major-quality chords on raised +keyword-only `chordHasMajorThird` argument so major-quality chords on raised ^6/^7 in minor keep their sharp (figure `#VI`, `.romanNumeral` `'#VI'`), and -the tsvConverter `localKeyAsRn` workaround was removed. Myke decided NOT to +the tsvConverter `localKeyAsRn` workaround was removed. (Naming settled after +two rounds: name the parameter for the chord-level fact it carries, not for +its consequence like "uppercaseNumeral" — the numeral doesn't exist yet when +the function runs.) Myke decided NOT to flip the `romanNumeralFromChord` default from CAUTIONARY to QUALITY (her 2022 idea in the issue) for now. Still open, to discuss with contributor pablopupo and Malcolm Sailor on the issue: (2) adding `sixthMinor`/`seventhMinor` diff --git a/music21/roman.py b/music21/roman.py index 8d80e533a9..9f5e29c6b0 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -770,7 +770,7 @@ def correctRNAlterationForMinor( figureTuple: FigureTuple, keyObj: key.Key, *, - uppercaseNumeral: bool = False, + chordHasMajorThird: bool = False, ) -> FigureTuple: ''' (This will become a private function in a future version) @@ -806,14 +806,14 @@ def correctRNAlterationForMinor( >>> roman.correctRNAlterationForMinor(ft7, key.Key('c')) FigureTuple(degFromRefPitch=7, alter=0, prefix='') - But when the numeral will be written in uppercase (major and augmented - qualities), pass `uppercaseNumeral=True` so that the sharp is kept: an - uppercase VI or VII in minor already refers to the chord on the *lowered* - (natural minor) degree, so a major chord on the raised degree needs its - sharp to be distinguished from it: + But when the chord has a major third (and thus an uppercase numeral), + pass `chordHasMajorThird=True` so that the sharp is kept: an uppercase + VI or VII in minor already refers to the chord on the *lowered* (natural + minor) degree, so a major chord on the raised degree needs its sharp to + be distinguished from it: >>> ft8 = roman.FigureTuple(degFromRefPitch=7, alter=1, prefix='#') - >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), uppercaseNumeral=True) + >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), chordHasMajorThird=True) FigureTuple(degFromRefPitch=7, alter=1, prefix='#') Does nothing for major and passes in the original Figure Tuple unchanged: @@ -834,9 +834,9 @@ def correctRNAlterationForMinor( >>> ft3 is ft4 True - * Changed in v11: the keyword-only argument `uppercaseNumeral` was added, - so that chords whose numerals are written in uppercase keep their sharp - prefix on raised ^6 and ^7 in minor (issue #1349). + * Changed in v11: the keyword-only argument `chordHasMajorThird` was + added, so that major-quality chords keep their sharp prefix on raised + ^6 and ^7 in minor (issue #1349). This fix was AI-assisted (Claude). ''' # Maintenance note: this is one of three places that implement the @@ -849,7 +849,7 @@ def correctRNAlterationForMinor( return figureTuple if figureTuple.degFromRefPitch not in (6, 7): return figureTuple - if uppercaseNumeral and figureTuple.alter >= 1.0: + if chordHasMajorThird and figureTuple.alter >= 1.0: # Keep the sharp(s): plain VI or VII in minor means the chord on the # lowered (natural minor) degree under both the QUALITY and CAUTIONARY # conventions, so stripping the prefix would move the root down a @@ -1319,7 +1319,7 @@ def romanNumeralFromChord( keyObj = key.Key(keyObj) ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple - ft = correctRNAlterationForMinor(ft, keyObj, uppercaseNumeral=chordHasMajorThird) + ft = correctRNAlterationForMinor(ft, keyObj, chordHasMajorThird=chordHasMajorThird) if ft.alter == 0: tonicPitch = keyObj.tonic From 3b1632b7b8b40dacb68eb873b2f4cf9e72bb95d1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 08:52:28 +0200 Subject: [PATCH 026/312] Simplify chordHasMajorThird assignment and dead elif The variable is a plain bool (semitonesFromChordStep returns None for third-less chords, which the == 4 comparison maps to False), so the five-line assignment collapses to one and the 'if X: pass / elif not X' becomes a single negated if. AI-assisted (Claude) --- music21/roman.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index 9f5e29c6b0..a0c85be803 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -1302,12 +1302,9 @@ def romanNumeralFromChord( # 'augmented': {5: 1}, # } root = chordObj.root() - thirdType = chordObj.semitonesFromChordStep(3) - if thirdType == 4: - chordHasMajorThird = True - else: - chordHasMajorThird = False - + # semitonesFromChordStep(3) returns None for third-less chords, so this + # is False for them, and they get lowercase numerals below. + chordHasMajorThird = chordObj.semitonesFromChordStep(3) == 4 if keyObj is None: if chordHasMajorThird: @@ -1341,9 +1338,7 @@ def romanNumeralFromChord( alteredKeyObj = _getKeyFromCache(tonicPitchName) stepRoman = common.toRoman(ft.degFromRefPitch) - if chordHasMajorThird: - pass - elif not chordHasMajorThird: + if not chordHasMajorThird: stepRoman = stepRoman.lower() inversionString = _postFigureFromChordAndKey(chordObj, alteredKeyObj) From 14fe359eac76ea85f91cb5d78ba9d618621c0b2d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 09:09:56 +0200 Subject: [PATCH 027/312] Rename figureTupleSolo to figureTupleFromPitch, deprecate old name Follows the module's result-From-source naming (romanNumeralFromChord); the old name read as the verb 'figure'. The new function's reference-pitch parameter is refPitch (was bass), matching FigureTuple.degFromRefPitch and the fact that romanNumeralFromChord passes the tonic, not a bass. figureTupleSolo remains at its old spot as a deprecated (v11 -> v12) alias with the old signature. AI-assisted (Claude) --- .../issue-1349-romanNumeral-direction.md | 9 ++-- music21/roman.py | 42 +++++++++++++------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/.agents/memory/issue-1349-romanNumeral-direction.md b/.agents/memory/issue-1349-romanNumeral-direction.md index 47fe7d46ae..a8f7b02b61 100644 --- a/.agents/memory/issue-1349-romanNumeral-direction.md +++ b/.agents/memory/issue-1349-romanNumeral-direction.md @@ -32,6 +32,9 @@ and the mismatch differs per degree. **How to apply:** when returning to items 2+4, the minor-^6/^7 accidental convention lives in three places that must stay in sync (see the maintenance comment in `correctRNAlterationForMinor`). The same session renamed -`FigureTuple.aboveBass` to `degFromRefPitch` and `ChordFigureTuple` to -`PitchFigureTuple`; the `bass` parameter of `figureTupleSolo` is still -unrenamed, an open question. +`FigureTuple.aboveBass` to `degFromRefPitch`, `ChordFigureTuple` to +`PitchFigureTuple`, and `figureTupleSolo` to `figureTupleFromPitch` (with +`refPitch` replacing the `bass` parameter; the old name remains as a +deprecated v11→v12 alias). Myke is not all-in on classmethod constructors — +prefer module-level `From` functions like +`romanNumeralFromChord`. diff --git a/music21/roman.py b/music21/roman.py index a0c85be803..d0fb8f2b32 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -37,7 +37,7 @@ class FigureTuple(t.NamedTuple): A namedtuple of a scale step above a reference pitch, a chromatic alteration, and the accidental string to print before a figure. - Produced by :func:`~music21.roman.figureTupleSolo` and consumed by + Produced by :func:`~music21.roman.figureTupleFromPitch` and consumed by :func:`~music21.roman.romanNumeralFromChord` and :func:`~music21.roman.correctRNAlterationForMinor`. @@ -78,7 +78,7 @@ class FigureTuple(t.NamedTuple): The `sixthMinor`/`seventhMinor` conventions (:class:`~music21.roman.Minor67Default`) are likewise *not* consulted - when a FigureTuple is created: `figureTupleSolo` renders `prefix` + when a FigureTuple is created: `figureTupleFromPitch` renders `prefix` purely from `alter`. A convention enters only afterwards, when :func:`~music21.roman.correctRNAlterationForMinor` rewrites the tuple for a chord root on ^6 or ^7 in minor -- and that function hardcodes @@ -553,7 +553,7 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu result = [] bass = chordObject.bass() for thisPitch in chordObject.pitches: - shortTuple = figureTupleSolo(thisPitch, keyObject, bass) + shortTuple = figureTupleFromPitch(thisPitch, keyObject, bass) appendTuple = PitchFigureTuple(shortTuple.degFromRefPitch, shortTuple.alter, shortTuple.prefix, @@ -562,21 +562,21 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu return result -def figureTupleSolo( +def figureTupleFromPitch( pitchObj: pitch.Pitch, keyObj: key.Key, - bass: pitch.Pitch + refPitch: pitch.Pitch, ) -> FigureTuple: ''' Return a single :class:`~music21.roman.FigureTuple` for a pitch and key, - showing the 1-based degree of the pitch counted from the reference pitch - given as `bass`, its alteration from a step in the given key, and an - accidental prefix string. (The reference need not be a sounding bass: - `romanNumeralFromChord` passes the tonic of the key here.) + showing the 1-based degree of the pitch counted from `refPitch`, its + alteration from a step in the given key, and an accidental prefix string. + (The reference need not be a sounding bass: `romanNumeralFromChord` + passes the tonic of the key here.) For instance, in C major, an A-3 above an F# bass would be: - >>> roman.figureTupleSolo( + >>> roman.figureTupleFromPitch( ... pitch.Pitch('A-3'), ... key.Key('C'), ... pitch.Pitch('F#2'), @@ -589,7 +589,7 @@ def figureTupleSolo( >>> c = key.Key('c') >>> c_as_bass = pitch.Pitch('C3') >>> for name in ('E--', 'E-', 'E', 'E#', 'A--', 'A-', 'A', 'A#', 'B--', 'B-', 'B', 'B#'): - ... ft = roman.figureTupleSolo(pitch.Pitch(name + '4'), c, c_as_bass) + ... ft = roman.figureTupleFromPitch(pitch.Pitch(name + '4'), c, c_as_bass) ... print(f'{name:4s} {ft}') E-- FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') E- FigureTuple(degFromRefPitch=3, alter=0.0, prefix='') @@ -605,10 +605,14 @@ def figureTupleSolo( B# FigureTuple(degFromRefPitch=7, alter=2.0, prefix='##') Returns a :class:`~music21.roman.FigureTuple`. + + * Changed in v11: renamed from `figureTupleSolo` (which remains as a + deprecated alias); the reference-pitch parameter is `refPitch` + (was `bass`). ''' unused_scaleStep, scaleAccidental = keyObj.getScaleDegreeAndAccidentalFromPitch(pitchObj) - thisInterval = interval.Interval(bass, pitchObj) + thisInterval = interval.Interval(refPitch, pitchObj) degFromRefPitch = thisInterval.diatonic.generic.mod7 if scaleAccidental is None: rootAlterationString = '' @@ -627,6 +631,18 @@ def figureTupleSolo( return appendTuple +@common.deprecated('v11', 'v12', 'use figureTupleFromPitch() instead') +def figureTupleSolo( + pitchObj: pitch.Pitch, + keyObj: key.Key, + bass: pitch.Pitch +) -> FigureTuple: + ''' + Deprecated: use :func:`~music21.roman.figureTupleFromPitch` instead. + ''' + return figureTupleFromPitch(pitchObj, keyObj, bass) + + def identifyAsTonicOrDominant( inChord: list|tuple|chord.Chord, inKey: key.Key @@ -1315,7 +1331,7 @@ def romanNumeralFromChord( elif isinstance(keyObj, str): keyObj = key.Key(keyObj) - ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple + ft = figureTupleFromPitch(root, keyObj, keyObj.tonic) # a FigureTuple ft = correctRNAlterationForMinor(ft, keyObj, chordHasMajorThird=chordHasMajorThird) if ft.alter == 0: From abde876ca9e896908d8300309f0f99c2ba226841 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 09:13:32 +0200 Subject: [PATCH 028/312] Revert "Rename figureTupleSolo to figureTupleFromPitch, deprecate old name" This reverts commit 14fe359eac76ea85f91cb5d78ba9d618621c0b2d. --- .../issue-1349-romanNumeral-direction.md | 9 ++-- music21/roman.py | 42 ++++++------------- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/.agents/memory/issue-1349-romanNumeral-direction.md b/.agents/memory/issue-1349-romanNumeral-direction.md index a8f7b02b61..47fe7d46ae 100644 --- a/.agents/memory/issue-1349-romanNumeral-direction.md +++ b/.agents/memory/issue-1349-romanNumeral-direction.md @@ -32,9 +32,6 @@ and the mismatch differs per degree. **How to apply:** when returning to items 2+4, the minor-^6/^7 accidental convention lives in three places that must stay in sync (see the maintenance comment in `correctRNAlterationForMinor`). The same session renamed -`FigureTuple.aboveBass` to `degFromRefPitch`, `ChordFigureTuple` to -`PitchFigureTuple`, and `figureTupleSolo` to `figureTupleFromPitch` (with -`refPitch` replacing the `bass` parameter; the old name remains as a -deprecated v11→v12 alias). Myke is not all-in on classmethod constructors — -prefer module-level `From` functions like -`romanNumeralFromChord`. +`FigureTuple.aboveBass` to `degFromRefPitch` and `ChordFigureTuple` to +`PitchFigureTuple`; the `bass` parameter of `figureTupleSolo` is still +unrenamed, an open question. diff --git a/music21/roman.py b/music21/roman.py index d0fb8f2b32..a0c85be803 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -37,7 +37,7 @@ class FigureTuple(t.NamedTuple): A namedtuple of a scale step above a reference pitch, a chromatic alteration, and the accidental string to print before a figure. - Produced by :func:`~music21.roman.figureTupleFromPitch` and consumed by + Produced by :func:`~music21.roman.figureTupleSolo` and consumed by :func:`~music21.roman.romanNumeralFromChord` and :func:`~music21.roman.correctRNAlterationForMinor`. @@ -78,7 +78,7 @@ class FigureTuple(t.NamedTuple): The `sixthMinor`/`seventhMinor` conventions (:class:`~music21.roman.Minor67Default`) are likewise *not* consulted - when a FigureTuple is created: `figureTupleFromPitch` renders `prefix` + when a FigureTuple is created: `figureTupleSolo` renders `prefix` purely from `alter`. A convention enters only afterwards, when :func:`~music21.roman.correctRNAlterationForMinor` rewrites the tuple for a chord root on ^6 or ^7 in minor -- and that function hardcodes @@ -553,7 +553,7 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu result = [] bass = chordObject.bass() for thisPitch in chordObject.pitches: - shortTuple = figureTupleFromPitch(thisPitch, keyObject, bass) + shortTuple = figureTupleSolo(thisPitch, keyObject, bass) appendTuple = PitchFigureTuple(shortTuple.degFromRefPitch, shortTuple.alter, shortTuple.prefix, @@ -562,21 +562,21 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu return result -def figureTupleFromPitch( +def figureTupleSolo( pitchObj: pitch.Pitch, keyObj: key.Key, - refPitch: pitch.Pitch, + bass: pitch.Pitch ) -> FigureTuple: ''' Return a single :class:`~music21.roman.FigureTuple` for a pitch and key, - showing the 1-based degree of the pitch counted from `refPitch`, its - alteration from a step in the given key, and an accidental prefix string. - (The reference need not be a sounding bass: `romanNumeralFromChord` - passes the tonic of the key here.) + showing the 1-based degree of the pitch counted from the reference pitch + given as `bass`, its alteration from a step in the given key, and an + accidental prefix string. (The reference need not be a sounding bass: + `romanNumeralFromChord` passes the tonic of the key here.) For instance, in C major, an A-3 above an F# bass would be: - >>> roman.figureTupleFromPitch( + >>> roman.figureTupleSolo( ... pitch.Pitch('A-3'), ... key.Key('C'), ... pitch.Pitch('F#2'), @@ -589,7 +589,7 @@ def figureTupleFromPitch( >>> c = key.Key('c') >>> c_as_bass = pitch.Pitch('C3') >>> for name in ('E--', 'E-', 'E', 'E#', 'A--', 'A-', 'A', 'A#', 'B--', 'B-', 'B', 'B#'): - ... ft = roman.figureTupleFromPitch(pitch.Pitch(name + '4'), c, c_as_bass) + ... ft = roman.figureTupleSolo(pitch.Pitch(name + '4'), c, c_as_bass) ... print(f'{name:4s} {ft}') E-- FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') E- FigureTuple(degFromRefPitch=3, alter=0.0, prefix='') @@ -605,14 +605,10 @@ def figureTupleFromPitch( B# FigureTuple(degFromRefPitch=7, alter=2.0, prefix='##') Returns a :class:`~music21.roman.FigureTuple`. - - * Changed in v11: renamed from `figureTupleSolo` (which remains as a - deprecated alias); the reference-pitch parameter is `refPitch` - (was `bass`). ''' unused_scaleStep, scaleAccidental = keyObj.getScaleDegreeAndAccidentalFromPitch(pitchObj) - thisInterval = interval.Interval(refPitch, pitchObj) + thisInterval = interval.Interval(bass, pitchObj) degFromRefPitch = thisInterval.diatonic.generic.mod7 if scaleAccidental is None: rootAlterationString = '' @@ -631,18 +627,6 @@ def figureTupleFromPitch( return appendTuple -@common.deprecated('v11', 'v12', 'use figureTupleFromPitch() instead') -def figureTupleSolo( - pitchObj: pitch.Pitch, - keyObj: key.Key, - bass: pitch.Pitch -) -> FigureTuple: - ''' - Deprecated: use :func:`~music21.roman.figureTupleFromPitch` instead. - ''' - return figureTupleFromPitch(pitchObj, keyObj, bass) - - def identifyAsTonicOrDominant( inChord: list|tuple|chord.Chord, inKey: key.Key @@ -1331,7 +1315,7 @@ def romanNumeralFromChord( elif isinstance(keyObj, str): keyObj = key.Key(keyObj) - ft = figureTupleFromPitch(root, keyObj, keyObj.tonic) # a FigureTuple + ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple ft = correctRNAlterationForMinor(ft, keyObj, chordHasMajorThird=chordHasMajorThird) if ft.alter == 0: From c055e05992a2668aebbb3ce1bfa791d24cf0d671 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 6 Jul 2026 09:15:54 +0200 Subject: [PATCH 029/312] Replace figureTupleSolo with FigureTuple.fromPitchAndReference classmethod The measurement that produces a FigureTuple now lives on the type it produces, with the reference-pitch parameter named refPitch to match degFromRefPitch. figureTupleSolo stays at its old spot as a deprecated (v11 -> v12) alias with its old signature. AI-assisted (Claude) --- .../issue-1349-romanNumeral-direction.md | 7 +- music21/roman.py | 138 ++++++++++-------- 2 files changed, 82 insertions(+), 63 deletions(-) diff --git a/.agents/memory/issue-1349-romanNumeral-direction.md b/.agents/memory/issue-1349-romanNumeral-direction.md index 47fe7d46ae..7831b06ad8 100644 --- a/.agents/memory/issue-1349-romanNumeral-direction.md +++ b/.agents/memory/issue-1349-romanNumeral-direction.md @@ -33,5 +33,8 @@ and the mismatch differs per degree. convention lives in three places that must stay in sync (see the maintenance comment in `correctRNAlterationForMinor`). The same session renamed `FigureTuple.aboveBass` to `degFromRefPitch` and `ChordFigureTuple` to -`PitchFigureTuple`; the `bass` parameter of `figureTupleSolo` is still -unrenamed, an open question. +`PitchFigureTuple`, and replaced `figureTupleSolo` with the classmethod +constructor `FigureTuple.fromPitchAndReference(pitchObj, keyObj, refPitch)` +(old name kept as a deprecated v11→v12 alias). Myke is not all-in on +classmethod constructors in general, but preferred one here over a +module-level function whose name read as the verb "figure". diff --git a/music21/roman.py b/music21/roman.py index a0c85be803..6161be2576 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -37,8 +37,8 @@ class FigureTuple(t.NamedTuple): A namedtuple of a scale step above a reference pitch, a chromatic alteration, and the accidental string to print before a figure. - Produced by :func:`~music21.roman.figureTupleSolo` and consumed by - :func:`~music21.roman.romanNumeralFromChord` and + Produced by :meth:`~music21.roman.FigureTuple.fromPitchAndReference` + and consumed by :func:`~music21.roman.romanNumeralFromChord` and :func:`~music21.roman.correctRNAlterationForMinor`. >>> ft = roman.FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') @@ -78,7 +78,7 @@ class FigureTuple(t.NamedTuple): The `sixthMinor`/`seventhMinor` conventions (:class:`~music21.roman.Minor67Default`) are likewise *not* consulted - when a FigureTuple is created: `figureTupleSolo` renders `prefix` + when a FigureTuple is created: `fromPitchAndReference` renders `prefix` purely from `alter`. A convention enters only afterwards, when :func:`~music21.roman.correctRNAlterationForMinor` rewrites the tuple for a chord root on ^6 or ^7 in minor -- and that function hardcodes @@ -97,6 +97,74 @@ class FigureTuple(t.NamedTuple): alter: float prefix: str + @classmethod + def fromPitchAndReference( + cls, + pitchObj: pitch.Pitch, + keyObj: key.Key, + refPitch: pitch.Pitch, + ) -> FigureTuple: + ''' + Measure a pitch against a key and a reference pitch, returning the + FigureTuple of the 1-based degree of the pitch counted from + `refPitch`, its alteration from a step in the given key, and an + accidental prefix string. (The reference need not be a sounding + bass: `romanNumeralFromChord` passes the tonic of the key here.) + + For instance, in C major, an A-3 above an F# bass would be: + + >>> roman.FigureTuple.fromPitchAndReference( + ... pitch.Pitch('A-3'), + ... key.Key('C'), + ... pitch.Pitch('F#2'), + ... ) + FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') + + These figures can be more complex in minor, so this is a good + reference, showing that natural minor is always used. + + >>> c = key.Key('c') + >>> c_as_bass = pitch.Pitch('C3') + >>> for name in ('E--', 'E-', 'E', 'E#', 'A--', 'A-', 'A', 'A#', + ... 'B--', 'B-', 'B', 'B#'): + ... ft = roman.FigureTuple.fromPitchAndReference( + ... pitch.Pitch(name + '4'), c, c_as_bass) + ... print(f'{name:4s} {ft}') + E-- FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') + E- FigureTuple(degFromRefPitch=3, alter=0.0, prefix='') + E FigureTuple(degFromRefPitch=3, alter=1.0, prefix='#') + E# FigureTuple(degFromRefPitch=3, alter=2.0, prefix='##') + A-- FigureTuple(degFromRefPitch=6, alter=-1.0, prefix='b') + A- FigureTuple(degFromRefPitch=6, alter=0.0, prefix='') + A FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') + A# FigureTuple(degFromRefPitch=6, alter=2.0, prefix='##') + B-- FigureTuple(degFromRefPitch=7, alter=-1.0, prefix='b') + B- FigureTuple(degFromRefPitch=7, alter=0.0, prefix='') + B FigureTuple(degFromRefPitch=7, alter=1.0, prefix='#') + B# FigureTuple(degFromRefPitch=7, alter=2.0, prefix='##') + + * New in v11: replaces `figureTupleSolo`, now a deprecated alias. + ''' + unused_scaleStep, scaleAccidental = ( + keyObj.getScaleDegreeAndAccidentalFromPitch(pitchObj)) + + thisInterval = interval.Interval(refPitch, pitchObj) + degFromRefPitch = thisInterval.diatonic.generic.mod7 + if scaleAccidental is None: + rootAlterationString = '' + alterDiff = 0.0 + else: + alterDiff = scaleAccidental.alter + alter = int(alterDiff) + if alter < 0: + rootAlterationString = 'b' * (-1 * alter) + elif alter > 0: + rootAlterationString = '#' * alter + else: + rootAlterationString = '' + + return cls(degFromRefPitch, alterDiff, rootAlterationString) + class PitchFigureTuple(t.NamedTuple): ''' @@ -553,7 +621,7 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu result = [] bass = chordObject.bass() for thisPitch in chordObject.pitches: - shortTuple = figureTupleSolo(thisPitch, keyObject, bass) + shortTuple = FigureTuple.fromPitchAndReference(thisPitch, keyObject, bass) appendTuple = PitchFigureTuple(shortTuple.degFromRefPitch, shortTuple.alter, shortTuple.prefix, @@ -562,69 +630,17 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu return result +@common.deprecated('v11', 'v12', 'use FigureTuple.fromPitchAndReference() instead') def figureTupleSolo( pitchObj: pitch.Pitch, keyObj: key.Key, bass: pitch.Pitch ) -> FigureTuple: ''' - Return a single :class:`~music21.roman.FigureTuple` for a pitch and key, - showing the 1-based degree of the pitch counted from the reference pitch - given as `bass`, its alteration from a step in the given key, and an - accidental prefix string. (The reference need not be a sounding bass: - `romanNumeralFromChord` passes the tonic of the key here.) - - For instance, in C major, an A-3 above an F# bass would be: - - >>> roman.figureTupleSolo( - ... pitch.Pitch('A-3'), - ... key.Key('C'), - ... pitch.Pitch('F#2'), - ... ) - FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') - - These figures can be more complex in minor, so this is a good reference, showing - that natural minor is always used. - - >>> c = key.Key('c') - >>> c_as_bass = pitch.Pitch('C3') - >>> for name in ('E--', 'E-', 'E', 'E#', 'A--', 'A-', 'A', 'A#', 'B--', 'B-', 'B', 'B#'): - ... ft = roman.figureTupleSolo(pitch.Pitch(name + '4'), c, c_as_bass) - ... print(f'{name:4s} {ft}') - E-- FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') - E- FigureTuple(degFromRefPitch=3, alter=0.0, prefix='') - E FigureTuple(degFromRefPitch=3, alter=1.0, prefix='#') - E# FigureTuple(degFromRefPitch=3, alter=2.0, prefix='##') - A-- FigureTuple(degFromRefPitch=6, alter=-1.0, prefix='b') - A- FigureTuple(degFromRefPitch=6, alter=0.0, prefix='') - A FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') - A# FigureTuple(degFromRefPitch=6, alter=2.0, prefix='##') - B-- FigureTuple(degFromRefPitch=7, alter=-1.0, prefix='b') - B- FigureTuple(degFromRefPitch=7, alter=0.0, prefix='') - B FigureTuple(degFromRefPitch=7, alter=1.0, prefix='#') - B# FigureTuple(degFromRefPitch=7, alter=2.0, prefix='##') - - Returns a :class:`~music21.roman.FigureTuple`. + Deprecated: use :meth:`~music21.roman.FigureTuple.fromPitchAndReference` + instead. ''' - unused_scaleStep, scaleAccidental = keyObj.getScaleDegreeAndAccidentalFromPitch(pitchObj) - - thisInterval = interval.Interval(bass, pitchObj) - degFromRefPitch = thisInterval.diatonic.generic.mod7 - if scaleAccidental is None: - rootAlterationString = '' - alterDiff = 0.0 - else: - alterDiff = scaleAccidental.alter - alter = int(alterDiff) - if alter < 0: - rootAlterationString = 'b' * (-1 * alter) - elif alter > 0: - rootAlterationString = '#' * alter - else: - rootAlterationString = '' - - appendTuple = FigureTuple(degFromRefPitch, alterDiff, rootAlterationString) - return appendTuple + return FigureTuple.fromPitchAndReference(pitchObj, keyObj, bass) def identifyAsTonicOrDominant( @@ -1315,7 +1331,7 @@ def romanNumeralFromChord( elif isinstance(keyObj, str): keyObj = key.Key(keyObj) - ft = figureTupleSolo(root, keyObj, keyObj.tonic) # a FigureTuple + ft = FigureTuple.fromPitchAndReference(root, keyObj, keyObj.tonic) ft = correctRNAlterationForMinor(ft, keyObj, chordHasMajorThird=chordHasMajorThird) if ft.alter == 0: From 96646438a3892708115956f668c662771cfcc971 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 9 Jul 2026 10:16:27 +0200 Subject: [PATCH 030/312] Auto-set musescoreDirectPNGPath when choosing MuseScore; stop double-escaping env values configure.py: when the configuration assistant's MusicXML-reader dialog selects a MuseScore build (MuseScore.app, MuseScore 4.app, mscore, etc., matched by the new reMuseScoreName regex), also set musescoreDirectPNGPath to the same path so PNG/PDF generation works without further setup. Selecting a non-MuseScore reader leaves any existing musescoreDirectPNGPath untouched. The two "set to" messages are merged: MuseScore selections print "MusicXML Reader and PNG/PDF generator set to: ...". environment.py: _EnvironmentCore.__setitem__ manually ran saxutils.escape on string values, but toSettingsXML serializes through ElementTree, which already escapes attribute values (and parsing unescapes them). The manual pass double-escaped, so a path containing "&" was stored and returned as "&". Removed the manual escape (and the now-unused saxutils import); ElementTree handles it. Added testSpecialCharactersRoundTrip covering set -> escaped XML -> parse round-trip for a path with "&" and "<>". AI-assisted (Claude). --- music21/configure.py | 27 +++++++++++++++++++++++++-- music21/environment.py | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/music21/configure.py b/music21/configure.py index e3c24e4ad4..3a5510a693 100644 --- a/music21/configure.py +++ b/music21/configure.py @@ -41,6 +41,9 @@ reFinaleReaderApp = re.compile(r'Finale Reader\.app', IGNORECASE) reMuseScoreApp = re.compile(r'MuseScore.*\.app', IGNORECASE) reMuseScoreExe = re.compile(r'Musescore.*\\bin\\MuseScore.*\.exe', IGNORECASE) +# matches any MuseScore file/app name regardless of version or platform: +# MuseScore.app, MuseScore 4.app, MuseScore3.exe, mscore, mscore4portable, etc. +reMuseScoreName = re.compile(r'MuseScore|mscore', IGNORECASE) reDoricoApp = re.compile(r'Dorico.*\.app', IGNORECASE) reDoricoExe = re.compile(r'Dorico.*\.exe', IGNORECASE) @@ -1293,7 +1296,10 @@ def _askFillEmptyList(self, default=None, force=None): def _performAction(self, simulate=False): ''' - The action here is to open the stored URL in a browser, if the user agrees. + Set musicxmlPath to the selected reader. If the reader is a version + of MuseScore, also set musescoreDirectPNGPath to it. + + AI-assisted (Claude). ''' result = self.getResult() if result is not None and not isinstance(result, DialogError): @@ -1303,7 +1309,12 @@ def _performAction(self, simulate=False): # us['musicxmlPath'] = result # automatically writes environment.set('musicxmlPath', result) musicXmlNew = environment.get('musicxmlPath') - self._writeToUser([f'MusicXML Reader set to: {musicXmlNew}', ' ']) + if reMuseScoreName.search(pathlib.Path(str(result)).name): + environment.set('musescoreDirectPNGPath', musicXmlNew) + self._writeToUser( + [f'MusicXML Reader and PNG/PDF generator set to: {musicXmlNew}', ' ']) + else: + self._writeToUser([f'MusicXML Reader set to: {musicXmlNew}', ' ']) # ------------------------------------------------------------------------------ @@ -1579,6 +1590,18 @@ def getValidResults(force=None): # returns a bad condition b/c there are no options and user entered 'n' self.assertIsInstance(post, configure.BadConditions) + def testMuseScoreNameRe(self): + ''' + AI-assisted (Claude). + ''' + for name in ('MuseScore.app', 'MuseScore 4.app', 'MuseScore 4.5.app', + 'MuseScore3.exe', 'MuseScore4.exe', 'MuseScore Studio.app', + 'mscore', 'mscore3', 'mscore4portable'): + self.assertIsNotNone(reMuseScoreName.search(name), name) + for name in ('Finale 2011.app', 'Finale.exe', 'Sibelius.app', + 'Dorico 5.app', 'Finale Reader.app'): + self.assertIsNone(reMuseScoreName.search(name), name) + def testRe(self): g = reFinaleApp.search('Finale 2011.app') self.assertEqual(g.group(0), 'Finale 2011.app') diff --git a/music21/environment.py b/music21/environment.py index 0073ea8e48..696bba105a 100644 --- a/music21/environment.py +++ b/music21/environment.py @@ -33,7 +33,6 @@ from typing import overload import xml.etree.ElementTree as ET -from xml.sax import saxutils from music21 import exceptions21 from music21 import common @@ -234,11 +233,6 @@ def __repr__(self): return '' def __setitem__(self, key, value): - # saxutils.escape # used for escaping strings going to xml - # with unicode encoding - # http://www.xml.com/pub/a/2002/11/13/py-xml.html?page=2 - # saxutils.escape(msg).encode('UTF-8') - # add local corpus path as a key # if isinstance(value, bytes): # value = value.decode(errors='replace') @@ -275,9 +269,8 @@ def __setitem__(self, key, value): raise EnvironmentException( f'{value} is not an acceptable value for preference: {key}') - # need to escape problematic characters for xml storage - if isinstance(value, str): - value = saxutils.escape(value) # .encode('UTF-8') + # ElementTree handles XML escaping when serializing in toSettingsXML. + # set value if key == 'localCorpusPath': # only add if unique @@ -885,8 +878,8 @@ def __setitem__(self, key, value): >>> a = environment.Environment() >>> a['debug'] = 1 >>> a['graphicsPath'] = '/test&Encode' - >>> 'test&Encode' in str(a['graphicsPath']) - True + >>> str(a['graphicsPath']) + '/test&Encode' >>> isinstance(a['graphicsPath'], Path) True @@ -902,6 +895,8 @@ def __setitem__(self, key, value): >>> a['showFormat'] = 'musicxml' >>> a['localCorpusPath'] = '/path/to/local' + + * Fixed in v11: values containing ``&`` etc. are properly stored and retrieved. ''' envSingleton().__setitem__(key, value) @@ -1540,6 +1535,28 @@ def testToSettings(self): '/System/Applications/Preview', '/Applications/Preview'), match) self.assertTrue(common.whitespaceEqual(canonic, match) or true_but_for_preview_location) + @unittest.skipIf(common.getPlatform() == 'win', 'test assumes Unix-style paths') + def testSpecialCharactersRoundTrip(self): + ''' + A path containing XML special characters such as "&" is stored + unescaped, serialized escaped by ElementTree, and comes back + unescaped after parsing. + + AI-assisted (Claude). + ''' + core = _EnvironmentCore(forcePlatform='darwin') + fp = '/Applications/Q&A /MuseScore 4.app' + core.__setitem__('musicxmlPath', fp) + self.assertEqual(str(core.__getitem__('musicxmlPath')), fp) + + settingsTree = core.toSettingsXML() + match = self.stringFromTree(settingsTree) + self.assertIn('Q&A <Beta>', match) + + ref = {'musicxmlPath': None} + core._fromSettings(settingsTree, ref) + self.assertEqual(ref['musicxmlPath'], fp) + @unittest.skipIf(common.getPlatform() == 'win', 'test assumes Unix-style paths') def testFromSettings(self): From 6a46c2a59f4a45a0d4d680efa2b6490410915be2 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 9 Jul 2026 10:35:14 +0200 Subject: [PATCH 031/312] lint and update docs --- .idea/pyProjectModel.xml | 6 ++++++ music21/configure.py | 7 ++----- music21/environment.py | 6 ++++-- 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 .idea/pyProjectModel.xml diff --git a/.idea/pyProjectModel.xml b/.idea/pyProjectModel.xml new file mode 100644 index 0000000000..d3679d8f6c --- /dev/null +++ b/.idea/pyProjectModel.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/music21/configure.py b/music21/configure.py index 3a5510a693..e5e3ad3c1b 100644 --- a/music21/configure.py +++ b/music21/configure.py @@ -3,8 +3,9 @@ # Purpose: Installation and Configuration Utilities # # Authors: Christopher Ariza +# Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2011-2019 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2011-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -1298,15 +1299,11 @@ def _performAction(self, simulate=False): ''' Set musicxmlPath to the selected reader. If the reader is a version of MuseScore, also set musescoreDirectPNGPath to it. - - AI-assisted (Claude). ''' result = self.getResult() if result is not None and not isinstance(result, DialogError): # noinspection PyTypeChecker reload(environment) - # us = environment.UserSettings() - # us['musicxmlPath'] = result # automatically writes environment.set('musicxmlPath', result) musicXmlNew = environment.get('musicxmlPath') if reMuseScoreName.search(pathlib.Path(str(result)).name): diff --git a/music21/environment.py b/music21/environment.py index 696bba105a..dd724a2de5 100644 --- a/music21/environment.py +++ b/music21/environment.py @@ -1546,8 +1546,10 @@ def testSpecialCharactersRoundTrip(self): ''' core = _EnvironmentCore(forcePlatform='darwin') fp = '/Applications/Q&A /MuseScore 4.app' - core.__setitem__('musicxmlPath', fp) - self.assertEqual(str(core.__getitem__('musicxmlPath')), fp) + + # these lines use overridden __setitem__ and __getitem__ internally + core['musicxmlPath'] = fp + self.assertEqual(str(core['musicxmlPath']), fp) settingsTree = core.toSettingsXML() match = self.stringFromTree(settingsTree) From 5ab603c93bbd3de2f76caa3dcd51b2603271b502 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:30:14 +0000 Subject: [PATCH 032/312] build(deps): bump soupsieve from 2.8.3 to 2.8.4 Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.8.3 to 2.8.4. - [Release notes](https://github.com/facelessuser/soupsieve/releases) - [Commits](https://github.com/facelessuser/soupsieve/compare/2.8.3...2.8.4) --- updated-dependencies: - dependency-name: soupsieve dependency-version: 2.8.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index f442b84f88..006e74e080 100644 --- a/uv.lock +++ b/uv.lock @@ -2956,11 +2956,11 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8.3" +version = "2.8.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, + { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, ] [[package]] From d00556bdccebdfd54b56e109ccb10d94756c3c99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 21:03:52 +0000 Subject: [PATCH 033/312] build(deps): bump mistune from 3.2.1 to 3.3.0 Bumps [mistune](https://github.com/lepture/mistune) from 3.2.1 to 3.3.0. - [Release notes](https://github.com/lepture/mistune/releases) - [Changelog](https://github.com/lepture/mistune/blob/main/docs/changes.rst) - [Commits](https://github.com/lepture/mistune/compare/v3.2.1...v3.3.0) --- updated-dependencies: - dependency-name: mistune dependency-version: 3.3.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 006e74e080..d06fc0e0b3 100644 --- a/uv.lock +++ b/uv.lock @@ -1763,11 +1763,11 @@ wheels = [ [[package]] name = "mistune" -version = "3.2.1" +version = "3.3.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/84/620cc3f7e3adf6f5067e10f4dbae71295d8f9e16d5d3f9ef97c40f2f592c/mistune-3.2.1.tar.gz", hash = "sha256:7c8e5501d38bac1582e067e46c8343f17d57ea1aaa735823f3aba1fd59c88a28", size = 98003, upload-time = "2026-05-03T14:33:22.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/9c/1939635275ec7258e2b43b00dafabc36d89ad11aa7838d375dc1b0e561cb/mistune-3.3.0.tar.gz", hash = "sha256:3074ec4c61b384abe725128e4dcbb483f5a09cc4632012505cdee655d3a113b9", size = 110936, upload-time = "2026-06-21T13:11:39.458Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/7f/a946aa4f8752b37102b41e64dca18a1976ac705c3a0d1dfe74d820a02552/mistune-3.2.1-py3-none-any.whl", hash = "sha256:78cdb0ba5e938053ccf63651b352508d2efa9411dc8810bfb05f2dc5140c0048", size = 53749, upload-time = "2026-05-03T14:33:20.551Z" }, + { url = "https://files.pythonhosted.org/packages/b7/76/b90f9d48d43fbd80a79a20d3eab2e5109859c7a56dc663b23187385898f3/mistune-3.3.0-py3-none-any.whl", hash = "sha256:a758e578acda49d8195f9a860b132dae2cf7bf409381393b1c4e6e489a65397b", size = 61250, upload-time = "2026-06-21T13:11:37.938Z" }, ] [[package]] From 0f899235508c43a8453a7afebc8b5c8e977be2aa Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 20 Jul 2026 11:56:57 -1000 Subject: [PATCH 034/312] small improvements: simplifyingMultiplEnharmonics Added the feature to music21j and wanted to align the type systems; add docs, notes, and caveats. Make things keyword only that were already treated that way. --- music21/chord/__init__.py | 2 +- music21/pitch.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index da9fc2e8d9..e024ed1909 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -4375,7 +4375,7 @@ def simplifyEnharmonics( for i in range(len(pitches)): returnObj._notes[i].pitch = pitches[i] - if inPlace is False: + if not inPlace: return returnObj @overload diff --git a/music21/pitch.py b/music21/pitch.py index 3cc56dfe02..8d909b8d41 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -539,6 +539,7 @@ def _convertHarmonicToCents(value: int|float) -> int: def _dissonanceScore(pitches: list[Pitch], + *, smallPythagoreanRatio: bool = True, accidentalPenalty: bool = True, triadAward: bool = True) -> float: @@ -549,7 +550,10 @@ def _dissonanceScore(pitches: list[Pitch], Pythagorean ratios of its constituent intervals are small (`smallPythagoreanRatio`), (2) it shows few double- or triple-accidentals (`accidentalPenalty`), and (3) it shows thirds that can form some triad (`triadAward`). + + * Changed in v11: all options except pitches are keyword only. ''' + # TODO: test other options. score_accidentals = 0.0 score_ratio = 0.0 score_triad = 0.0 @@ -579,6 +583,10 @@ def _dissonanceScore(pitches: list[Pitch], for this_interval in intervals: # does not accept weird intervals, e.g. with semitones ratio = interval.intervalToPythagoreanRatio(this_interval) + # The constant 0.075853.... is 1 / ln(3**12), where 3**12 is 531441, the numerator + # of 531441/524288 -- the Pythagorean comma: it normalizes the penalty so a + # diminished second (the Pythagorean comma; slightly more than a unison), + # scores exactly 1.0, and all other penalties are based on that. penalty = math.log(ratio.denominator) * 0.07585326888 score_ratio += penalty @@ -626,6 +634,7 @@ def _greedyEnharmonicsSearch( def simplifyMultipleEnharmonics( pitches: t.Iterable[Pitch|str|int|float], + *, criterion: t.Callable[[list[Pitch]], float] = _dissonanceScore, keyContext: key.KeySignature|None = None ) -> list[Pitch]: @@ -675,13 +684,15 @@ def simplifyMultipleEnharmonics( ... keyContext=key.Key('C')) [, , ] - * Changed in v7.3: fixed a bug with compound intervals (such as formed against - the tonic of a KeySignature defaulting to octave 4): + Former bug: simplifying enharmonics in a key context will not remove octaves. >>> pitch.simplifyMultipleEnharmonics([pitch.Pitch('B5')], keyContext=key.Key('D')) [] - ''' + * Changed in v7.3: fixed a bug with compound intervals (such as those formed against + the tonic of a KeySignature defaulting to octave 4). + * Changed in v11: crtierion and keyContext are keyword only. + ''' oldPitches = [p if isinstance(p, Pitch) else Pitch(p) for p in pitches] if keyContext: From a84d52730de9950b3560a3d446faf4884ccf5cc8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 11:06:40 +0000 Subject: [PATCH 035/312] build(deps): bump pillow from 12.2.0 to 12.3.0 Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.2.0 to 12.3.0. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](https://github.com/python-pillow/Pillow/compare/12.2.0...12.3.0) --- updated-dependencies: - dependency-name: pillow dependency-version: 12.3.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 132 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/uv.lock b/uv.lock index d06fc0e0b3..10c8084df4 100644 --- a/uv.lock +++ b/uv.lock @@ -2163,71 +2163,73 @@ wheels = [ [[package]] name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, ] [[package]] From a5ea55817f5468ae7c238c3cc16271b23d4d9e17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jul 2026 04:13:43 +0000 Subject: [PATCH 036/312] build(deps): bump setuptools from 82.0.1 to 83.0.0 Bumps [setuptools](https://github.com/pypa/setuptools) from 82.0.1 to 83.0.0. - [Release notes](https://github.com/pypa/setuptools/releases) - [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/setuptools/compare/v82.0.1...v83.0.0) --- updated-dependencies: - dependency-name: setuptools dependency-version: 83.0.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 10c8084df4..6c1fc0d869 100644 --- a/uv.lock +++ b/uv.lock @@ -2922,11 +2922,11 @@ wheels = [ [[package]] name = "setuptools" -version = "82.0.1" +version = "83.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, + { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, ] [[package]] From 0becda8cd14a7b8c909bf8415a16f4327c2189a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Jul 2026 07:09:44 +0000 Subject: [PATCH 037/312] build(deps): bump jupyterlab from 4.5.9 to 4.5.10 Bumps [jupyterlab](https://github.com/jupyterlab/jupyterlab) from 4.5.9 to 4.5.10. - [Changelog](https://github.com/jupyterlab/jupyterlab/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterlab/jupyterlab/compare/@jupyterlab/lsp@4.5.9...@jupyterlab/lsp@4.5.10) --- updated-dependencies: - dependency-name: jupyterlab dependency-version: 4.5.10 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 10c8084df4..53412ba9f5 100644 --- a/uv.lock +++ b/uv.lock @@ -1306,7 +1306,7 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.5.9" +version = "4.5.10" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1323,9 +1323,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/52/a8d4895bef501ffeb6af448e8bf7079541c7772978211963aa653518c2d9/jupyterlab-4.5.9.tar.gz", hash = "sha256:dd79a073fecae7a39066ea99e4627ed6c76269ac926e95a810e1e1df6358d865", size = 23994445, upload-time = "2026-06-17T15:42:16.406Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/24/621aa20ec0d2fe72f52095bda0fc1be7738ac21aabe4129ff623140d5cdf/jupyterlab-4.5.10.tar.gz", hash = "sha256:77e8d80b78be59b2eaba2154562e21caa6e79c2f1281d6f486584f7144ee2f47", size = 23998879, upload-time = "2026-07-21T12:43:27.324Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/bb/2f9b425062416fba58f580c9b89c3b07277ccdf0a292501fedbca8ea00ea/jupyterlab-4.5.9-py3-none-any.whl", hash = "sha256:5ff0f908e8ac0afbed32b106fdef360f101c0a6654d1bf4a81e98a293ae1b336", size = 12449803, upload-time = "2026-06-17T15:42:12.18Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c9/940f95f17ee4e413ad252bf8d4f2ee9a341f18cfeda87775fef3d7847321/jupyterlab-4.5.10-py3-none-any.whl", hash = "sha256:5967ca61e692e67a2f30b5a2b901c941dc6ce56c0b0e357bc6d34fed5ec095f6", size = 12452502, upload-time = "2026-07-21T12:43:23.542Z" }, ] [[package]] From bfdb18c558a8b50442d8da9972b1c3f34b622086 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 27 Jul 2026 11:27:16 -1000 Subject: [PATCH 038/312] Rename .worktreeignore to .worktreeinclude and trim its entries The harness only ever reads .worktreeinclude, so the old filename matched no code path and nothing was actually being copied into new worktrees. Also drop three entries that are not worth copying per worktree: .venv (~560 MB / 18k files, and its interpreter symlinks point back at the source checkout -- run 'uv sync' instead), .mypy_cache (~210 MB, rebuilt on first run), and documentation/build (~150 MB of Sphinx HTML; the tracked sources under documentation/source are what agents actually read). AI-assisted (Claude) --- .worktreeignore | 20 -------------------- .worktreeinclude | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 20 deletions(-) delete mode 100644 .worktreeignore create mode 100644 .worktreeinclude diff --git a/.worktreeignore b/.worktreeignore deleted file mode 100644 index e39ef00ff2..0000000000 --- a/.worktreeignore +++ /dev/null @@ -1,20 +0,0 @@ -# .worktreeignore -# -# Paths that are git-ignored (see .gitignore) but are expensive to regenerate -# and worth carrying into / sharing across a fresh worktree, so a new worktree -# is usable immediately instead of after a full rebuild. - -# Python virtual environment (expensive `uv sync`) -.venv - -# Build / packaging artifacts -dist -build - -# Built documentation (expensive Sphinx build) -documentation/build - -# Tool caches that speed up repeated runs -.mypy_cache -.pytest_cache -.coverage diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 0000000000..963c851d43 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,23 @@ +# .worktreeinclude +# +# Paths that are git-ignored (see .gitignore) but are expensive to regenerate +# and worth copying into a fresh worktree, so a new worktree is usable +# immediately instead of after a full rebuild. +# +# Keep this list small: every entry is copied file-by-file into each worktree. +# Anything large, cheap to regenerate, or holding absolute paths belongs in +# .gitignore only: +# .venv run `uv sync` in the new worktree instead; a copy is +# ~560 MB / 18k files and its interpreter symlinks point +# back at the source checkout. +# .mypy_cache ~210 MB, and mypy rebuilds it on first run. +# documentation/build ~150 MB of Sphinx HTML; rebuild with +# `documentation/make.py` on the rare occasion it is needed. + +# Build / packaging artifacts +dist +build + +# Tool caches that speed up repeated runs +.pytest_cache +.coverage From 71101dbefd8201a98bbf408ac66a00b1b909b0b0 Mon Sep 17 00:00:00 2001 From: ahmadalguydi Date: Thu, 30 Jul 2026 17:14:45 +0300 Subject: [PATCH 039/312] abc: import aligned lyric fields --- music21/_version.py | 2 +- music21/abcFormat/translate.py | 80 ++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index d45d6c0cd6..240ab9a1f5 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b7' +__version__ = '11.0.0b8' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/abcFormat/translate.py b/music21/abcFormat/translate.py index e80b08788e..82a32878cb 100644 --- a/music21/abcFormat/translate.py +++ b/music21/abcFormat/translate.py @@ -109,6 +109,7 @@ def abcToStreamPart( barCount = 0 measureNumber = 1 + lyricTargets: list[note.GeneralNote] = [] # merged handler are ABCHandlerBar objects, defining attributes for barlines for mh in mergedHandlers: @@ -181,7 +182,9 @@ def abcToStreamPart( # environLocal.printDebug([mh, 'dst', dst]) # ql = 0 # might not be zero if there is a pickup - postTransposition, clefSet = parseTokens(mh, dst, p, useMeasures) + postTransposition, clefSet = parseTokens( + mh, dst, p, useMeasures, lyricTargets + ) # append measure to part; in the case of trailing metadata # dst may be part, even though useMeasures is True @@ -253,7 +256,8 @@ def parseTokens( mh: abcFormat.ABCHandler, dst: stream.Measure|stream.Part, p: stream.Part, - useMeasures: bool + useMeasures: bool, + lyricTargets: list[note.GeneralNote], ) -> tuple[int, bool]: ''' Parses all the tokens in a measure or part. @@ -265,11 +269,16 @@ def parseTokens( clefSet = False for t in mh.tokens: if isinstance(t, abcFormat.ABCMetadata): + if t.tag == 'w': + addABCLyrics(t.data, lyricTargets) + lyricTargets.clear() postTransposition, clefSet = metadataToM21Object( t, dst, postTransposition, clefSet, useMeasures ) elif isinstance(t, abcFormat.ABCNote): - parseABCNote(t, dst) + m21Note = parseABCNote(t, dst) + if m21Note is not None and not isinstance(m21Note, note.Rest) and not t.inGrace: + lyricTargets.append(m21Note) elif isinstance(t, abcFormat.ABCSlurStart): if typing.TYPE_CHECKING: assert t.slurObj is not None @@ -285,10 +294,36 @@ def parseTokens( dst.coreElementsChanged() return postTransposition, clefSet + +def addABCLyrics(lyricData: str, lyricTargets: list[note.GeneralNote]) -> None: + ''' + Attach simple ``w:`` ABC lyric syllables to their corresponding notes. + + AI-assisted implementation. + ''' + lyricTargetIndex = 0 + for lyricWord in lyricData.split(): + if lyricWord in ('*', '-'): + lyricTargetIndex += 1 + continue + + syllables = lyricWord.split('-') + for syllableIndex, syllable in enumerate(syllables): + if not syllable: + lyricTargetIndex += 1 + continue + if lyricTargetIndex >= len(lyricTargets): + return + prefix = '-' if syllableIndex else '' + suffix = '-' if syllableIndex < len(syllables) - 1 else '' + lyricTargets[lyricTargetIndex].addLyric(prefix + syllable + suffix) + lyricTargetIndex += 1 + + def parseABCNote( t: abcFormat.ABCNote, dst: stream.Measure|stream.Part, -) -> None: +) -> note.GeneralNote|None: ''' Parse an ABCNote object and add it to the destination stream. ''' @@ -307,7 +342,7 @@ def parseABCNote( if cs_name in ('NC', 'N.C.', 'No Chord', 'None'): cs = harmony.NoChord(cs_name) elif cs_name.startswith('>'): - return # fingering diagram? Appears in some pieces, ryans-Neumedia + return None # fingering diagram? Appears in some pieces, ryans-Neumedia else: cs = harmony.ChordSymbol(cs_name) dst.coreAppend(cs, setActiveSite=False) @@ -319,7 +354,7 @@ def parseABCNote( if isinstance(t, abcFormat.ABCChord): # Skip an empty chord if not t.subTokens: - return + return None # may have more than notes? pitchNameList: list[str] = [] @@ -345,6 +380,7 @@ def parseABCNote( if acc is not None: acc.displayStatus = accStatusList[pIndex] dst.coreAppend(c) + return c # ql += t.quarterLength else: @@ -388,6 +424,7 @@ def parseABCNote( n.articulations.append(m21ArticulationObj) dst.coreAppend(n, setActiveSite=False) + return n def metadataToM21Object( @@ -885,23 +922,28 @@ def testOpusImport(self): self.assertEqual(len(op), 8) def testLyrics(self): - # TODO(msc) -- test better - from music21 import abcFormat - from music21.abcFormat import testFiles - - tf = testFiles.sicutRosa af = abcFormat.ABCFile() - s = abcToStreamScore(af.readstr(tf)) + s = abcToStreamScore(af.readstr(''' +X:1 +T:Lyrics +M:4/4 +L:1/4 +K:C +C D E F|G A B c| +w:hel-lo world * one two three four +''')) assert s is not None - # s.show() - # self.assertEqual(len(s.parts), 3) - # self.assertEqual(len(s.parts[0].notesAndRests), 6) - # self.assertEqual(len(s.parts[1].notesAndRests), 20) - # self.assertEqual(len(s.parts[2].notesAndRests), 6) - # s.show() - # s.show('midi') + notes = s.parts[0].recurse().notes + self.assertEqual(notes[0].lyric, 'hel') + self.assertEqual(notes[0].lyrics[0].syllabic, 'begin') + self.assertEqual(notes[1].lyric, 'lo') + self.assertEqual(notes[1].lyrics[0].syllabic, 'end') + self.assertEqual(notes[2].lyric, 'world') + self.assertEqual(notes[3].lyrics, []) + self.assertEqual([m21Note.lyric for m21Note in notes[4:]], + ['one', 'two', 'three', 'four']) def testMultiWorkImported(self): From 9fd67a44179c547bd8ccd2f200729f7ec75baffe Mon Sep 17 00:00:00 2001 From: Emerson Date: Sun, 2 Aug 2026 14:42:05 -0400 Subject: [PATCH 040/312] Fix MetronomeMark objects silently dropped from Lilypond output The Lilypond translator's element dispatch never recognized MetronomeMark, so tempo markings were dropped entirely when writing to lily. Also fixes LyTempoEvent.stringOutput(), which ignored a paired stenoDuration when no tempoRange was given, and its docstring, which showed an invalid steno duration ('quarter' instead of '4'). Fixes #1852 This PR is AI-assisted (>10 lines). Co-Authored-By: Claude Sonnet 5 --- music21/lily/lilyObjects.py | 24 ++++++++++++--- music21/lily/translate.py | 59 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py index c9a4eeb24f..77eadfa0a3 100644 --- a/music21/lily/lilyObjects.py +++ b/music21/lily/lilyObjects.py @@ -867,23 +867,34 @@ class LyTempoEvent(LyObject): r''' tempo_event: "\tempo" steno_duration '=' tempo_range | "\tempo" scalar steno_duration '=' tempo_range + | "\tempo" steno_duration '=' scalar | "\tempo" scalar >>> lte = lily.lilyObjects.LyTempoEvent(scalar='40') >>> str(lte) '\\tempo 40' - More complex: + A steno_duration paired with a single bpm scalar (and no tempoRange) + is the common case for a music21 MetronomeMark, e.g. quarter = 87: - >>> steno = lily.lilyObjects.LyStenoDuration('quarter') + >>> steno = lily.lilyObjects.LyStenoDuration('4') + >>> lte = lily.lilyObjects.LyTempoEvent(stenoDuration=steno, scalar=87) + >>> str(lte) + '\\tempo 4 = 87' + + More complex, with a tempo range. Note that steno_duration takes a + Lilypond duration number such as '4' for a quarter note, not the + English name 'quarter': + + >>> steno = lily.lilyObjects.LyStenoDuration('4') >>> tempoRange = lily.lilyObjects.LyTempoRange(70, 100) >>> lte = lily.lilyObjects.LyTempoEvent(tempoRange=tempoRange, stenoDuration=steno) >>> str(lte) - '\\tempo quarter = 70~100 ' + '\\tempo 4 = 70~100 ' >>> lte.scalar = 85 >>> str(lte) - '\\tempo 85 quarter = 70~100 ' + '\\tempo 85 4 = 70~100 ' ''' def __init__(self, tempoRange=None, stenoDuration=None, scalar=None): @@ -906,6 +917,11 @@ def stringOutput(self): else: return ' '.join([base, self.stenoDuration.stringOutput(), '=', self.tempoRange.stringOutput()]) + elif self.stenoDuration is not None: + if self.scalar is None: # pragma: no cover + raise LilyObjectsException( + 'If tempoRange is not defined but stenoDuration is, need a scalar') + return ' '.join([base, self.stenoDuration.stringOutput(), '=', str(self.scalar)]) elif self.scalar is None: # pragma: no cover raise LilyObjectsException('If tempoRange is not defined then need scalar') diff --git a/music21/lily/translate.py b/music21/lily/translate.py index 2975dcb20d..9f307aaa4e 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -33,6 +33,7 @@ from music21 import key from music21 import note from music21 import stream +from music21 import tempo from music21 import variant from music21.lily import lilyObjects as lyo @@ -1096,6 +1097,11 @@ def appendM21ObjectToContext(self, thisObject): lyObject = self.lyEmbeddedScmFromTimeSignature(thisObject) currentMusicList.append(lyObject) lyObject.setParent(contextObject) + elif 'MetronomeMark' in c: + lyObject = self.lyEmbeddedScmFromMetronomeMark(thisObject) + if lyObject is not None: + currentMusicList.append(lyObject) + lyObject.setParent(contextObject) elif 'Variant' in c: self.appendContextFromVariant(thisObject, coloredVariants=self.coloredVariants) elif 'SystemLayout' in c: @@ -1680,6 +1686,38 @@ def lyEmbeddedScmFromTimeSignature(self, ts): lpEmbeddedScm.content = keyScheme return lpEmbeddedScm + def lyEmbeddedScmFromMetronomeMark(self, mm: tempo.MetronomeMark) -> lyo.LyEmbeddedScm|None: + # noinspection PyShadowingNames + r''' + convert a :class:`~music21.tempo.MetronomeMark` object + to a lilyObjects.LyEmbeddedScm object + + * New in v11: MetronomeMark objects are now written out when + converting a Stream to Lilypond; previously they were silently dropped. + + >>> mm = tempo.MetronomeMark(number=87, referent=note.Note(type='quarter')) + >>> conv = lily.translate.LilypondConverter() + >>> print(conv.lyEmbeddedScmFromMetronomeMark(mm)) + \tempo 4 = 87 + + A MetronomeMark without a number produces no output: + + >>> mm = tempo.MetronomeMark() + >>> mm.number is None + True + >>> conv.lyEmbeddedScmFromMetronomeMark(mm) is None + True + ''' + if mm.number is None: + return None + + multipliedDuration = self.lyMultipliedDurationFromDuration(mm.referent) + tempoEvent = lyo.LyTempoEvent(stenoDuration=multipliedDuration.stenoDur, scalar=mm.number) + + lpEmbeddedScm = lyo.LyEmbeddedScm() + lpEmbeddedScm.content = tempoEvent.stringOutput() + lpEmbeddedScm.newlineIndent + return lpEmbeddedScm + def setContextForTupletStart(self, inObj): r''' if the inObj has tuplets then we set a new context @@ -2596,6 +2634,27 @@ def testColors(self): "c' 4 " ) + def testMetronomeMark(self): + mm = tempo.MetronomeMark(number=87, referent=note.Note(type='quarter')) + lpEmbeddedScm = LilypondConverter().lyEmbeddedScmFromMetronomeMark(mm) + self.assertEqual(str(lpEmbeddedScm).strip(), r'\tempo 4 = 87') + + # a MetronomeMark with no number produces no output + textOnly = tempo.MetronomeMark() + self.assertIsNone(LilypondConverter().lyEmbeddedScmFromMetronomeMark(textOnly)) + + def testMetronomeMarkWrittenInStream(self): + # https://github.com/cuthbertLab/music21/issues/1852 + from music21 import key + from music21 import meter + keysig = key.Key('a-') + mm = tempo.MetronomeMark(number=87, referent=note.Note(type='quarter')) + timesig = meter.TimeSignature('3/4') + s = stream.Stream([keysig, mm, timesig, note.Note()]) + lpc = LilypondConverter() + lpc.loadObjectFromScore(s, makeNotation=False) + self.assertIn(r'\tempo 4 = 87', str(lpc.topLevelObject)) + class TestExternal(unittest.TestCase): show = True From 81a5d9611b887c37bbafee7c9fa60522a2d236cf Mon Sep 17 00:00:00 2001 From: ahmadalguydi Date: Sun, 2 Aug 2026 22:36:10 +0300 Subject: [PATCH 041/312] test: ignore lyric syllables after final note --- music21/abcFormat/translate.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/music21/abcFormat/translate.py b/music21/abcFormat/translate.py index 82a32878cb..69c943a272 100644 --- a/music21/abcFormat/translate.py +++ b/music21/abcFormat/translate.py @@ -945,6 +945,25 @@ def testLyrics(self): self.assertEqual([m21Note.lyric for m21Note in notes[4:]], ['one', 'two', 'three', 'four']) + def testLyricsIgnoreSyllablesAfterLastNote(self): + '''AI-assisted regression coverage for surplus lyric syllables.''' + from music21 import abcFormat + af = abcFormat.ABCFile() + s = abcToStreamScore(af.readstr(''' +X:1 +T:Lyrics with extra words +M:4/4 +L:1/4 +K:C +C D| +w:first second third +''')) + assert s is not None + + notes = s.parts[0].recurse().notes + self.assertEqual([m21Note.lyric for m21Note in notes], + ['first', 'second']) + def testMultiWorkImported(self): from music21 import corpus From 88b379a89d5704e944abbb7a6fc6f90e25d2d6cc Mon Sep 17 00:00:00 2001 From: ahmadalguydi Date: Wed, 5 Aug 2026 03:11:10 +0300 Subject: [PATCH 042/312] test: keep doctest version aligned --- music21/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/music21/_version.py b/music21/_version.py index 240ab9a1f5..d45d6c0cd6 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b8' +__version__ = '11.0.0b7' def get_version_tuple(vv): v = vv.split('.') From 82aa65fdc3b77efdb9dec01c46eb2170727977a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 05:05:49 +0000 Subject: [PATCH 043/312] build(deps): bump cryptography from 48.0.1 to 50.0.0 Bumps [cryptography](https://github.com/pyca/cryptography) from 48.0.1 to 50.0.0. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/48.0.1...50.0.0) --- updated-dependencies: - dependency-name: cryptography dependency-version: 50.0.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 72 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/uv.lock b/uv.lock index 10c8084df4..a6cc92c20a 100644 --- a/uv.lock +++ b/uv.lock @@ -603,46 +603,46 @@ wheels = [ [[package]] name = "cryptography" -version = "48.0.1" +version = "50.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/12/45/870e7f4bef50e5f53b9f51d4428aee5290eedf58ba443f16b1ebb7ab8e66/cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a", size = 832989, upload-time = "2026-06-09T22:32:31.8Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/85/6379d42181bfc713094f081360fc5784d6c816b599d45e7f082502d173ce/cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225", size = 4696243, upload-time = "2026-06-09T22:32:33.446Z" }, - { url = "https://files.pythonhosted.org/packages/9c/87/c85d147b53323c7eb4d850920c8901377323c2a0ff8d79c262d4fee89aa2/cryptography-48.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0d27a5696721ef7a672b8c810f6aded391058e0b9486e63e6d93baf765da691", size = 4713235, upload-time = "2026-06-09T22:31:40.141Z" }, - { url = "https://files.pythonhosted.org/packages/79/58/67cbf8cf1ee7c54b439ca07bbecf8362c07afc11a3724fea70f745784add/cryptography-48.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb86ce1af36fe65041b6db9a8bb064ee621a7e5fded0f80d475ec243477cd242", size = 4702323, upload-time = "2026-06-09T22:31:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/89/c6/24266ac10c47f6cd2a865f4446062b466da1d1f10b27189eac00e61bf0c9/cryptography-48.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b024e784ad6c077ee0147b35ea9cbfc1e34e1fd4c1dcca214c2794d73a12df08", size = 5300085, upload-time = "2026-06-09T22:31:58.703Z" }, - { url = "https://files.pythonhosted.org/packages/d2/bb/cc4b78784f97efc8c5874c2a9743708d172be6663024b34a0467885ae0c8/cryptography-48.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3752f2dbc8f07a30aad2932c986cea495b03bb554887828225da104f732852b6", size = 4746137, upload-time = "2026-06-09T22:31:31.01Z" }, - { url = "https://files.pythonhosted.org/packages/1f/52/0c44de3f5267f8fbe8e835138017522a333436166e406f0db9b9e6e3033f/cryptography-48.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:bd81490cd5801d755cf97bb68ac191f14b708470b1c7cf4580f669b9c9264cd8", size = 4333867, upload-time = "2026-06-09T22:32:28.096Z" }, - { url = "https://files.pythonhosted.org/packages/9a/2e/772d7adbfa931537bc401640b7cac9976bff689bda187833e5d63b428e49/cryptography-48.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:66fd0771e7b9c6dcd44cf1120690d2338d16d72795cf40cae2786a39eba65429", size = 4701805, upload-time = "2026-06-09T22:31:38.284Z" }, - { url = "https://files.pythonhosted.org/packages/f8/a3/b06844f303873493c963caf581c04df31c7035e0c1b0f02c4814d319ec80/cryptography-48.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:3fd2ca57062b241c856670b073487d2e86c4637937ca5601e48f97bf8e11fc8f", size = 5258461, upload-time = "2026-06-09T22:31:04.187Z" }, - { url = "https://files.pythonhosted.org/packages/9f/13/8b765e2e12b07c74941caadb9d1c8fdc006c4dfbf2b8f2d610519758954d/cryptography-48.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:0ee6ea481db1ab889cba043ec1eda17bb9c1ea79db6722f779c3667f9f70322f", size = 4745488, upload-time = "2026-06-09T22:32:30.07Z" }, - { url = "https://files.pythonhosted.org/packages/2e/aa/48972bce55049b32a94f4907eda4d75fa385aad8a39506cc2fc72196ecf0/cryptography-48.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2ceef93cb096aa3c4cc4b5c94ca6131f9196d28c64d6111533402a9b2054d41", size = 4830256, upload-time = "2026-06-09T22:31:43.868Z" }, - { url = "https://files.pythonhosted.org/packages/47/a2/e5079a032fb85cf6005046ca92bbd78b0c82dad2b5751ab8c311659da06f/cryptography-48.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bd3f92d76217892b15df84ca256c2c113d386fdda7a7d8691aeeced976507c6", size = 4979117, upload-time = "2026-06-09T22:31:05.845Z" }, - { url = "https://files.pythonhosted.org/packages/8a/13/6476736484b94041110c8340a3eb63962fea4975baea8cb4a512adb44d4d/cryptography-48.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d5d30989c6917b478b5817902e85fddaea2261efa8648383d965381ccb9e1ac4", size = 4689201, upload-time = "2026-06-09T22:31:09.745Z" }, - { url = "https://files.pythonhosted.org/packages/79/62/65a87f34d2a431546e2509b85d55e8c90df86d668f6731da64d538512ac2/cryptography-48.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:df637c05205ea7c1d7fbcbe54bbfea648a52951155f997af13d895d0ecc96991", size = 4702822, upload-time = "2026-06-09T22:32:24.409Z" }, - { url = "https://files.pythonhosted.org/packages/7f/59/810b5204b0a9b10f4b6bc06bd551a8b609803cd931806bc3b71884b225e5/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:869c3b8a53bfe27147832df48b32adadf558249d50e76cb3769d40e986b13265", size = 4694875, upload-time = "2026-06-09T22:32:08.737Z" }, - { url = "https://files.pythonhosted.org/packages/24/dc/d8ca05ffea724eec6d232ea6f18e74c269eb6bdfdcc9bfba689790d1325f/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:e361afba8918070d376df76f408a4f67fec0ee9cff81a99e48fe9a233ef59e17", size = 5290385, upload-time = "2026-06-09T22:31:15.212Z" }, - { url = "https://files.pythonhosted.org/packages/03/8c/3be6cb4da181f5bb6c19cf560c2359d60644a6b5fc5b57854e528f47b296/cryptography-48.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:d069066deead00ac7f090be101be875a06855908f7ec004c27b8fefb4acfb411", size = 4737082, upload-time = "2026-06-09T22:32:22.66Z" }, - { url = "https://files.pythonhosted.org/packages/aa/f6/d5f60a5a1434dbfd949e227fd0065d194c7e6b6ac526b17f5c06152b8231/cryptography-48.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:09f73a725d582cef64b91281a322cd798d14a33b2b6f2b7ad9531dc336d84c02", size = 4325328, upload-time = "2026-06-09T22:32:10.777Z" }, - { url = "https://files.pythonhosted.org/packages/17/b7/ba75dd947a14b6ad907b01ae8f6b5b348cdd1b48142f0063dee9e20c1d9d/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:15254441469dd6bf027039453288e2072124f8b6603563f5d759e1c9b69273fa", size = 4694530, upload-time = "2026-06-09T22:31:53.105Z" }, - { url = "https://files.pythonhosted.org/packages/62/29/50d6b9e8aff12d8b67afaeb3569335e32dc83a5723e3bbded24fdac9f809/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:8ace4507d1e6533c125f4fac754f8bb8b6a74c08e92179dabd7e16571a3efbf3", size = 5245046, upload-time = "2026-06-09T22:31:25.774Z" }, - { url = "https://files.pythonhosted.org/packages/9f/04/618f4115cfc0add0838c82507aa18a346089428da8653ad38b3ff36f5cb3/cryptography-48.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:b4e391975f038e66432328639620a4aff2d307513b004f1ca06d6225bced815c", size = 4736660, upload-time = "2026-06-09T22:32:12.676Z" }, - { url = "https://files.pythonhosted.org/packages/24/9c/06e062462a0de28a3b3911322eded4c16deb9f441b1b7575d3dc59488ab5/cryptography-48.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42fcd8e26fe555d9b3577a135f5091fefa0aa4e99129c23fb56787a1bd4ada72", size = 4822229, upload-time = "2026-06-09T22:31:17.062Z" }, - { url = "https://files.pythonhosted.org/packages/f4/be/0561971eaaee4b8a0e7d5113c536921063ab91aaf23278ac374eaf881e11/cryptography-48.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1400da5e32a43253392277eac7490a60e497d810a63dd5608d71bbd7af507c9", size = 4966364, upload-time = "2026-06-09T22:31:32.842Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d9/45f309a7e4e5f3f8f121d6d3be9e94024a7726ec598d6e08ae04edb2f04d/cryptography-48.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48fe40804d4caa2288f24e70ca8c64c42dd826da0ad7e4f1b41b2128d679e6c8", size = 4690196, upload-time = "2026-06-09T22:31:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9f/a1bc8bcc798811b8527eb374bbccf30a3f3e806829d967118222bf1125eb/cryptography-48.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86be3b1b0b6bf09482fb50a979c508d2950ed95f5621ec77f4e385962006b83a", size = 4696782, upload-time = "2026-06-09T22:31:45.615Z" }, - { url = "https://files.pythonhosted.org/packages/66/c2/81a4fb4e4373c500bb526bc337ac5719dd31dd15b970b84a238168c6aa08/cryptography-48.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab0a343c807bbcd90c971cd1ecf072937cd01847a9e002bef88fb47ac6be577", size = 4696618, upload-time = "2026-06-09T22:31:11.564Z" }, - { url = "https://files.pythonhosted.org/packages/e5/0b/aa68b221dde92d09cb29a024ede17550ee21e77a404e59fc093c82bb51e1/cryptography-48.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9621de99d2da096006b629979efd8ae7eb2d8b822488d0c89ee4000c306c59b1", size = 5289970, upload-time = "2026-06-09T22:31:20.368Z" }, - { url = "https://files.pythonhosted.org/packages/78/13/fba657f958d2af66ea959a4ba01212632089249d34af1ae48054136344d7/cryptography-48.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:88c852a0ae366e262e5a1744b685e6a433dc8788dd2a277e418bf4904203609d", size = 4731873, upload-time = "2026-06-09T22:31:22.253Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4c/9a964756d24a26b3e34dfcb16f961b89838786e6700b635b0d1e3adff4b6/cryptography-48.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:43c5835e2cb98c8733d86f57d6fc879b613f5c3478607281c3e36daffc6dd8a6", size = 4330804, upload-time = "2026-06-09T22:31:36.56Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0f/a10f3a6eb12950a10e3a874070283aa2dd5875b2bfd15fad8a3e17b3f13e/cryptography-48.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46", size = 4696217, upload-time = "2026-06-09T22:31:13.351Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6f/5cd12f951165ea73ef85266775d97e4c763b2474ccfd816dd69d3a18d6f8/cryptography-48.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:b7a2d1a937a738a881737cec135a38bb61470589b17515b9f73f571d0ae10401", size = 5245252, upload-time = "2026-06-09T22:32:02.193Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/8aaa12e4516ec4464033ab79b6f3b592bd5a92102467c4ace8a0d970203f/cryptography-48.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b74ca3b8e5ecdd833bf6a002ca41b4793bb27fb8f1c06ffaf2643c9e9140e31b", size = 4731388, upload-time = "2026-06-09T22:32:04.019Z" }, - { url = "https://files.pythonhosted.org/packages/1b/24/50027ea4dca85ec1f40688f3c24fb32ccacd520583c9592c3cc95628e6fb/cryptography-48.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c37f2461406063b417837f5f3daab668652acd82423efcd7f0a9f04be972de1", size = 4824186, upload-time = "2026-06-09T22:32:18.707Z" }, - { url = "https://files.pythonhosted.org/packages/52/41/04cb5eb17085ade6f50cc611fb657df6a0f5885350de8764ece89c050197/cryptography-48.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86fe77abb1bd87afb251d4d02ada7ecf53a32cee9b67d976abb2e45a13297475", size = 4964539, upload-time = "2026-06-09T22:31:18.793Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, + { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, + { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, + { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, + { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, + { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, + { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, + { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, + { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, + { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, + { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, + { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, + { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, + { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, + { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, + { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, + { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, + { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, + { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, ] [[package]] From 93d38423456b94513c91289041877feeb53e8e49 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 10:20:17 -1000 Subject: [PATCH 044/312] fix: honor displayType 'always' when nothing precedes the pitch Pitch.updateAccidentalDisplay checked cautionaryAll and a displayType of 'always'/'even-tied' only after the `if not pitchPastAll:` early return, so the first pitch of a part -- the one case with nothing in the past -- never reached it and fell through to the key-signature branch instead. An explicitly requested accidental was silently switched off whenever the key signature made it redundant, and since MusicXML export gates on displayStatus, it was dropped from the exported file entirely. Hoist the block above the early return. When pitchPastAll is non-empty this is a no-op: the only thing it now precedes is the conflict loop, whose two outcomes (conflicting alteration -> True, same name -> break into this block -> True) both already ended in True. Port of the same fix in music21j (cuthbertLab/music21j#325). Version bumped to invalidate cached pickles: the musedata parser sets displayType='always', so cached scores can hold the old display statuses. AI-assisted (Claude) --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/pitch.py | 34 ++++++++++++++++++-------- music21/test/test_pitch.py | 49 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 12 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index d45d6c0cd6..240ab9a1f5 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b7' +__version__ = '11.0.0b8' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 85ae8bbe72..3a3c407d2c 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b7' +'11.0.0b8' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/pitch.py b/music21/pitch.py index 8d909b8d41..2830c95297 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -4984,7 +4984,20 @@ def updateAccidentalDisplay( >>> a4.accidental is None True + `cautionaryAll` and a `displayType` of 'always' or 'even-tied' are honored + even when nothing precedes the pitch, and regardless of the key signature: + + >>> fSharp = pitch.Pitch('F#4') + >>> fSharp.accidental.displayType = 'always' + >>> fSharp.updateAccidentalDisplay(alteredPitches=key.Key('G').alteredPitches) + >>> fSharp.accidental.displayStatus + True + v8 -- made keyword-only and added `otherSimultaneousPitches`. + + * Changed in v11: `cautionaryAll` and `displayType` of 'always'/'even-tied' + are honored for the first pitch of a part (previously an empty `pitchPast` + fell through to key-signature logic and could turn the accidental off). ''' # N.B. -- this is a very complex method # do not alter it without significant testing. @@ -5048,6 +5061,17 @@ def set_displayStatus(newDisplayStatus: bool) -> None: set_displayStatus(True) return + # here tied and always are treated the same; we assume that + # making ties sets the displayStatus, and thus we would not be + # overriding that display status here + if (cautionaryAll is True + or (acc is not None + and acc.displayType in ('even-tied', 'always'))): + # show all accidentals, no matter what is in the past + # or in the key signature + set_displayStatus(True) + return # do not search past + # no pitches in the past list if not pitchPastAll: # if we have no past, we show the accidental if this pitch name @@ -5089,16 +5113,6 @@ def set_displayStatus(newDisplayStatus: bool) -> None: break # nope, no conflicting accidentals at this name and octave in the past - # here tied and always are treated the same; we assume that - # making ties sets the displayStatus, and thus we would not be - # overriding that display status here - if (cautionaryAll is True - or (acc is not None - and acc.displayType in ('even-tied', 'always'))): - # show all accidentals, even if past encountered - set_displayStatus(True) - return # do not search past - # store if a match was found and display set from past pitches setFromPitchPast = False diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 80cfedfe0c..309fe3f3cb 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -396,6 +396,55 @@ def testOverrideDisplayStatus(self): n.pitch.updateAccidentalDisplay(overrideStatus=True, alteredPitches=k.alteredPitches) self.assertIs(n.pitch.accidental.displayStatus, False) + def testDisplayTypeAlwaysWithNothingInPast(self): + ''' + `displayType='always'` and `cautionaryAll` win over the key signature + even for the first pitch of a part, where nothing precedes it. + + AI-assisted (Claude). + ''' + p = Pitch('An2') + p.accidental.displayType = 'always' + p.updateAccidentalDisplay() + self.assertIs(p.accidental.displayStatus, True) + + # a sharp already implied by the key signature still shows + p = Pitch('F#4') + p.accidental.displayType = 'always' + p.updateAccidentalDisplay(alteredPitches=key.Key('G').alteredPitches) + self.assertIs(p.accidental.displayStatus, True) + + # cautionaryAll creates the natural that was not there + p = Pitch('B4') + p.updateAccidentalDisplay(cautionaryAll=True) + self.assertEqual(p.accidental.name, 'natural') + self.assertIs(p.accidental.displayStatus, True) + + # 'even-tied' behaves the same as 'always' here + p = Pitch('E-3') + p.accidental.displayType = 'even-tied' + p.updateAccidentalDisplay(alteredPitches=key.Key('E-').alteredPitches) + self.assertIs(p.accidental.displayStatus, True) + + # in a stream, every note of the part shows, not just the ones after the first + s = converter.parse('tinyNotation: AAn2 Fn') + for n in s.recurse().notes: + n.pitch.accidental.displayType = 'always' + s.makeAccidentals(inPlace=True) + self.assertEqual([n.pitch.accidental.displayStatus for n in s.recurse().notes], + [True, True]) + + # regression guard: a normal displayType is untouched by the above + p = Pitch('F#4') + p.updateAccidentalDisplay(alteredPitches=key.Key('G').alteredPitches) + self.assertIs(p.accidental.displayStatus, False) + + # regression guard: with a non-empty past, repeated pitches are unchanged + past = [Pitch('c#4')] + p = Pitch('c#4') + p.updateAccidentalDisplay(pitchPast=past) + self.assertIs(p.accidental.displayStatus, False) + def testImplicitToExplicitNatural(self): p = converter.parse('tinyNotation: 2/4 f4 fn4') last_note = p.recurse().notes.last() From 58ab28e2d5fd75981dd4c593765848a0a0da9b7c Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 10:29:45 -1000 Subject: [PATCH 045/312] docs: drop the doctest and version marker from updateAccidentalDisplay Doctests demonstrate future usage, not bug fixes -- regression coverage belongs in test_pitch.py, where testDisplayTypeAlwaysWithNothingInPast already covers this. A simple bug fix does not warrant a "Changed in vXX" marker either. Also: the previous commit's stated reason for the version bump was wrong. It claimed cached musedata pickles could hold stale display statuses, but musedata/__init__.py sets displayStatus=True alongside displayType='always', so updateAccidentalDisplay returns at the existing displayStatus check well before the hoisted block, and makeAccidentals does not pass overrideStatus. No parsed output changes and no cache is invalidated. The bump to 11.0.0b8 stands only on the ordinary bug-fix rule. AI-assisted (Claude) --- music21/pitch.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index 2830c95297..40680a0923 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -4984,20 +4984,7 @@ def updateAccidentalDisplay( >>> a4.accidental is None True - `cautionaryAll` and a `displayType` of 'always' or 'even-tied' are honored - even when nothing precedes the pitch, and regardless of the key signature: - - >>> fSharp = pitch.Pitch('F#4') - >>> fSharp.accidental.displayType = 'always' - >>> fSharp.updateAccidentalDisplay(alteredPitches=key.Key('G').alteredPitches) - >>> fSharp.accidental.displayStatus - True - v8 -- made keyword-only and added `otherSimultaneousPitches`. - - * Changed in v11: `cautionaryAll` and `displayType` of 'always'/'even-tied' - are honored for the first pitch of a part (previously an empty `pitchPast` - fell through to key-signature logic and could turn the accidental off). ''' # N.B. -- this is a very complex method # do not alter it without significant testing. From 0cf34a98663cc09cfdef4e8d283aa527f56820de Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 15:44:00 -1000 Subject: [PATCH 046/312] lily: add typing throughout; fix invalid LilyPond output Annotate every __init__, method and non-obvious local in lilyObjects.py and translate.py. stringOutput() is now `-> str` on all ~90 grammar classes. Eight subclasses returned None for empty contents, which is unrepresentable in an override and was also a latent crash: LyObject.__str__ does stringOutput().replace(...), so str(LyBookBody()) raised AttributeError. They return '' now. In translate.py the .classes string dispatch in loadFromMusic21Object and appendM21ObjectToContext became isinstance checks -- that is what lets mypy verify the dispatch -- and self.context.contents access moved behind contextContents()/setContextContents(), since context is legitimately polymorphic (LyLilypondTop, LyMusicList, LyCompositeMusic). LilyPond correctness, each checked against an installed LilyPond 2.24.4 rather than from memory. All four produced silently wrong or unparseable output: - barlineDict: 'dotted' was ":", 'dashed' was "dashed", 'heavy-heavy' was ".|.", and the repeats were "|:"/":|". None of those are defined in 2.24's bar-line.scm, and LilyPond draws an unknown bar name as nothing at all with no warning. Now ";", "!", "..", ".|:", ":|.". 'short' was commented out entirely, so a `short` barline raised KeyError; mapped to ",". All 11 entries of bar.barTypeList now round-trip. - \markuplines was renamed \markuplist in 2.16; the old name is a hard parse error today. - LyTempoRange joined a range with "~", a tie: `\tempo 4 = 70~100` fails with "not a duration" plus an unterminated-tie warning. Uses "-". - LyModeChangingHead emitted \notes for mode='note', which has never existed. Always \notemode for that mode; the other four shorthands are valid. Not changed, having verified they still work in 2.24: the pre-2.18 (parser location) music-function signatures in fictaDef/colorDef, and \times (deprecated for \tuplet but still valid). \[ \] and \~ are faithful to the grammar this file follows -- ligature brackets and the pes-or-flexa event -- so those only gained comments. Three bugs surfaced by the annotations: - LyOutputDefBody.stringOutput had an inverted guard: it raised when outputDefBody was set and dereferenced None when it was not. - LyMarkupBracedListBody.stringOutput called super().__init__(), resetting the object mid-render. - LilypondConverter.__init__ set self.LILYEXEC = None after setupTools() had already populated it. No version bump: lily is output-only, so no parse caches are invalidated. AI-assisted (Claude) --- music21/lily/lilyObjects.py | 748 ++++++++++++++++++++++-------------- music21/lily/translate.py | 471 ++++++++++++----------- 2 files changed, 718 insertions(+), 501 deletions(-) diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py index c9a4eeb24f..d0ba53ac26 100644 --- a/music21/lily/lilyObjects.py +++ b/music21/lily/lilyObjects.py @@ -20,11 +20,15 @@ import typing as t import unittest +import weakref from music21 import common from music21 import exceptions21 from music21 import prebase +if t.TYPE_CHECKING: + from music21 import base + class LilyObjectsException(exceptions21.Music21Exception): pass @@ -38,24 +42,26 @@ class LyObject(prebase.ProtoM21Object): >>> lyo.stringOutput() '' + * Changed in v11: `stringOutput()` always returns a `str`; the subclasses that + returned None for empty contents now return `''`. ''' - supportedClasses: list[object] = [] # ordered list of classes to support - m21toLy: dict[str, dict] = {} + supportedClasses: list[str] = [] # ordered list of classes to support + m21toLy: dict[str, dict[str, str]] = {} defaultAttributes: dict[str, t.Any] = {} backslash = '\\' - def __init__(self): + def __init__(self) -> None: # self.context = context - self.lilyAttributes = {} - self._parent = None + self.lilyAttributes: dict[str, t.Any] = {} + self._parent: weakref.ReferenceType[LyObject]|None = None self.thisIndent = 0 - self.markupTop = None - self.lyricMarkupOrIdentifier = None - self.markupListOrIdentifier = None - self.markupTopOrIdentifier = None + self.markupTop: LyObject|str|None = None + self.lyricMarkupOrIdentifier: LyObject|str|None = None + self.markupListOrIdentifier: LyObject|str|None = None + self.markupTopOrIdentifier: LyObject|str|None = None # self.setLilyAttributes(inObject, context, **keywords) - def __setattr__(self, name, value): + def __setattr__(self, name: str, value: t.Any) -> None: if isinstance(value, LyObject): value.setParent(self) elif common.isIterable(value): @@ -66,15 +72,15 @@ def __setattr__(self, name, value): object.__setattr__(self, name, value) - def getParent(self): - if self._parent is not None: - actualParent = common.unwrapWeakref(self._parent) - return actualParent + def getParent(self) -> LyObject|None: + if self._parent is None: + return None + return self._parent() - def setParent(self, parentObject): - self._parent = common.wrapWeakref(parentObject) + def setParent(self, parentObject: LyObject) -> None: + self._parent = weakref.ref(parentObject) - def ancestorList(self): + def ancestorList(self) -> list[LyObject]: r''' returns a list of all unwrapped parent objects for the current object ''' @@ -85,7 +91,11 @@ def ancestorList(self): currentParent = currentParent.getParent() return ancestors - def getAncestorByClass(self, classObj, getAncestorNumber=1): + def getAncestorByClass( + self, + classObj: type[LyObject], + getAncestorNumber: int = 1, + ) -> LyObject|None: currentIter = 1 for a in self.ancestorList(): if isinstance(a, classObj): @@ -96,7 +106,7 @@ def getAncestorByClass(self, classObj, getAncestorNumber=1): return None @property - def newlineIndent(self): + def newlineIndent(self) -> str: # totalIndents = self.thisIndent ancestors = self.ancestorList() # for ancestor in ancestors: @@ -105,7 +115,7 @@ def newlineIndent(self): indentSpaces = ' ' * totalIndents return '\n' + indentSpaces - def setAttributes(self, m21Object): + def setAttributes(self, m21Object: base.Music21Object) -> dict[str, t.Any]: r''' Returns a dictionary and sets self.lilyAttributes to that dictionary, for a m21Object of class classLookup using the mapping of self.m21toLy[classLookup] @@ -135,21 +145,19 @@ def setAttributes(self, m21Object): >>> lilyAttributes is lm.lilyAttributes True ''' - attrs = None - foundClass = False for tryClass in self.supportedClasses: if tryClass in m21Object.classes or tryClass == '*': - attrs = self.setAttributesFromClassObject(tryClass, m21Object) - foundClass = True - break + return self.setAttributesFromClassObject(tryClass, m21Object) - if not foundClass: # pragma: no cover - raise LilyObjectsException( - 'Could not support setting attributes from ' - f'{m21Object}: supported classes: {self.supportedClasses}') - return attrs + raise LilyObjectsException( # pragma: no cover + 'Could not support setting attributes from ' + f'{m21Object}: supported classes: {self.supportedClasses}') - def setAttributesFromClassObject(self, classLookup, m21Object): + def setAttributesFromClassObject( + self, + classLookup: str, + m21Object: base.Music21Object, + ) -> dict[str, t.Any]: r''' Returns a dictionary and sets self.lilyAttributes to that dictionary, for a m21Object of class classLookup using the mapping of self.m21toLy[classLookup] @@ -209,21 +217,24 @@ def _reprInternal(self) -> str: msg = msg[:10] + '...' return msg - def __str__(self): + def __str__(self) -> str: so = self.stringOutput() so = so.replace('\n\n', '\n') return so - def stringOutput(self): + def stringOutput(self) -> str: return '' - def getFirstNonNoneAttribute(self, attributeList): + def getFirstNonNoneAttribute(self, attributeList: t.Iterable[str]) -> t.Any: for a in attributeList: if getattr(self, a) is not None: return getattr(self, a) return None - def newlineSeparateStringOutputIfNotNone(self, contents): + def newlineSeparateStringOutputIfNotNone( + self, + contents: t.Iterable[LyObject|str|None], + ) -> str: c = '' for n in contents: if n is None: @@ -232,7 +243,7 @@ def newlineSeparateStringOutputIfNotNone(self, contents): return c - def encloseCurly(self, arg): + def encloseCurly(self, arg: t.Sequence[str]|LyObject|str|None) -> str: if isinstance(arg, list): strArg = self.newlineIndent.join(arg) return ''.join([' { ', self.newlineIndent, strArg, self.newlineIndent, @@ -243,7 +254,7 @@ def encloseCurly(self, arg): else: return ' { } ' - def quoteString(self, stringIn): + def quoteString(self, stringIn: str) -> str: r''' returns a string that is quoted with internal quotation marks backslash'd out @@ -272,7 +283,7 @@ class LyMock(LyObject): ''' supportedClasses = ['Mock', 'Mocker'] m21toLy = {'Mock': {'mockAttribute': 'mock-attribute', - 'mockAttribute2': 'mock-attribute-2', + 'mockAttribute2': 'mock-attribute-2', }, 'Mocker': {'mockerAttribute': 'mock-attribute', 'greg': 'mock-attribute-2', }, @@ -299,13 +310,13 @@ class LyLilypondTop(LyObject): ''' canContain = [None, 'TopLevelExpression', 'Assignment'] - def __init__(self, contents=None): + def __init__(self, contents: list[LyObject|str]|None = None) -> None: if contents is None: contents = [] super().__init__() self.contents = contents - def stringOutput(self): + def stringOutput(self) -> str: return self.newlineSeparateStringOutputIfNotNone(self.contents) @@ -328,10 +339,16 @@ class LyTopLevelExpression(LyObject): '\\book { } ' ''' - def __init__(self, lilypondHeader=None, bookBlock=None, - bookPartBlock=None, scoreBlock=None, compositeMusic=None, - fullMarkup=None, fullMarkupList=None, outputDef=None - ): + def __init__(self, + lilypondHeader: LyLilypondHeader|None = None, + bookBlock: LyBookBlock|None = None, + bookPartBlock: LyBookpartBlock|None = None, + scoreBlock: LyScoreBlock|None = None, + compositeMusic: LyCompositeMusic|None = None, + fullMarkup: LyFullMarkup|None = None, + fullMarkupList: LyFullMarkupList|None = None, + outputDef: LyOutputDef|None = None, + ) -> None: super().__init__() self.lilypondHeader = lilypondHeader self.bookBlock = bookBlock @@ -342,7 +359,7 @@ def __init__(self, lilypondHeader=None, bookBlock=None, self.fullMarkupList = fullMarkupList self.outputDef = outputDef - def stringOutput(self): + def stringOutput(self) -> str: outputObject = self.getFirstNonNoneAttribute([ 'lilypondHeader', 'bookBlock', 'bookPartBlock', 'scoreBlock', 'compositeMusic', 'fullMarkup', 'fullMarkupList', 'outputDef']) @@ -360,11 +377,11 @@ class LyLilypondHeader(LyObject): '\\header { } ' ''' - def __init__(self, lilypondHeaderBody=None): + def __init__(self, lilypondHeaderBody: LyLilypondHeaderBody|None = None) -> None: super().__init__() self.lilypondHeaderBody = lilypondHeaderBody - def stringOutput(self): + def stringOutput(self) -> str: return self.backslash + 'header' + self.encloseCurly(self.lilypondHeaderBody) @@ -385,22 +402,22 @@ class LyEmbeddedScm(LyObject): '##t' ''' - def __init__(self, content=None): + def __init__(self, content: str = '') -> None: super().__init__() self.content = content - def stringOutput(self): + def stringOutput(self) -> str: return self.content class LyLilypondHeaderBody(LyObject): - def __init__(self, assignments=None): + def __init__(self, assignments: list[LyAssignment]|None = None) -> None: if assignments is None: assignments = [] super().__init__() self.assignments = assignments - def stringOutput(self): + def stringOutput(self) -> str: return self.newlineSeparateStringOutputIfNotNone(self.assignments) @@ -411,12 +428,12 @@ class LyAssignmentId(LyObject): 'title' ''' - def __init__(self, content=None, isLyricString=False): + def __init__(self, content: str = '', isLyricString: bool = False) -> None: super().__init__() self.content = content self.isLyricString = isLyricString - def stringOutput(self): + def stringOutput(self) -> str: return self.content @@ -441,15 +458,19 @@ class LyAssignment(LyObject): but that's overkill for a lot of things. ''' - def __init__(self, assignmentId=None, identifierInit=None, - propertyPath=None, embeddedScm=None): + def __init__(self, + assignmentId: LyAssignmentId|str|None = None, + identifierInit: LyIdentifierInit|None = None, + propertyPath: LyPropertyPath|None = None, + embeddedScm: LyEmbeddedScm|None = None, + ) -> None: super().__init__() self.assignmentId = assignmentId self.identifierInit = identifierInit self.propertyPath = propertyPath self.embeddedScm = embeddedScm - def stringOutput(self): + def stringOutput(self) -> str: if self.embeddedScm is not None: return self.embeddedScm.stringOutput() elif self.propertyPath is not None: @@ -475,14 +496,21 @@ class LyIdentifierInit(LyObject): ''' def __init__(self, - scoreBlock=None, - bookBlock=None, - bookPartBlock=None, - outputDef=None, - contextDefSpecBlock=None, - music=None, postEvent=None, numberExpression=None, - string=None, embeddedScm=None, fullMarkup=None, fullMarkupList=None, - digit=None, contextModification=None): + scoreBlock: LyScoreBlock|None = None, + bookBlock: LyBookBlock|None = None, + bookPartBlock: LyBookpartBlock|None = None, + outputDef: LyOutputDef|None = None, + contextDefSpecBlock: LyContextDefSpecBlock|None = None, + music: LyMusic|None = None, + postEvent: LyPostEvent|None = None, + numberExpression: LyNumberExpression|None = None, + string: str|None = None, + embeddedScm: LyEmbeddedScm|None = None, + fullMarkup: LyFullMarkup|None = None, + fullMarkupList: LyFullMarkupList|None = None, + digit: int|None = None, + contextModification: LyContextModification|None = None, + ) -> None: super().__init__() self.scoreBlock = scoreBlock self.bookBlock = bookBlock @@ -499,7 +527,7 @@ def __init__(self, self.digit = digit self.contextModification = contextModification - def stringOutput(self): + def stringOutput(self) -> str: outputObject = self.getFirstNonNoneAttribute([ 'scoreBlock', 'bookBlock', 'bookPartBlock', 'outputDef', 'contextDefSpecBlock', 'music', 'postEvent', 'numberExpression', @@ -510,18 +538,18 @@ def stringOutput(self): if outputObject is self.digit: # better test for digit return str(outputObject) - elif outputObject is self.string: - return self.quoteString(outputObject) + elif self.string is not None and outputObject is self.string: + return self.quoteString(self.string) else: return outputObject.stringOutput() class LyContextDefSpecBlock(LyObject): - def __init__(self, contextDefSpecBody=None): + def __init__(self, contextDefSpecBody: LyContextDefSpecBody|None = None) -> None: super().__init__() self.contextDefSpecBody = contextDefSpecBody - def stringOutput(self): + def stringOutput(self) -> str: return self.backslash + 'context ' + self.encloseCurly(self.contextDefSpecBody) @@ -545,8 +573,13 @@ class LyContextDefSpecBody(LyObject): 'body \\grobdescriptions #t' ''' - def __init__(self, contextDefIdentifier=None, contextDefSpecBody=None, - embeddedScm=None, contextMod=None, contextModification=None): + def __init__(self, + contextDefIdentifier: str|None = None, + contextDefSpecBody: str|None = None, + embeddedScm: LyEmbeddedScm|None = None, + contextMod: LyContextMod|None = None, + contextModification: LyContextModification|None = None, + ) -> None: super().__init__() self.contextDefIdentifier = contextDefIdentifier self.contextDefSpecBody = contextDefSpecBody @@ -554,7 +587,7 @@ def __init__(self, contextDefIdentifier=None, contextDefSpecBody=None, self.contextMod = contextMod self.contextModification = contextModification - def stringOutput(self): + def stringOutput(self) -> str: if self.contextDefIdentifier is not None: return self.contextDefIdentifier elif self.embeddedScm is not None: @@ -576,15 +609,15 @@ def stringOutput(self): else: return self.contextModification.stringOutput() else: - return None + return '' class LyBookBlock(LyObject): - def __init__(self, bookBody=None): + def __init__(self, bookBody: LyBookBody|None = None) -> None: super().__init__() self.bookBody = bookBody - def stringOutput(self): + def stringOutput(self) -> str: return self.backslash + 'book' + ' ' + self.encloseCurly(self.bookBody) @@ -606,8 +639,8 @@ class LyBookBody(LyObject): 'bookId' >>> lyBookBody = lily.lilyObjects.LyBookBody() - >>> lyBookBody.stringOutput() is None - True + >>> lyBookBody.stringOutput() + '' >>> lyBookBody = lily.lilyObjects.LyBookBody(contents=['a', 'b', 'c']) >>> print(lyBookBody.stringOutput()) @@ -616,18 +649,21 @@ class LyBookBody(LyObject): c ''' - def __init__(self, contents=None, bookIdentifier=None): + def __init__(self, + contents: list[LyObject|str]|None = None, + bookIdentifier: str|None = None, + ) -> None: if contents is None: contents = [] super().__init__() self.contents = contents self.bookIdentifier = bookIdentifier - def stringOutput(self): + def stringOutput(self) -> str: if self.bookIdentifier is not None: return self.bookIdentifier elif not self.contents: - return None + return '' else: return self.newlineSeparateStringOutputIfNotNone(self.contents) @@ -639,11 +675,11 @@ class LyBookpartBlock(LyObject): '\\bookpart { \n\n } \n' ''' - def __init__(self, bookpartBody=None): + def __init__(self, bookpartBody: LyBookpartBody|None = None) -> None: super().__init__() self.bookpartBody = bookpartBody - def stringOutput(self): + def stringOutput(self) -> str: if self.bookpartBody is None: return self.backslash + 'bookpart ' + self.encloseCurly('') else: @@ -668,8 +704,8 @@ class LyBookpartBody(LyObject): 'bookId' >>> lyBookpartBody = lily.lilyObjects.LyBookpartBody() - >>> lyBookpartBody.stringOutput() is None - True + >>> lyBookpartBody.stringOutput() + '' >>> lyBookpartBody = lily.lilyObjects.LyBookpartBody(contents=['a', 'b', 'c']) >>> print(lyBookpartBody.stringOutput()) @@ -678,18 +714,21 @@ class LyBookpartBody(LyObject): c ''' - def __init__(self, contents=None, bookIdentifier=None): + def __init__(self, + contents: list[LyObject|str]|None = None, + bookIdentifier: str|None = None, + ) -> None: if contents is None: contents = [] super().__init__() self.contents = contents self.bookIdentifier = bookIdentifier - def stringOutput(self): + def stringOutput(self) -> str: if self.bookIdentifier is not None: return self.bookIdentifier elif not self.contents: - return None + return '' else: return self.newlineSeparateStringOutputIfNotNone(self.contents) @@ -705,11 +744,11 @@ class LyScoreBlock(LyObject): \score { hello } ''' - def __init__(self, scoreBody=None): + def __init__(self, scoreBody: LyScoreBody|str|None = None) -> None: super().__init__() self.scoreBody = scoreBody - def stringOutput(self): + def stringOutput(self) -> str: if self.scoreBody is None: raise LilyObjectsException('scoreBody object cannot be empty!') # pragma: no cover @@ -729,8 +768,14 @@ class LyScoreBody(LyObject): 'score' ''' - def __init__(self, music=None, scoreIdentifier=None, scoreBody=None, lilypondHeader=None, - outputDef=None, error=None): + def __init__(self, + music: LyMusic|None = None, + scoreIdentifier: str|None = None, + scoreBody: LyScoreBody|None = None, + lilypondHeader: LyLilypondHeader|None = None, + outputDef: LyOutputDef|None = None, + error: LyObject|None = None, + ) -> None: super().__init__() self.music = music self.scoreIdentifier = scoreIdentifier @@ -739,7 +784,7 @@ def __init__(self, music=None, scoreIdentifier=None, scoreBody=None, lilypondHea self.outputDef = outputDef self.error = error - def stringOutput(self): + def stringOutput(self) -> str: if self.music is not None: return self.music.stringOutput() elif self.scoreIdentifier is not None: @@ -761,18 +806,18 @@ def stringOutput(self): class LyPaperBlock(LyObject): - def __init__(self, outputDef=None): + def __init__(self, outputDef: LyOutputDef|None = None) -> None: super().__init__() self.outputDef = outputDef - def stringOutput(self): + def stringOutput(self) -> str: if self.outputDef is None: # legal?? - return None + return '' else: return self.outputDef.stringOutput() class LyLayout(LyObject): - def stringOutput(self): + def stringOutput(self) -> str: theseStrings = [self.backslash + 'layout {', ' ' + self.backslash + 'context {', ' ' + self.backslash + 'RemoveEmptyStaves', @@ -787,11 +832,11 @@ class LyOutputDef(LyObject): This is an ugly grammar, since it does not close the curly bracket. ''' - def __init__(self, outputDefBody=None): + def __init__(self, outputDefBody: LyOutputDefBody|None = None) -> None: super().__init__() self.outputDefBody = outputDefBody - def stringOutput(self): + def stringOutput(self) -> str: if self.outputDefBody is None: raise LilyObjectsException('Need outputDefBody to be set') # pragma: no cover return self.outputDefBody.stringOutput() + '}' @@ -809,11 +854,11 @@ class LyOutputDefHead(LyObject): According to Appendix C, is the same as LyOutputDefHeadWithModeSwitch ''' - def __init__(self, defType=None): + def __init__(self, defType: str|None = None) -> None: super().__init__() self.defType = defType - def stringOutput(self): + def stringOutput(self) -> str: if self.defType not in ('paper', 'midi', 'layout'): # pragma: no cover raise LilyObjectsException("self.defType must be one of 'paper', 'midi', or 'layout'") @@ -832,8 +877,14 @@ class LyOutputDefBody(LyObject): | output_def_body error ''' - def __init__(self, outputDefHead=None, outputDefIdentifier=None, outputDefBody=None, - assignment=None, contextDefSpecBlock=None, error=None): + def __init__(self, + outputDefHead: LyOutputDefHead|None = None, + outputDefIdentifier: str|None = None, + outputDefBody: LyOutputDefBody|None = None, + assignment: LyAssignment|None = None, + contextDefSpecBlock: LyContextDefSpecBlock|None = None, + error: LyObject|None = None, + ) -> None: super().__init__() self.outputDefHead = outputDefHead self.outputDefIdentifier = outputDefIdentifier @@ -842,14 +893,14 @@ def __init__(self, outputDefHead=None, outputDefIdentifier=None, outputDefBody=N self.contextDefSpecBlock = contextDefSpecBlock self.error = error - def stringOutput(self): + def stringOutput(self) -> str: if self.outputDefHead is not None: out = str(self.outputDefHead) + ' { ' if self.outputDefIdentifier is not None: return out + str(self.outputDefIdentifier) else: return out - elif self.outputDefBody is not None: # pragma: no cover + elif self.outputDefBody is None: # pragma: no cover raise LilyObjectsException('Need embedded outputDefBody if outputDefIdentifier ' + 'or outputDefHead are not defined') elif self.assignment is not None: @@ -875,24 +926,28 @@ class LyTempoEvent(LyObject): More complex: - >>> steno = lily.lilyObjects.LyStenoDuration('quarter') + >>> steno = lily.lilyObjects.LyStenoDuration(4) >>> tempoRange = lily.lilyObjects.LyTempoRange(70, 100) >>> lte = lily.lilyObjects.LyTempoEvent(tempoRange=tempoRange, stenoDuration=steno) >>> str(lte) - '\\tempo quarter = 70~100 ' + '\\tempo 4 = 70-100 ' >>> lte.scalar = 85 >>> str(lte) - '\\tempo 85 quarter = 70~100 ' + '\\tempo 85 4 = 70-100 ' ''' - def __init__(self, tempoRange=None, stenoDuration=None, scalar=None): + def __init__(self, + tempoRange: LyTempoRange|None = None, + stenoDuration: LyStenoDuration|None = None, + scalar: int|str|None = None, + ) -> None: super().__init__() self.tempoRange = tempoRange self.stenoDuration = stenoDuration self.scalar = scalar - def stringOutput(self): + def stringOutput(self) -> str: base = self.backslash + 'tempo' if self.tempoRange is not None: if self.stenoDuration is None: # pragma: no cover @@ -917,24 +972,27 @@ class LyMusicList(LyObject): can take any number of LyMusic, LyEmbeddedScm, or LyError objects ''' - def __init__(self, contents=None): + def __init__(self, contents: list[LyObject|str]|None = None) -> None: super().__init__() if contents is None: contents = [] self.contents = contents - def stringOutput(self): + def stringOutput(self) -> str: return self.newlineSeparateStringOutputIfNotNone(self.contents) class LyMusic(LyObject): - def __init__(self, simpleMusic=None, compositeMusic=None): + def __init__(self, + simpleMusic: LySimpleMusic|None = None, + compositeMusic: LyCompositeMusic|None = None, + ) -> None: super().__init__() self.simpleMusic = simpleMusic self.compositeMusic = compositeMusic - def stringOutput(self): + def stringOutput(self) -> str: if self.simpleMusic is not None: return self.simpleMusic.stringOutput() elif self.compositeMusic is not None: @@ -945,11 +1003,11 @@ def stringOutput(self): class LyAlternativeMusic(LyObject): - def __init__(self, musicList=None): + def __init__(self, musicList: LyMusicList|None = None) -> None: super().__init__() self.musicList = musicList - def stringOutput(self): + def stringOutput(self) -> str: if self.musicList is None: return '' else: @@ -958,14 +1016,24 @@ def stringOutput(self): class LyRepeatedMusic(LyObject): - def __init__(self, simpleString=None, unsignedNumber=None, music=None, alternativeMusic=None): + def __init__(self, + simpleString: LyObject|None = None, + unsignedNumber: LyObject|None = None, + music: LyMusic|None = None, + alternativeMusic: LyAlternativeMusic|None = None, + ) -> None: super().__init__() self.simpleString = simpleString self.unsignedNumber = unsignedNumber self.music = music self.alternativeMusic = alternativeMusic - def stringOutput(self): + def stringOutput(self) -> str: + if (self.simpleString is None + or self.unsignedNumber is None + or self.music is None): # pragma: no cover + raise LilyObjectsException( + 'need simpleString, unsignedNumber, and music to output repeated music') out = (self.backslash + 'repeat ' + self.simpleString.stringOutput() @@ -984,13 +1052,17 @@ class LySequentialMusic(LyObject): Can be explicitly tagged with "\sequential" if displayTag is True ''' - def __init__(self, musicList=None, displayTag=False, beforeMatter=None): + def __init__(self, + musicList: LyObject|None = None, + displayTag: bool = False, + beforeMatter: str|None = None, + ) -> None: super().__init__() self.musicList = musicList self.displayTag = displayTag self.beforeMatter = beforeMatter - def stringOutput(self): + def stringOutput(self) -> str: if self.musicList is not None: musicListSO = self.musicList.stringOutput() else: @@ -1015,12 +1087,12 @@ class LyOssiaMusic(LyObject): Can be tagged with \startStaff and \stopStaff if startstop is True ''' - def __init__(self, musicList=None, startstop=True): + def __init__(self, musicList: LyMusicList|None = None, startstop: bool = True) -> None: super().__init__() self.musicList = musicList self.startstop = startstop - def stringOutput(self): + def stringOutput(self) -> str: if self.startstop is True: start = self.backslash + 'startStaff ' stop = self.backslash + 'stopStaff' @@ -1043,12 +1115,12 @@ class LySimultaneousMusic(LyObject): otherwise encloses in double angle brackets ''' - def __init__(self, musicList=None, displayTag=False): + def __init__(self, musicList: LyMusicList|None = None, displayTag: bool = False) -> None: super().__init__() self.musicList = musicList self.displayTag = displayTag - def stringOutput(self): + def stringOutput(self) -> str: if self.musicList is not None: musicListSO = self.musicList.stringOutput() else: @@ -1062,15 +1134,19 @@ def stringOutput(self): class LySimpleMusic(LyObject): - def __init__(self, eventChord=None, musicIdentifier=None, - musicPropertyDef=None, contextChange=None): + def __init__(self, + eventChord: LyEventChord|None = None, + musicIdentifier: LyObject|None = None, + musicPropertyDef: LyMusicPropertyDef|None = None, + contextChange: LyContextChange|None = None, + ) -> None: super().__init__() self.eventChord = eventChord self.musicIdentifier = musicIdentifier self.musicPropertyDef = musicPropertyDef self.contextChange = contextChange - def stringOutput(self): + def stringOutput(self) -> str: outputObject = self.getFirstNonNoneAttribute(['eventChord', 'musicIdentifier', 'musicPropertyDef', 'contextChange']) if outputObject is None: @@ -1085,13 +1161,17 @@ class LyContextModification(LyObject): but not context_mod!!!!! ''' - def __init__(self, contextModList=None, contextModIdentifier=None, displayWith=True): + def __init__(self, + contextModList: LyContextModList|list[str]|None = None, + contextModIdentifier: str|None = None, + displayWith: bool = True, + ) -> None: super().__init__() self.contextModList = contextModList - self.contextModIdentifier = contextModIdentifier # String? + self.contextModIdentifier = contextModIdentifier self.displayWith = displayWith # optional, but not supported without so far - def stringOutput(self): + def stringOutput(self) -> str: if self.contextModList is not None: return self.backslash + 'with ' + self.encloseCurly(self.contextModList) elif self.contextModIdentifier is not None: @@ -1105,14 +1185,17 @@ class LyContextModList(LyObject): contains zero or more LyContextMod objects and an optional contextModIdentifier ''' - def __init__(self, contents=None, contextModIdentifier=None): + def __init__(self, + contents: list[LyContextMod]|None = None, + contextModIdentifier: str|None = None, + ) -> None: if contents is None: contents = [] super().__init__() self.contents = contents - self.contextModIdentifier = contextModIdentifier # STRING + self.contextModIdentifier = contextModIdentifier - def stringOutput(self): + def stringOutput(self) -> str: output = self.newlineSeparateStringOutputIfNotNone(self.contents) if self.contextModIdentifier is not None: return output + ' ' + self.contextModIdentifier @@ -1125,20 +1208,25 @@ class LyCompositeMusic(LyObject): one of LyPrefixCompositeMusic or LyGroupedMusicList stored in self.contents ''' - def __init__(self, prefixCompositeMusic=None, groupedMusicList=None, newLyrics=None): + def __init__(self, + prefixCompositeMusic: LyPrefixCompositeMusic|None = None, + groupedMusicList: LyObject|None = None, + newLyrics: LyNewLyrics|None = None, + ) -> None: super().__init__() self.prefixCompositeMusic = prefixCompositeMusic self.groupedMusicList = groupedMusicList self.newLyrics = newLyrics @property - def contents(self): + def contents(self) -> LyObject|None: if self.prefixCompositeMusic is not None: return self.prefixCompositeMusic else: return self.groupedMusicList - def stringOutput(self): + def stringOutput(self) -> str: + newLyrics: LyNewLyrics|str if self.newLyrics is not None: newLyrics = self.newLyrics else: @@ -1158,12 +1246,15 @@ class LyGroupedMusicList(LyObject): one of LySimultaneousMusic or LySequentialMusic ''' - def __init__(self, simultaneousMusic=None, sequentialMusic=None): + def __init__(self, + simultaneousMusic: LySimultaneousMusic|None = None, + sequentialMusic: LySequentialMusic|None = None, + ) -> None: super().__init__() self.simultaneousMusic = simultaneousMusic self.sequentialMusic = sequentialMusic - def stringOutput(self): + def stringOutput(self) -> str: if self.simultaneousMusic is not None: return str(self.simultaneousMusic) elif self.sequentialMusic is not None: @@ -1212,13 +1303,13 @@ class LySchemeFunction(LyObject): We have usually been using LyEmbeddedScm for this ''' - def __init__(self, content=None): + def __init__(self, content: LyObject|str|None = None) -> None: super().__init__() self.content = content - def stringOutput(self): + def stringOutput(self) -> str: if self.content is None: - return None + return '' else: return str(self.content) @@ -1228,13 +1319,13 @@ class LyOptionalId(LyObject): an optional id setting ''' - def __init__(self, content=None): + def __init__(self, content: str|None = None) -> None: super().__init__() self.content = content - def stringOutput(self): + def stringOutput(self) -> str: if self.content is None: - return None + return '' else: return ' = ' + self.content @@ -1272,14 +1363,23 @@ class LyPrefixCompositeMusic(LyObject): | re_rhythmed_music ''' # pylint: disable=redefined-builtin - def __init__(self, type=None, genericPrefixMusicScm=None, - simpleString=None, optionalId=None, optionalContextMod=None, - music=None, fraction=None, repeatedMusic=None, - pitchAlsoInChords1=None, pitchAlsoInChords2=None, - modeChangingHead=None, groupedMusicList=None, - modeChangingHeadWithContext=None, relativeMusic=None, - reRhythmedMusic=None - ): + def __init__(self, + type: str|None = None, + genericPrefixMusicScm: LyObject|None = None, + simpleString: str|None = None, + optionalId: LyOptionalId|None = None, + optionalContextMod: LyContextModification|None = None, + music: LyObject|str|None = None, + fraction: str|None = None, + repeatedMusic: LyRepeatedMusic|None = None, + pitchAlsoInChords1: LyPitch|None = None, + pitchAlsoInChords2: LyPitch|None = None, + modeChangingHead: LyModeChangingHead|None = None, + groupedMusicList: LyGroupedMusicList|None = None, + modeChangingHeadWithContext: LyModeChangingHead|None = None, + relativeMusic: LyRelativeMusic|None = None, + reRhythmedMusic: LyReRhythmedMusic|None = None, + ) -> None: super().__init__() self.type = type self.genericPrefixMusicScm = genericPrefixMusicScm @@ -1297,7 +1397,7 @@ def __init__(self, type=None, genericPrefixMusicScm=None, self.relativeMusic = relativeMusic self.reRhythmedMusic = reRhythmedMusic - def stringOutput(self): + def stringOutput(self) -> str: myType = self.type if myType == 'scheme': return str(self.genericPrefixMusicScm) @@ -1346,24 +1446,30 @@ class LyModeChangingHead(LyObject): >>> print(l2.stringOutput()) \chords + 'note' has no context-creating shorthand in LilyPond, so it always gives `\notemode`: + + >>> l3 = lily.lilyObjects.LyModeChangingHead(hasContext=False, mode='note') + >>> print(l3.stringOutput()) + \notemode ''' allowableModes = ['note', 'drum', 'figure', 'chord', 'lyric'] - def __init__(self, hasContext=False, mode=None): + def __init__(self, hasContext: bool = False, mode: str|None = None) -> None: super().__init__() self.hasContext = hasContext self.mode = mode - def stringOutput(self): - if self.mode is None: + def stringOutput(self) -> str: + mode = self.mode + if mode is None: raise LilyObjectsException('Mode must be set') # pragma: no cover - if self.mode not in self.allowableModes: - raise LilyObjectsException(f'Not an allowable mode {self.mode}') # pragma: no cover + if mode not in self.allowableModes: + raise LilyObjectsException(f'Not an allowable mode {mode}') # pragma: no cover - if self.hasContext: - return self.backslash + self.mode + 'mode' + if self.hasContext or mode == 'note': + return self.backslash + mode + 'mode' else: - return self.backslash + self.mode + 's' + return self.backslash + mode + 's' class LyRelativeMusic(LyObject): @@ -1371,11 +1477,13 @@ class LyRelativeMusic(LyObject): relative music ''' - def __init__(self, content=None): + def __init__(self, content: LyObject|None = None) -> None: super().__init__() self.content = content - def stringOutput(self): + def stringOutput(self) -> str: + if self.content is None: # pragma: no cover + raise LilyObjectsException('need content for relative music') return self.backslash + 'relative ' + self.content.stringOutput() @@ -1384,17 +1492,17 @@ class LyNewLyrics(LyObject): contains a list of LyGroupedMusicList objects or identifiers ''' - def __init__(self, groupedMusicLists=None): + def __init__(self, groupedMusicLists: list[LyGroupedMusicList|str]|None = None) -> None: if groupedMusicLists is None: groupedMusicLists = [] super().__init__() self.groupedMusicLists = groupedMusicLists - def stringOutput(self): + def stringOutput(self) -> str: outputString = '' for c in self.groupedMusicLists: outputString += self.backslash + 'addlyrics ' - if hasattr(c, 'stringOutput'): + if isinstance(c, LyObject): outputString += c.stringOutput() else: outputString += c + ' ' @@ -1403,19 +1511,25 @@ def stringOutput(self): class LyReRhythmedMusic(LyObject): - def __init__(self, groupedMusic=None, newLyrics=None): + def __init__(self, + groupedMusic: LyGroupedMusicList|str|None = None, + newLyrics: LyNewLyrics|None = None, + ) -> None: super().__init__() self.groupedMusic = groupedMusic self.newLyrics = newLyrics - def stringOutput(self): + def stringOutput(self) -> str: c = self.groupedMusic - if hasattr(c, 'stringOutput'): + if isinstance(c, LyObject): outputString = c.stringOutput() - else: + elif c is not None: outputString = c + ' ' - outputString += self.newLyrics.stringOutput() - return outputString # previously this did not return + else: # pragma: no cover + raise LilyObjectsException('need groupedMusic for re-rhythmed music') + if self.newLyrics is not None: + outputString += self.newLyrics.stringOutput() + return outputString class LyContextChange(LyObject): @@ -1425,12 +1539,12 @@ class LyContextChange(LyObject): '\\change x = y ' ''' - def __init__(self, before=None, after=None): + def __init__(self, before: str = '', after: str = '') -> None: super().__init__() self.before = before self.after = after - def stringOutput(self): + def stringOutput(self) -> str: return self.backslash + 'change ' + self.before + ' = ' + self.after + ' ' @@ -1441,14 +1555,14 @@ class LyPropertyPath(LyObject): has one or more of LyEmbeddedScm objects ''' - def __init__(self, embeddedScheme=None): + def __init__(self, embeddedScheme: list[LyEmbeddedScm]|None = None) -> None: if embeddedScheme is None: embeddedScheme = [] super().__init__() self.embeddedScheme = embeddedScheme - def stringOutput(self): + def stringOutput(self) -> str: return ' '.join([es.stringOutput() for es in self.embeddedScheme]) @@ -1480,17 +1594,19 @@ class LyPropertyOperation(LyObject): TODO: should \set be given? ''' - def __init__(self, mode=None, value1=None, value2=None, value3=None): + def __init__(self, + mode: str|None = None, + value1: str = '', + value2: str = '', + value3: str = '', + ) -> None: super().__init__() self.mode = mode self.value1 = value1 self.value2 = value2 self.value3 = value3 - def stringOutput(self): - if self.mode not in ('set', 'unset', 'override', 'revert'): - raise LilyObjectsException(f'invalid mode {self.mode}') - + def stringOutput(self) -> str: if self.mode == 'set': return self.backslash + 'set ' + self.value1 + ' = ' + self.value2 + ' ' elif self.mode == 'unset': @@ -1500,6 +1616,8 @@ def stringOutput(self): ' = ', self.value3, ' ']) elif self.mode == 'revert': return self.backslash + 'revert ' + self.value1 + '.' + self.value2 + ' ' + else: + raise LilyObjectsException(f'invalid mode {self.mode}') class LyContextDefMod(LyObject): @@ -1507,21 +1625,26 @@ class LyContextDefMod(LyObject): one of consists, remove, accepts, defaultchild, denies, alias, type, description, name ''' - def __init__(self, contextDef=None): + def __init__(self, contextDef: str = '') -> None: super().__init__() self.contextDef = contextDef - def stringOutput(self): + def stringOutput(self) -> str: return self.backslash + self.contextDef + ' ' class LyContextMod(LyObject): - def __init__(self, contextDefOrProperty=None, scalar=None): + def __init__(self, + contextDefOrProperty: LyContextDefMod|LyPropertyOperation|None = None, + scalar: str|None = None, + ) -> None: super().__init__() self.contextDefOrProperty = contextDefOrProperty self.scalar = scalar - def stringOutput(self): + def stringOutput(self) -> str: + if self.contextDefOrProperty is None: # pragma: no cover + raise LilyObjectsException('need a contextDef or property to modify a context') if self.scalar is None: return self.contextDefOrProperty.stringOutput() else: @@ -1533,12 +1656,14 @@ def stringOutput(self): class LyMusicPropertyDef(LyObject): - def __init__(self, isOnce=False, propertyDef=None): + def __init__(self, isOnce: bool = False, propertyDef: LyPropertyOperation|None = None) -> None: super().__init__() self.isOnce = isOnce self.propertyDef = propertyDef - def stringOutput(self): + def stringOutput(self) -> str: + if self.propertyDef is None: # pragma: no cover + raise LilyObjectsException('need a propertyDef for a music property definition') s = '' if self.isOnce: s += self.backslash + 'once ' @@ -1562,8 +1687,15 @@ class LyEventChord(LyObject): once that is done. But there is no LySimpleChordElements object yet. ''' - def __init__(self, simpleChordElements=None, postEvents=None, chordRepetition=None, - multiMeasureRest=None, duration=None, commandElement=None, noteChordElement=None): + def __init__(self, + simpleChordElements: LySimpleElement|None = None, + postEvents: list[LyObject|str]|None = None, + chordRepetition: LyObject|str|None = None, + multiMeasureRest: LyObject|str|None = None, + duration: str|None = None, + commandElement: LyCommandElement|None = None, + noteChordElement: LyNoteChordElement|None = None, + ) -> None: super().__init__() self.simpleChordElements = simpleChordElements self.postEvents = postEvents @@ -1573,7 +1705,7 @@ def __init__(self, simpleChordElements=None, postEvents=None, chordRepetition=No self.commandElement = commandElement self.noteChordElement = noteChordElement - def stringOutput(self): + def stringOutput(self) -> str: if self.noteChordElement is not None: return str(self.noteChordElement) + ' ' elif self.commandElement is not None: @@ -1606,7 +1738,11 @@ def stringOutput(self): class LyNoteChordElement(LyObject): - def __init__(self, chordBody=None, optionalNoteModeDuration=None, postEvents=None): + def __init__(self, + chordBody: LyChordBody|LyPitch|None = None, + optionalNoteModeDuration: LyMultipliedDuration|None = None, + postEvents: list[LyObject|str]|None = None, + ) -> None: if postEvents is None: postEvents = [] super().__init__() @@ -1614,7 +1750,7 @@ def __init__(self, chordBody=None, optionalNoteModeDuration=None, postEvents=Non self.optionalNoteModeDuration = optionalNoteModeDuration self.postEvents = postEvents - def stringOutput(self): + def stringOutput(self) -> str: c = str(self.chordBody) if self.optionalNoteModeDuration is not None: c += str(self.optionalNoteModeDuration) + ' ' @@ -1625,14 +1761,14 @@ def stringOutput(self): class LyChordBody(LyObject): - def __init__(self, chordBodyElements=None): + def __init__(self, chordBodyElements: list[LyChordBodyElement]|None = None) -> None: if chordBodyElements is None: chordBodyElements = [] super().__init__() self.chordBodyElements = chordBodyElements - def stringOutput(self): + def stringOutput(self) -> str: c = ' '.join([str(cbe) for cbe in self.chordBodyElements]) return ' '.join(['<', c, '> ']) @@ -1652,13 +1788,13 @@ class LyChordBodyElement(LyObject): TODO: only the first form is currently supported in creation ''' - def __init__(self, parts=None): + def __init__(self, parts: list[LyObject|str]|None = None) -> None: if parts is None: parts = [] super().__init__() self.parts = parts - def stringOutput(self): + def stringOutput(self) -> str: return ' '.join([str(p) for p in self.parts]) # music_function_identifier_musicless_prefix: MUSIC_FUNCTION @@ -1680,41 +1816,53 @@ def stringOutput(self): class LyCommandElement(LyObject): - def __init__(self, commandType=None, argument=None): + def __init__(self, + commandType: LyObject|str|None = None, + argument: LyObject|str|None = None, + ) -> None: super().__init__() self.commandType = commandType self.argument = argument - def stringOutput(self): + def stringOutput(self) -> str: ct = self.commandType - if ct == 'skip': - return self.backslash + 'skip ' + self.argument.stringOutput() + if not isinstance(ct, str): + if ct is None: # pragma: no cover + raise LilyObjectsException('need a commandType to output a command element') + return ct.stringOutput() + + arg = self.argument + if ct in ('skip', 'partial'): + argOut = arg.stringOutput() if isinstance(arg, LyObject) else str(arg) + return self.backslash + ct + ' ' + argOut elif ct == '[': + # \[ and \] are ligature brackets; manual beams are plain [ and ] return self.backslash + '[ ' elif ct == ']': return self.backslash + '] ' elif ct == self.backslash: return ct + ' ' - elif ct == 'partial': - return self.backslash + 'partial ' + self.argument.stringOutput() - elif ct == 'time': - return self.backslash + 'time ' + self.argument + ' ' - elif ct == 'mark': - return self.backslash + 'mark ' + self.argument + ' ' - else: - return ct.stringOutput() + elif ct in ('time', 'mark'): + return self.backslash + ct + ' ' + str(arg) + ' ' + else: # pragma: no cover + raise LilyObjectsException(f'unknown commandType {ct}') class LyCommandEvent(LyObject): - def __init__(self, commandType=None, argument1=None, argument2=None): + def __init__(self, + commandType: LyTempoEvent|str|None = None, + argument1: str = '', + argument2: str = '', + ) -> None: super().__init__() self.commandType = commandType self.argument1 = argument1 self.argument2 = argument2 - def stringOutput(self): + def stringOutput(self) -> str: ct = self.commandType - if ct == '~': # ??? not tie? + if ct == '~': + # E_TILDE: the pes-or-flexa ligature event, not a tie (a tie is a bare ~) return self.backslash + '~ ' elif ct == 'mark-default': return self.backslash + 'mark ' + self.backslash + 'default ' @@ -1723,29 +1871,31 @@ def stringOutput(self): elif ct == 'key': # \key NOTENAME_PITCH SCM_IDENTIFIER return self.backslash + 'key ' + self.argument1 + ' ' + self.argument2 + ' ' - else: # tempo_event + elif isinstance(ct, LyTempoEvent): return ct.stringOutput() + else: # pragma: no cover + raise LilyObjectsException(f'unknown commandType {ct}') class LyPostEvents(LyObject): - def __init__(self, eventList=None): + def __init__(self, eventList: list[LyObject]|None = None) -> None: if eventList is None: eventList = [] super().__init__() self.eventList = eventList - def stringOutput(self): + def stringOutput(self) -> str: return ' '.join([e.stringOutput() for e in self.eventList]) class LyPostEvent(LyObject): - def __init__(self, arg1=None, arg2=None): + def __init__(self, arg1: LyObject|str|None = None, arg2: LyObject|str|None = None) -> None: super().__init__() self.arg1 = arg1 self.arg2 = arg2 - def stringOutput(self): + def stringOutput(self) -> str: c = str(self.arg1) if self.arg2 is not None: c += ' ' + str(self.arg2) @@ -1758,34 +1908,34 @@ class LyDirectionLessEvent(LyObject): or an EVENT_IDENTIFIER or a tremolo_type ''' - def __init__(self, event=None): + def __init__(self, event: LyObject|str|None = None) -> None: super().__init__() self.event = event - def stringOutput(self): + def stringOutput(self) -> str: return str(self.event) + ' ' # noinspection SpellCheckingInspection class LyDirectionReqdEvent(LyObject): - def __init__(self, event=None): + def __init__(self, event: LyObject|str|None = None) -> None: super().__init__() self.event = event - def stringOutput(self): + def stringOutput(self) -> str: return str(self.event) + ' ' class LyOctaveCheck(LyObject): - def __init__(self, equalOrQuotesOrNone=None): + def __init__(self, equalOrQuotesOrNone: str|None = None) -> None: super().__init__() self.equalOrQuotesOrNone = equalOrQuotesOrNone - def stringOutput(self): + def stringOutput(self) -> str: eqn = self.equalOrQuotesOrNone if eqn is None: - return None + return '' elif eqn == '=': return '= ' else: @@ -1798,13 +1948,13 @@ class LyPitch(LyObject): also used for steno_pitch and steno_tonic_pitch ''' - def __init__(self, noteNamePitch=None, quotes=None): + def __init__(self, noteNamePitch: str = '', quotes: str = '') -> None: super().__init__() self.noteNamePitch = noteNamePitch self.quotes = quotes - def stringOutput(self): - return self.noteNamePitch + str(self.quotes) + ' ' + def stringOutput(self) -> str: + return self.noteNamePitch + self.quotes + ' ' # no need for pitch_also_in_chords @@ -1814,11 +1964,11 @@ class LyGenTextDef(LyObject): holds either full_markup, string, or DIGIT ''' - def __init__(self, value=None): + def __init__(self, value: LyFullMarkup|str|int|None = None) -> None: super().__init__() self.value = value - def stringOutput(self): + def stringOutput(self) -> str: return str(self.value) + ' ' @@ -1830,12 +1980,12 @@ class LyScriptAbbreviation(LyObject): ''' - def __init__(self, value=None): + def __init__(self, value: str = '') -> None: super().__init__() self.value = value - def stringOutput(self): - return str(self.value) + ' ' + def stringOutput(self) -> str: + return self.value + ' ' class LyScriptDir(LyObject): @@ -1846,12 +1996,12 @@ class LyScriptDir(LyObject): ''' - def __init__(self, value=None): + def __init__(self, value: str = '') -> None: super().__init__() self.value = value - def stringOutput(self): - return str(self.value) + ' ' + def stringOutput(self) -> str: + return self.value + ' ' # no need for absolute_pitch # no need for optional_notemode_duration -- we can use LyMultipliedDuration or None @@ -1869,12 +2019,12 @@ class LyStenoDuration(LyObject): ''' - def __init__(self, durationNumber=None, numDots=0): + def __init__(self, durationNumber: int|str|None = None, numDots: int = 0) -> None: super().__init__() self.durationNumber = durationNumber self.numDots = numDots - def stringOutput(self): + def stringOutput(self) -> str: dotStr = '.' * self.numDots return str(self.durationNumber) + dotStr + ' ' @@ -1884,20 +2034,23 @@ class LyMultipliedDuration(LyObject): represents either a simple LyStenoDuration or a list of things that the steno duration should be multiplied by. - if stenoDur is None then output is None -- thus also represents + if stenoDur is None then output is empty -- thus also represents optional_notemode_duration ''' - def __init__(self, stenoDur=None, multiply=None): + def __init__(self, + stenoDur: LyStenoDuration|None = None, + multiply: list[int|str]|None = None, + ) -> None: if multiply is None: multiply = [] super().__init__() self.stenoDur = stenoDur self.multiply = multiply - def stringOutput(self): + def stringOutput(self) -> str: if self.stenoDur is None: - return None + return '' else: s = str(self.stenoDur) for m in self.multiply: @@ -1907,11 +2060,11 @@ def stringOutput(self): class LyTremoloType(LyObject): - def __init__(self, tremTypeOrNone=None): + def __init__(self, tremTypeOrNone: int|str|None = None) -> None: super().__init__() self.tremTypeOrNone = tremTypeOrNone - def stringOutput(self): + def stringOutput(self) -> str: if self.tremTypeOrNone is not None: return ':' + str(self.tremTypeOrNone) + ' ' else: @@ -1921,11 +2074,11 @@ def stringOutput(self): class LyOptionalRest(LyObject): - def __init__(self, rest=False): + def __init__(self, rest: bool = False) -> None: super().__init__() self.rest = rest - def stringOutput(self): + def stringOutput(self) -> str: if self.rest is False: return '' else: @@ -1947,13 +2100,13 @@ class LySimpleElement(LyObject): | lyric_element optional_notemode_duration ''' - def __init__(self, parts=None): + def __init__(self, parts: list[LyObject|str]|None = None) -> None: if parts is None: parts = [] super().__init__() self.parts = parts - def stringOutput(self): + def stringOutput(self) -> str: return ''.join([str(p) for p in self.parts]) # SKIPPING ALL ChordSymbol Markup for now @@ -1970,29 +2123,35 @@ class LyLyricElement(LyObject): hel_ ''' - def __init__(self, lyMarkupOrString=None): + def __init__(self, lyMarkupOrString: LyMarkup|str|None = None) -> None: super().__init__() self.lyMarkupOrString = lyMarkupOrString - def stringOutput(self): + def stringOutput(self) -> str: return str(self.lyMarkupOrString) + ' ' class LyTempoRange(LyObject): r''' defines either a single tempo or a range + + >>> print(lily.lilyObjects.LyTempoRange(70, 100)) + 70-100 ''' - def __init__(self, lowestOrOnlyTempo=None, highestTempoOrNone=None): + def __init__(self, + lowestOrOnlyTempo: int|str|None = None, + highestTempoOrNone: int|str|None = None, + ) -> None: super().__init__() self.lowestOrOnlyTempo = lowestOrOnlyTempo self.highestTempoOrNone = highestTempoOrNone - def stringOutput(self): + def stringOutput(self) -> str: if self.highestTempoOrNone is None: return str(self.lowestOrOnlyTempo) + ' ' else: - return str(self.lowestOrOnlyTempo) + '~' + str(self.highestTempoOrNone) + ' ' + return str(self.lowestOrOnlyTempo) + '-' + str(self.highestTempoOrNone) + ' ' class LyNumberExpression(LyObject): @@ -2000,13 +2159,13 @@ class LyNumberExpression(LyObject): any list of numbers or LyNumberTerms separated by '+' or '-' objects. ''' - def __init__(self, numberAndSepList=None): + def __init__(self, numberAndSepList: list[LyNumberTerm|int|str]|None = None) -> None: if numberAndSepList is None: numberAndSepList = [] super().__init__() self.numberAndSepList = numberAndSepList - def stringOutput(self): + def stringOutput(self) -> str: c = ' '.join([str(s) for s in self.numberAndSepList]) return c + ' ' @@ -2016,24 +2175,27 @@ class LyNumberTerm(LyObject): any list of numbers separated by '*' or '/' strings. ''' - def __init__(self, numberAndSepList=None): + def __init__(self, numberAndSepList: list[int|str]|None = None) -> None: if numberAndSepList is None: numberAndSepList = [] super().__init__() self.numberAndSepList = numberAndSepList - def stringOutput(self): + def stringOutput(self) -> str: c = ' '.join([str(s) for s in self.numberAndSepList]) return c + ' ' class LyLyricMarkup(LyObject): - def __init__(self, lyricMarkupOrIdentifier=None, markupTop=None): + def __init__(self, + lyricMarkupOrIdentifier: LyObject|str|None = None, + markupTop: LyMarkupTop|None = None, + ) -> None: super().__init__() self.lyricMarkupOrIdentifier = lyricMarkupOrIdentifier self.markupTop = markupTop - def stringOutput(self): + def stringOutput(self) -> str: if self.markupTop is None: return str(self.lyricMarkupOrIdentifier) + ' ' else: @@ -2041,81 +2203,92 @@ def stringOutput(self): class LyFullMarkupList(LyObject): - def __init__(self, markupListOrIdentifier=None): + r''' + >>> markupList = lily.lilyObjects.LyMarkupList('{ "a" "b" }') + >>> print(lily.lilyObjects.LyFullMarkupList(markupList)) + \markuplist { "a" "b" } + ''' + + def __init__(self, markupListOrIdentifier: LyMarkupList|str|None = None) -> None: super().__init__() self.markupListOrIdentifier = markupListOrIdentifier - def stringOutput(self): - if isinstance(self.markupListOrIdentifier, str): - return self.markupListOrIdentifier + ' ' + def stringOutput(self) -> str: + mli = self.markupListOrIdentifier + if isinstance(mli, str): + return mli + ' ' + elif mli is None: # pragma: no cover + raise LilyObjectsException('need a markup list or identifier') else: - # noinspection SpellCheckingInspection - return self.backslash + 'markuplines ' + self.markupListOrIdentifier.stringOutput() + # \markuplines was renamed \markuplist in LilyPond 2.16 + return self.backslash + 'markuplist ' + mli.stringOutput() class LyFullMarkup(LyObject): - def __init__(self, markupTopOrIdentifier=None): + def __init__(self, markupTopOrIdentifier: LyMarkupTop|str|None = None) -> None: super().__init__() self.markupTopOrIdentifier = markupTopOrIdentifier - def stringOutput(self): - if isinstance(self.markupTopOrIdentifier, str): - return self.markupTopOrIdentifier + ' ' + def stringOutput(self) -> str: + mti = self.markupTopOrIdentifier + if isinstance(mti, str): + return mti + ' ' + elif mti is None: # pragma: no cover + raise LilyObjectsException('need a markup top or identifier') else: - return self.backslash + 'markup ' + self.markupTopOrIdentifier.stringOutput() + return self.backslash + 'markup ' + mti.stringOutput() class LyMarkupTop(LyObject): - def __init__(self, argument1=None, argument2=None): + def __init__(self, argument1: str = '', argument2: str|None = None) -> None: super().__init__() self.argument1 = argument1 self.argument2 = argument2 - def stringOutput(self): + def stringOutput(self) -> str: if self.argument2 is None: - return str(self.argument1) + return self.argument1 else: return ' '.join([self.argument1, self.argument2]) class LyMarkupList(LyObject): - def __init__(self, markupIdentifierOrList=None): + def __init__(self, markupIdentifierOrList: LyObject|str|None = None) -> None: super().__init__() self.markupIdentifierOrList = markupIdentifierOrList - def stringOutput(self): + def stringOutput(self) -> str: return str(self.markupIdentifierOrList) class LyMarkupComposedList(LyObject): - def __init__(self, markupHeadList=None, markupBracedList=None): + def __init__(self, markupHeadList: str = '', markupBracedList: str = '') -> None: super().__init__() self.markupHeadList = markupHeadList self.markupBracedList = markupBracedList - def stringOutput(self): + def stringOutput(self) -> str: return ' '.join([self.markupHeadList, self.markupBracedList]) class LyMarkupBracedList(LyObject): - def __init__(self, listBody=None): + def __init__(self, listBody: str = '') -> None: super().__init__() self.listBody = listBody - def stringOutput(self): + def stringOutput(self) -> str: return ' '.join(['{', self.listBody, '}']) class LyMarkupBracedListBody(LyObject): - def __init__(self, markupOrMarkupList=None): + def __init__(self, markupOrMarkupList: list[LyObject|str]|None = None) -> None: if markupOrMarkupList is None: markupOrMarkupList = [] super().__init__() self.markupOrMarkupList = markupOrMarkupList - def stringOutput(self): - super().__init__() + def stringOutput(self) -> str: c = '' for m in self.markupOrMarkupList: c += str(m) + ' ' @@ -2136,28 +2309,37 @@ class LySimpleMarkup(LyObject): takes 1 required arg, 2nd for markup_function ''' - def __init__(self, simpleType='string', argument1=None, argument2=None): + def __init__(self, + simpleType: str = 'string', + argument1: str = '', + argument2: LyObject|str|None = None, + ) -> None: super().__init__() self.simpleType = simpleType self.argument1 = argument1 self.argument2 = argument2 - def stringOutput(self): + def stringOutput(self) -> str: if self.simpleType == 'string': return self.argument1 + ' ' elif self.simpleType == 'score-body': return self.backslash + 'score { ' + self.argument1 + ' } ' elif self.simpleType == 'markup-function': return self.argument1 + ' ' + str(self.argument2) + ' ' + else: # pragma: no cover + raise LilyObjectsException(f'unknown simpleType {self.simpleType}') class LyMarkup(LyObject): - def __init__(self, simpleMarkup=None, optionalMarkupHeadList=None): + def __init__(self, + simpleMarkup: LySimpleMarkup|None = None, + optionalMarkupHeadList: str|None = None, + ) -> None: super().__init__() self.simpleMarkup = simpleMarkup self.optionalMarkupHeadList = optionalMarkupHeadList - def stringOutput(self): + def stringOutput(self) -> str: if self.optionalMarkupHeadList is not None: c = self.optionalMarkupHeadList + ' ' else: diff --git a/music21/lily/translate.py b/music21/lily/translate.py index 2975dcb20d..cd7f1e4e2b 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -21,8 +21,11 @@ import re import subprocess import sys +import typing as t import unittest +from music21 import base +from music21 import chord from music21 import clef from music21 import common from music21.converter.subConverters import SubConverter @@ -31,7 +34,11 @@ from music21 import environment from music21 import exceptions21 from music21 import key +from music21 import layout +from music21 import metadata +from music21 import meter from music21 import note +from music21 import pitch from music21 import stream from music21 import variant @@ -58,7 +65,7 @@ class _sharedCorpusTestObject: sharedCacheObject = _sharedCorpusTestObject() -def _getCachedCorpusFile(keyName): +def _getCachedCorpusFile(keyName: str) -> stream.Stream: # return corpus.parse(keyName) if keyName not in sharedCacheObject.sharedCache: sharedCacheObject.sharedCache[keyName] = corpus.parse(keyName) @@ -67,7 +74,7 @@ def _getCachedCorpusFile(keyName): # b.parts[0].measure(4)[2].color = 'blue'#.rightBarline = 'double' -def makeLettersOnlyId(inputString): +def makeLettersOnlyId(inputString: str|int) -> str: # noinspection SpellCheckingInspection r''' Takes an id and makes it purely letters by substituting @@ -133,46 +140,48 @@ class LilypondConverter: 'half-flat': 'eh', } + # bar line names as defined by LilyPond 2.18 and later; unknown names + # are silently drawn as nothing. barlineDict = {'regular': '|', - 'dotted': ':', - 'dashed': 'dashed', - 'heavy': '.', # ?? + 'dotted': ';', + 'dashed': '!', + 'heavy': '.', 'double': '||', 'final': '|.', 'heavy-light': '.|', - 'heavy-heavy': '.|.', - 'start-repeat': '|:', - 'end-repeat': ':|', + 'heavy-heavy': '..', + 'start-repeat': '.|:', + 'end-repeat': ':|.', # no music21 support for |.| lightHeavyLight yet 'tick': "'", - # 'short': '', # no lilypond support?? + 'short': ',', 'none': '', } - def __init__(self): + def __init__(self) -> None: self.majorVersion = '1' self.minorVersion = '0' self.versionString = '1.0' self.backend = 'ps' - self.versionScheme = '' - self.headerScheme = '' + self.versionScheme = lyo.LyEmbeddedScm() + self.headerScheme = lyo.LyEmbeddedScm() self.backendString = '--backend=' + self.LILYEXEC = '' self.topLevelObject = lyo.LyLilypondTop() self.setupTools() - self.context = self.topLevelObject - self.storedContexts = [] - self.doNotOutput = [] - self.currentMeasure = None - self.addedVariants = [] + self.context: lyo.LyObject = self.topLevelObject + self.storedContexts: list[lyo.LyObject] = [] + self.doNotOutput: list[base.Music21Object] = [] + self.currentMeasure: stream.Measure|None = None + self.addedVariants: list[str] = [] self.variantColors = ['blue', 'red', 'purple', 'green', 'orange', 'yellow', 'grey'] self.coloredVariants = False self.variantMode = False - self.LILYEXEC = None - self.tempName = None - self.inWord = None + self.tempName: pathlib.Path|None = None + self.inWord = False - def findLilyExec(self): + def findLilyExec(self) -> str: lpEnvironment = environLocal['lilypondPath'] if lpEnvironment is not None and lpEnvironment.exists(): LILYEXEC = str(lpEnvironment) # pragma: no cover @@ -196,17 +205,17 @@ def findLilyExec(self): self.LILYEXEC = LILYEXEC return LILYEXEC - def setupTools(self): + def setupTools(self) -> None: LILYEXEC = self.findLilyExec() command = [LILYEXEC, '--version'] platform = common.getPlatform() - creation_flags = subprocess.CREATE_NO_WINDOW if platform == 'win' else 0 + # CREATE_NO_WINDOW only exists on Windows + creation_flags = getattr(subprocess, 'CREATE_NO_WINDOW', 0) if platform == 'win' else 0 try: with subprocess.Popen(command, stdout=subprocess.PIPE, creationflags=creation_flags) as proc: - stdout, unused = proc.communicate() - stdout = stdout.decode(encoding='utf-8') - versionString = stdout.split()[2] + stdoutBytes, unused = proc.communicate() + versionString = stdoutBytes.decode(encoding='utf-8').split()[2] versionPieces = versionString.split('.') except OSError as exc: # pragma: no cover raise LilyTranslateException( @@ -237,11 +246,11 @@ def setupTools(self): # I had a note that said 2.12 and > should use # 'self.backendString = '--formats=' ' but doesn't seem true - def newContext(self, newContext): + def newContext(self, newContext: lyo.LyObject) -> None: self.storedContexts.append(self.context) self.context = newContext - def restoreContext(self): + def restoreContext(self) -> None: try: self.context = self.storedContexts.pop() except IndexError: # pragma: no cover @@ -249,7 +258,7 @@ def restoreContext(self): # ----------- Set a complete LilyPond Tree from a music21 object ----------# - def textFromMusic21Object(self, m21ObjectIn): + def textFromMusic21Object(self, m21ObjectIn: base.Music21Object) -> str: r''' get a proper lilypond text file for writing from a music21 object @@ -277,7 +286,7 @@ def textFromMusic21Object(self, m21ObjectIn): s = re.sub(r'\s*\n\s*\n', '\n', s).strip() return s - def loadFromMusic21Object(self, m21ObjectIn): + def loadFromMusic21Object(self, m21ObjectIn: base.Music21Object) -> None: r''' Create a LilyPond object hierarchy in self.topLevelObject from an arbitrary music21 object. @@ -285,35 +294,29 @@ def loadFromMusic21Object(self, m21ObjectIn): TODO: make lilypond automatically run makeNotation.makeTupletBrackets(s) TODO: Add tests. ''' - c = m21ObjectIn.classes - if 'Stream' in c: - if m21ObjectIn[variant.Variant]: - # has variants. so we need to make a deepcopy - m21ObjectIn = variant.makeAllVariantsReplacements(m21ObjectIn, recurse=True) - variant.makeVariantBlocks(m21ObjectIn) - - if ('Stream' not in c) or ('Measure' in c) or ('Voice' in c): + if isinstance(m21ObjectIn, stream.Stream) and m21ObjectIn[variant.Variant]: + # has variants. so we need to make a deepcopy + m21ObjectIn = variant.makeAllVariantsReplacements(m21ObjectIn, recurse=True) + variant.makeVariantBlocks(m21ObjectIn) + + if isinstance(m21ObjectIn, stream.Score): + self.loadObjectFromScore(m21ObjectIn, makeNotation=False) + elif isinstance(m21ObjectIn, stream.Opus): + self.loadObjectFromOpus(m21ObjectIn, makeNotation=False) + elif (not isinstance(m21ObjectIn, stream.Stream) + or isinstance(m21ObjectIn, (stream.Measure, stream.Voice))): scoreObj = stream.Score() partObj = stream.Part() # no need for measures or voices partObj.insert(0, m21ObjectIn) scoreObj.insert(0, partObj) self.loadObjectFromScore(scoreObj, makeNotation=False) - elif 'Part' in c: - scoreObj = stream.Score() - scoreObj.insert(0, m21ObjectIn) - self.loadObjectFromScore(scoreObj, makeNotation=False) - elif 'Score' in c: - self.loadObjectFromScore(m21ObjectIn, makeNotation=False) - elif 'Opus' in c: - self.loadObjectFromOpus(m21ObjectIn, makeNotation=False) - else: # treat as part + else: # a Part, or treated as one scoreObj = stream.Score() scoreObj.insert(0, m21ObjectIn) self.loadObjectFromScore(scoreObj, makeNotation=False) - # raise LilyTranslateException(f'Unknown stream type {m21ObjectIn.__class__}') - def loadObjectFromOpus(self, opusIn=None, makeNotation=True): + def loadObjectFromOpus(self, opusIn: stream.Opus, makeNotation: bool = True) -> None: r''' creates a filled topLevelObject (lily.lilyObjects.LyLilypondTop) whose string representation accurately reflects all the Score objects @@ -324,7 +327,7 @@ def loadObjectFromOpus(self, opusIn=None, makeNotation=True): >>> #_DOCS_SHOW lpc.loadObjectFromOpus(fifeOpus, makeNotation=False) >>> #_DOCS_SHOW lpc.showPDF() ''' - contents = [] + contents: list[lyo.LyObject|str] = [] lpVersionScheme = self.versionScheme lpHeaderScheme = self.headerScheme lpColorScheme = lyo.LyEmbeddedScm(self.colorDef) @@ -353,9 +356,9 @@ def loadObjectFromOpus(self, opusIn=None, makeNotation=True): contents.append(lpLayout) - self.context.contents = contents + self.setContextContents(contents) - def loadObjectFromScore(self, scoreIn=None, makeNotation=True): + def loadObjectFromScore(self, scoreIn: stream.Score, makeNotation: bool = True) -> None: r''' creates a filled topLevelObject (lily.lilyObjects.LyLilypondTop) @@ -381,16 +384,16 @@ def loadObjectFromScore(self, scoreIn=None, makeNotation=True): lpOutputDefBody = lyo.LyOutputDefBody(outputDefHead=lpOutputDefHead) lpOutputDef = lyo.LyOutputDef(outputDefBody=lpOutputDefBody) lpLayout = lyo.LyLayout() - contents = [lpVersionScheme, lpHeaderScheme, lpColorScheme, - lpHeader, lpScoreBlock, lpOutputDef, lpLayout] + contents: list[lyo.LyObject|str] = [lpVersionScheme, lpHeaderScheme, lpColorScheme, + lpHeader, lpScoreBlock, lpOutputDef, lpLayout] if scoreIn.metadata is not None: self.setHeaderFromMetadata(scoreIn.metadata, lpHeader=lpHeader) - self.context.contents = contents + self.setContextContents(contents) # ------ return Lily objects or append to the current context -----------# - def lyScoreBlockFromScore(self, scoreIn): + def lyScoreBlockFromScore(self, scoreIn: stream.Score) -> lyo.LyScoreBlock: lpCompositeMusic = lyo.LyCompositeMusic() self.newContext(lpCompositeMusic) @@ -418,7 +421,7 @@ def lyScoreBlockFromScore(self, scoreIn): return lpScoreBlock - def lyPartsAndOssiaInitFromScore(self, scoreIn): + def lyPartsAndOssiaInitFromScore(self, scoreIn: stream.Score) -> lyo.LyMusicList: r''' Takes in a score and returns a block that starts each part context and variant context with an identifier and {\stopStaff s1*n} (or s, whatever is needed for the duration) @@ -512,7 +515,7 @@ def lyPartsAndOssiaInitFromScore(self, scoreIn): ''' lpMusicList = lyo.LyMusicList() - musicList = [] + musicList: list[lyo.LyObject|str] = [] lpMusic = r'{ \stopStaff %s}' for p in scoreIn.parts: @@ -525,7 +528,7 @@ def lyPartsAndOssiaInitFromScore(self, scoreIn): music=lpMusic % spacerDuration) musicList.append(lpPrefixCompositeMusicPart) - variantsAddedForPart = [] + variantsAddedForPart: list[str] = [] for v in p.getElementsByClass(variant.Variant): variantName = v.groups[0] if variantName not in variantsAddedForPart: @@ -558,7 +561,7 @@ def lyPartsAndOssiaInitFromScore(self, scoreIn): return lpMusicList - def getLySpacersFromStream(self, streamIn, measuresOnly=True): + def getLySpacersFromStream(self, streamIn: stream.Stream, measuresOnly: bool = True) -> str: # noinspection PyShadowingNames r''' Creates a series of Spacer objects for the measures in a Stream Part. @@ -606,7 +609,11 @@ def getLySpacersFromStream(self, streamIn, measuresOnly=True): return returnString - def lyGroupedMusicListFromScoreWithParts(self, scoreIn, scoreInit=None): + def lyGroupedMusicListFromScoreWithParts( + self, + scoreIn: stream.Score, + scoreInit: lyo.LyMusicList|None = None, + ) -> lyo.LyGroupedMusicList: # noinspection PyShadowingNames,GrazieInspection r''' More complex example showing how the score can be set up with ossia parts: @@ -673,7 +680,7 @@ def lyGroupedMusicListFromScoreWithParts(self, scoreIn, scoreInit=None): ''' - compositeMusicList = [] + compositeMusicList: list[lyo.LyObject|str] = [] lpGroupedMusicList = lyo.LyGroupedMusicList() lpSimultaneousMusic = lyo.LySimultaneousMusic() @@ -699,7 +706,12 @@ def lyGroupedMusicListFromScoreWithParts(self, scoreIn, scoreInit=None): return lpGroupedMusicList - def lyNewLyricsFromStream(self, streamIn, streamId=None, alignment='alignBelowContext'): + def lyNewLyricsFromStream( + self, + streamIn: stream.Stream, + streamId: str|None = None, + alignment: str = 'alignBelowContext', + ) -> lyo.LyNewLyrics: r''' returns a LyNewLyrics object @@ -726,9 +738,9 @@ def lyNewLyricsFromStream(self, streamIn, streamId=None, alignment='alignBelowCo streamId = '#' + lyo.LyObject().quoteString(streamId) - lpGroupedMusicLists = [] + lpGroupedMusicLists: list[lyo.LyGroupedMusicList|str] = [] for lyricNum in sorted(lyricsDict): - lyricList = [] + lyricList: list[lyo.LyObject|str] = [] lpAlignmentProperty = lyo.LyPropertyOperation(mode='set', value1=alignment, value2=streamId) @@ -736,6 +748,8 @@ def lyNewLyricsFromStream(self, streamIn, streamId=None, alignment='alignBelowCo self.inWord = False for el in lyricsDict[lyricNum]: + if isinstance(el, list): # only arises from .lyrics(recurse=True) + continue lpLyricElement = self.lyLyricElementFromM21Lyric(el) lyricList.append(lpLyricElement) @@ -751,7 +765,7 @@ def lyNewLyricsFromStream(self, streamIn, streamId=None, alignment='alignBelowCo return lpNewLyrics - def lyLyricElementFromM21Lyric(self, m21Lyric): + def lyLyricElementFromM21Lyric(self, m21Lyric: note.Lyric|None) -> lyo.LyLyricElement: r''' Returns a :class:`~music21.lily.lilyObjects.LyLyricElement` object from a :class:`~music21.note.Lyric` object. @@ -778,15 +792,10 @@ def lyLyricElementFromM21Lyric(self, m21Lyric): False ''' - if hasattr(self, 'inWord'): - inWord = self.inWord - else: - inWord = False + inWord = self.inWord el = m21Lyric - if el is None and inWord: - text = ' _ ' - elif el is None and inWord is False: + if el is None: text = ' _ ' elif el.text == '': text = ' _ ' @@ -807,7 +816,11 @@ def lyLyricElementFromM21Lyric(self, m21Lyric): lpLyricElement = lyo.LyLyricElement(text) return lpLyricElement - def lySequentialMusicFromStream(self, streamIn, beforeMatter=None): + def lySequentialMusicFromStream( + self, + streamIn: stream.Stream, + beforeMatter: str|None = None, + ) -> lyo.LySequentialMusic: r''' returns a LySequentialMusic object from a stream @@ -828,7 +841,7 @@ def lySequentialMusicFromStream(self, streamIn, beforeMatter=None): } ''' - musicList = [] + musicList: list[lyo.LyObject|str] = [] lpMusicList = lyo.LyMusicList(contents=musicList) lpSequentialMusic = lyo.LySequentialMusic(musicList=lpMusicList, @@ -846,11 +859,11 @@ def lySequentialMusicFromStream(self, streamIn, beforeMatter=None): # pylint: disable=redefined-builtin def lyPrefixCompositeMusicFromStream( self, - streamIn, - contextType=None, - type=None, - beforeMatter=None - ): + streamIn: stream.Stream, + contextType: str|None = None, + type: str|None = None, + beforeMatter: str|None = None, + ) -> lyo.LyPrefixCompositeMusic: # noinspection PyShadowingNames r''' returns an LyPrefixCompositeMusic object from @@ -882,7 +895,7 @@ def lyPrefixCompositeMusicFromStream( compositeMusicType = type optionalId = None - contextModList = [] + contextModList: list[str] = [] c = streamIn.classes if contextType is None: @@ -916,10 +929,7 @@ def lyPrefixCompositeMusicFromStream( if compositeMusicType is None: compositeMusicType = 'new' - if contextModList: - contextMod = lyo.LyContextModification(contextModList) - else: - contextMod = None + contextMod = lyo.LyContextModification(contextModList) if contextModList else None lpPrefixCompositeMusic = lyo.LyPrefixCompositeMusic(type=compositeMusicType, optionalId=optionalId, @@ -928,7 +938,7 @@ def lyPrefixCompositeMusicFromStream( music=lpMusic) return lpPrefixCompositeMusic - def appendObjectsToContextFromStream(self, streamObject): + def appendObjectsToContextFromStream(self, streamObject: stream.Stream) -> None: r''' takes a Stream and appends all the elements in it to the current context's .contents list, and deals with creating Voices in it. It also deals with @@ -975,18 +985,18 @@ def appendObjectsToContextFromStream(self, streamObject): >> ''' from music21.stream.iterator import OffsetIterator - for groupedElements in OffsetIterator(streamObject): + for groupedElements in OffsetIterator[base.Music21Object](streamObject): # print(groupedElements) if len(groupedElements) == 1: # one thing at that moment - el = groupedElements[0] - el.activeSite = streamObject - self.appendM21ObjectToContext(el) + soleElement = groupedElements[0] + soleElement.activeSite = streamObject + self.appendM21ObjectToContext(soleElement) else: # voices or other More than one thing at once # if voices - voiceList = [] - variantList = [] - otherList = [] + voiceList: list[stream.Voice] = [] + variantList: list[variant.Variant] = [] + otherList: list[base.Music21Object] = [] for el in groupedElements: if isinstance(el, stream.Voice): voiceList.append(el) @@ -1004,7 +1014,7 @@ def appendObjectsToContextFromStream(self, streamObject): coloredVariants=self.coloredVariants) if voiceList: - musicList2 = [] + musicList2: list[lyo.LyObject|str] = [] lp2GroupedMusicList = lyo.LyGroupedMusicList() lp2SimultaneousMusic = lyo.LySimultaneousMusic() lp2MusicList = lyo.LyMusicList() @@ -1018,8 +1028,7 @@ def appendObjectsToContextFromStream(self, streamObject): lp2MusicList.contents = musicList2 - contextObject = self.context - currentMusicList = contextObject.contents + currentMusicList = self.contextContents() currentMusicList.append(lp2GroupedMusicList) lp2GroupedMusicList.setParent(self.context) @@ -1027,7 +1036,7 @@ def appendObjectsToContextFromStream(self, streamObject): for el in otherList: self.appendM21ObjectToContext(el) - def appendM21ObjectToContext(self, thisObject): + def appendM21ObjectToContext(self, thisObject: base.Music21Object) -> None: r''' converts any type of object into a lilyObject of LyMusic ( LySimpleMusic, LyEmbeddedScm etc.) type @@ -1036,28 +1045,22 @@ def appendM21ObjectToContext(self, thisObject): return # treat complex duration objects as multiple objects - c = thisObject.classes - - if 'Stream' not in c and thisObject.duration.type == 'complex': + if not isinstance(thisObject, stream.Stream) and thisObject.duration.type == 'complex': thisObjectSplit = thisObject.splitAtDurations() for subComponent in thisObjectSplit: self.appendM21ObjectToContext(subComponent) return contextObject = self.context - if hasattr(contextObject, 'contents'): - currentMusicList = contextObject.contents - else: # pragma: no cover - raise LilyTranslateException( - f'Cannot get a currentMusicList from contextObject {contextObject!r}') + currentMusicList = self.contextContents() - if hasattr(thisObject, 'startTransparency') and thisObject.startTransparency is True: + if getattr(thisObject, 'startTransparency', False) is True: # old hack, replace with the better "hidden" attribute lyScheme = lyo.LyEmbeddedScm(self.transparencyStartScheme) currentMusicList.append(lyScheme) - lyObject = None - if 'Measure' in c: + lyObject: lyo.LyObject|None = None + if isinstance(thisObject, stream.Measure): # lilypond does not put groups around measures # it does however need barline ends # also, if variantMode is True, the last note in each "measure" should have \noBeam @@ -1075,46 +1078,43 @@ def appendM21ObjectToContext(self, thisObject): self.appendObjectsToContextFromStream(thisObject) self.currentMeasure = thisObject - elif 'Stream' in c: - # try: + elif isinstance(thisObject, stream.Stream): lyObject = self.lyPrefixCompositeMusicFromStream(thisObject) currentMusicList.append(lyObject) lyObject.setParent(contextObject) - elif 'Note' in c or 'Rest' in c: + elif isinstance(thisObject, (note.Note, note.Rest)): self.appendContextFromNoteOrRest(thisObject) - elif 'Chord' in c: + elif isinstance(thisObject, chord.Chord): self.appendContextFromChord(thisObject) - elif 'Clef' in c: + elif isinstance(thisObject, clef.Clef): lyObject = self.lyEmbeddedScmFromClef(thisObject) currentMusicList.append(lyObject) lyObject.setParent(contextObject) - elif 'KeySignature' in c: + elif isinstance(thisObject, key.KeySignature): lyObject = self.lyEmbeddedScmFromKeySignature(thisObject) currentMusicList.append(lyObject) lyObject.setParent(contextObject) - elif 'TimeSignature' in c and self.variantMode is False: + elif isinstance(thisObject, meter.TimeSignature) and self.variantMode is False: lyObject = self.lyEmbeddedScmFromTimeSignature(thisObject) currentMusicList.append(lyObject) lyObject.setParent(contextObject) - elif 'Variant' in c: + elif isinstance(thisObject, variant.Variant): self.appendContextFromVariant(thisObject, coloredVariants=self.coloredVariants) - elif 'SystemLayout' in c: + elif isinstance(thisObject, layout.SystemLayout): lyObject = lyo.LyEmbeddedScm(r'\break') currentMusicList.append(lyObject) lyObject.setParent(contextObject) - elif 'PageLayout' in c: + elif isinstance(thisObject, layout.PageLayout): lyObject = lyo.LyEmbeddedScm(r'\pageBreak') currentMusicList.append(lyObject) lyObject.setParent(contextObject) - else: - lyObject = None - if hasattr(thisObject, 'stopTransparency') and thisObject.stopTransparency is True: + if getattr(thisObject, 'stopTransparency', False) is True: # old hack, replace with the better "hidden" attribute lyScheme = lyo.LyEmbeddedScm(self.transparencyStopScheme) currentMusicList.append(lyScheme) - def appendContextFromNoteOrRest(self, noteOrRest): + def appendContextFromNoteOrRest(self, noteOrRest: note.GeneralNote) -> None: r''' appends lySimpleMusicFromNoteOrRest to the current context. @@ -1175,11 +1175,11 @@ def appendContextFromNoteOrRest(self, noteOrRest): self.appendStemCode(noteOrRest) lpSimpleMusic = self.lySimpleMusicFromNoteOrRest(noteOrRest) - self.context.contents.append(lpSimpleMusic) + self.contextContents().append(lpSimpleMusic) lpSimpleMusic.setParent(self.context) self.setContextForTupletStop(noteOrRest) - def appendContextFromChord(self, chord): + def appendContextFromChord(self, chordIn: chord.Chord) -> None: r''' appends lySimpleMusicFromChord to the current context. @@ -1220,16 +1220,34 @@ def appendContextFromChord(self, chord): ''' - self.setContextForTupletStart(chord) - self.appendBeamCode(chord) - self.appendStemCode(chord) + self.setContextForTupletStart(chordIn) + self.appendBeamCode(chordIn) + self.appendStemCode(chordIn) - lpSimpleMusic = self.lySimpleMusicFromChord(chord) - self.context.contents.append(lpSimpleMusic) + lpSimpleMusic = self.lySimpleMusicFromChord(chordIn) + self.contextContents().append(lpSimpleMusic) lpSimpleMusic.setParent(self.context) - self.setContextForTupletStop(chord) + self.setContextForTupletStop(chordIn) - def lySimpleMusicFromNoteOrRest(self, noteOrRest): + def contextContents(self) -> list[lyo.LyObject|str]: + ''' + The `.contents` list of the current context; raises a + LilyTranslateException if the context cannot hold contents. + ''' + contents: t.Any = getattr(self.context, 'contents', None) + if contents is None: # pragma: no cover + raise LilyTranslateException( + f'Cannot get a currentMusicList from contextObject {self.context!r}') + return contents + + def setContextContents(self, contents: list[lyo.LyObject|str]) -> None: + ''' + Replace the `.contents` list of the current context, reparenting its members. + ''' + context: t.Any = self.context + context.contents = contents + + def lySimpleMusicFromNoteOrRest(self, noteOrRest: note.GeneralNote) -> lyo.LySimpleMusic: r''' returns a lilyObjects.LySimpleMusic object for the generalNote containing this hierarchy:: @@ -1267,9 +1285,7 @@ def lySimpleMusicFromNoteOrRest(self, noteOrRest): >>> print(sm) s 4 ''' - c = noteOrRest.classes - - simpleElementParts = [] + simpleElementParts: list[lyo.LyObject|str] = [] # https://lilypond.org/doc/v2.22/Documentation/notation/inside-the-staff#coloring-objects if noteOrRest.hasStyleInformation: @@ -1280,18 +1296,18 @@ def lySimpleMusicFromNoteOrRest(self, noteOrRest): simpleElementParts.append(noteheadColor) simpleElementParts.append(stemColor) - if 'Note' in c: + if isinstance(noteOrRest, note.Note): if not noteOrRest.hasStyleInformation or noteOrRest.style.hideObjectOnPrint is False: - lpPitch = self.lyPitchFromPitch(noteOrRest.pitch) - simpleElementParts.append(lpPitch) - if noteOrRest.pitch.accidental is not None: - if noteOrRest.pitch.accidental.displayType == 'always': + notePitch = noteOrRest.pitch + simpleElementParts.append(self.lyPitchFromPitch(notePitch)) + if notePitch.accidental is not None: + if notePitch.accidental.displayType == 'always': simpleElementParts.append('! ') - if noteOrRest.pitch.accidental.displayStyle == 'parentheses': + if notePitch.accidental.displayStyle == 'parentheses': simpleElementParts.append('? ') else: simpleElementParts.append('s ') - elif 'Rest' in c: + elif isinstance(noteOrRest, note.Rest): if noteOrRest.hasStyleInformation and noteOrRest.style.hideObjectOnPrint: simpleElementParts.append('s ') else: @@ -1315,7 +1331,7 @@ def lySimpleMusicFromNoteOrRest(self, noteOrRest): return mlSM - def appendBeamCode(self, noteOrChord): + def appendBeamCode(self, noteOrChord: note.GeneralNote) -> None: r''' Adds an LyEmbeddedScm object to the context's contents if the object's has a .beams attribute. @@ -1368,16 +1384,16 @@ def appendBeamCode(self, noteOrChord): if leftBeams > 0: beamText = rf'''\set stemLeftBeamCount = #{leftBeams}''' lpBeamScheme = lyo.LyEmbeddedScm(beamText) - self.context.contents.append(lpBeamScheme) + self.contextContents().append(lpBeamScheme) lpBeamScheme.setParent(self.context) if rightBeams > 0: beamText = fr'''\set stemRightBeamCount = #{rightBeams}''' lpBeamScheme = lyo.LyEmbeddedScm(beamText) - self.context.contents.append(lpBeamScheme) + self.contextContents().append(lpBeamScheme) lpBeamScheme.setParent(self.context) - def appendStemCode(self, noteOrChord): + def appendStemCode(self, noteOrChord: note.GeneralNote) -> None: r''' Adds an LyEmbeddedScm object to the context's contents if the object's stem direction is set (currently, only "up" and "down" are supported). @@ -1400,10 +1416,10 @@ def appendStemCode(self, noteOrChord): if stemDirection in ['UP', 'DOWN']: stemFile = fr'''\once \override Stem.direction = #{stemDirection} ''' lpStemScheme = lyo.LyEmbeddedScm(stemFile) - self.context.contents.append(lpStemScheme) + self.contextContents().append(lpStemScheme) lpStemScheme.setParent(self.context) - def lySimpleMusicFromChord(self, chordObj): + def lySimpleMusicFromChord(self, chordObj: chord.Chord) -> lyo.LySimpleMusic: ''' >>> conv = lily.translate.LilypondConverter() @@ -1419,6 +1435,7 @@ def lySimpleMusicFromChord(self, chordObj): >>> print(conv.lySimpleMusicFromChord(c1)) s 2.. ''' + lpChordBody: lyo.LyChordBody|lyo.LyPitch self.appendBeamCode(chordObj) if not chordObj.hasStyleInformation or chordObj.style.hideObjectOnPrint is not True: @@ -1426,7 +1443,7 @@ def lySimpleMusicFromChord(self, chordObj): chordBodyElements = [] for p in chordObj.pitches: - chordBodyElementParts = [] + chordBodyElementParts: list[lyo.LyObject|str] = [] lpPitch = self.lyPitchFromPitch(p) chordBodyElementParts.append(lpPitch) if p.accidental is not None: @@ -1452,12 +1469,12 @@ def lySimpleMusicFromChord(self, chordObj): return mlSM # TODO: Chord beaming - def postEventsFromObject(self, generalNote): + def postEventsFromObject(self, generalNote: note.GeneralNote) -> list[lyo.LyObject|str]: r''' attaches events that apply to notes and chords (and some other things) equally ''' - postEvents = [] + postEvents: list[lyo.LyObject|str] = [] # remove this hack once lyrics work # if generalNote.lyric is not None: # hack that uses markup @@ -1474,35 +1491,35 @@ def postEventsFromObject(self, generalNote): postEvents.append(r'\fermata ') return postEvents - def lyPitchFromPitch(self, pitch): + def lyPitchFromPitch(self, pitchObj: pitch.Pitch) -> lyo.LyPitch: r''' converts a music21.pitch.Pitch object to a lily.lilyObjects.LyPitch object. ''' - baseName = self.baseNameFromPitch(pitch) - octaveModChars = self.octaveCharactersFromPitch(pitch) + baseName = self.baseNameFromPitch(pitchObj) + octaveModChars = self.octaveCharactersFromPitch(pitchObj) lyPitch = lyo.LyPitch(baseName, octaveModChars) return lyPitch - def baseNameFromPitch(self, pitch): + def baseNameFromPitch(self, pitchObj: pitch.Pitch) -> str: r''' returns a string of the base name (including accidental) for a music21 pitch ''' - baseName = pitch.step.lower() - if pitch.accidental is not None: - if pitch.accidental.name in self.accidentalConvert: - baseName += self.accidentalConvert[pitch.accidental.name] + baseName = pitchObj.step.lower() + if pitchObj.accidental is not None: + if pitchObj.accidental.name in self.accidentalConvert: + baseName += self.accidentalConvert[pitchObj.accidental.name] return baseName - def octaveCharactersFromPitch(self, pitch): + def octaveCharactersFromPitch(self, pitchObj: pitch.Pitch) -> str: r''' returns a string of single-quotes or commas or '' representing the octave of a :class:`~music21.pitch.Pitch` object ''' - implicitOctave = pitch.implicitOctave + implicitOctave = pitchObj.implicitOctave if implicitOctave < 3: correctedOctave = 3 - implicitOctave octaveModChars = ',' * correctedOctave # C2 = c, C1 = c,, @@ -1514,7 +1531,7 @@ def octaveCharactersFromPitch(self, pitch): def lyMultipliedDurationFromDuration( self, durationObj: duration.Duration|duration.DurationTuple, - ): + ) -> lyo.LyMultipliedDuration: r''' take a simple Duration (that is, one with one DurationTuple) object and return a LyMultipliedDuration object: @@ -1581,7 +1598,7 @@ def lyMultipliedDurationFromDuration( f'DurationException: Cannot translate durationObject {durationObj}: {de}') return multipliedDuration - def lyEmbeddedScmFromClef(self, clefObj): + def lyEmbeddedScmFromClef(self, clefObj: clef.Clef) -> lyo.LyEmbeddedScm: # noinspection PyShadowingNames r''' converts a Clef object to a @@ -1627,7 +1644,7 @@ def lyEmbeddedScmFromClef(self, clefObj): lpEmbeddedScm.content = clefScheme return lpEmbeddedScm - def lyEmbeddedScmFromKeySignature(self, keyObj): + def lyEmbeddedScmFromKeySignature(self, keyObj: key.KeySignature) -> lyo.LyEmbeddedScm: # noinspection PyShadowingNames r''' converts a Key or KeySignature object @@ -1646,11 +1663,10 @@ def lyEmbeddedScmFromKeySignature(self, keyObj): \key fis \major ''' - if not isinstance(keyObj, key.Key): - keyObj = keyObj.asKey('major') + keyAsKey = keyObj if isinstance(keyObj, key.Key) else keyObj.asKey('major') - p = keyObj.tonic - m = keyObj.mode + p = keyAsKey.tonic + m = keyAsKey.mode pn = self.baseNameFromPitch(p) @@ -1664,7 +1680,7 @@ def lyEmbeddedScmFromKeySignature(self, keyObj): lpEmbeddedScm.content = keyScheme return lpEmbeddedScm - def lyEmbeddedScmFromTimeSignature(self, ts): + def lyEmbeddedScmFromTimeSignature(self, ts: meter.TimeSignature) -> lyo.LyEmbeddedScm: # noinspection PyShadowingNames r''' convert a :class:`~music21.meter.TimeSignature` object @@ -1680,7 +1696,7 @@ def lyEmbeddedScmFromTimeSignature(self, ts): lpEmbeddedScm.content = keyScheme return lpEmbeddedScm - def setContextForTupletStart(self, inObj): + def setContextForTupletStart(self, inObj: base.Music21Object) -> lyo.LyMusicList|None: r''' if the inObj has tuplets then we set a new context for the tuplets and anything up till a tuplet stop. @@ -1706,7 +1722,11 @@ def setContextForTupletStart(self, inObj): else: return None - def setContextForTimeFraction(self, numerator, denominator): + def setContextForTimeFraction( + self, + numerator: int|str, + denominator: int|str, + ) -> lyo.LyMusicList: r''' Explicitly starts a new context for scaled music (tuplets, etc.) for the given numerator and denominator (either an int or a string or unicode) @@ -1749,17 +1769,13 @@ def setContextForTimeFraction(self, numerator, denominator): lpPrefixCompositeMusic = lyo.LyPrefixCompositeMusic(type='times', fraction=fraction, music=lpSequentialMusic) - currentContents = self.context.contents - if currentContents is None: # pragma: no cover - raise LilyTranslateException( - f'Cannot find contents for self.context: {self.context!r} ') - + currentContents = self.contextContents() currentContents.append(lpPrefixCompositeMusic) lpPrefixCompositeMusic.setParent(self.context) self.newContext(lpMusicList) return lpMusicList - def setContextForTupletStop(self, inObj): + def setContextForTupletStop(self, inObj: base.Music21Object) -> None: r''' Reverse of setContextForTupletStart ''' @@ -1767,15 +1783,18 @@ def setContextForTupletStop(self, inObj): return elif inObj.duration.tuplets[0].type == 'stop': self.restoreContext() - else: - return None - def appendContextFromVariant(self, variantObjectOrList, activeSite=None, coloredVariants=False): + def appendContextFromVariant( + self, + variantObjectOrList: variant.Variant|list[variant.Variant], + activeSite: stream.Stream|None = None, + coloredVariants: bool = False, + ) -> None: r''' Create a new context from the variant object or a list of variants and append. ''' - musicList = [] - longestReplacedElements = [] + musicList: list[lyo.LyObject|str] = [] + longestReplacedElements: stream.Stream = stream.Stream() if isinstance(variantObjectOrList, variant.Variant): variantObject = variantObjectOrList @@ -1788,8 +1807,8 @@ def appendContextFromVariant(self, variantObjectOrList, activeSite=None, colored musicList.append(lpSequentialMusicStandard) elif isinstance(variantObjectOrList, list): - longestReplacementLength = -1 - variantDict = {} + longestReplacementLength = -1.0 + variantDict: dict[str, list[variant.Variant]] = {} for variantObject in variantObjectOrList: if variantObject.groups: variantName = variantObject.groups[0] @@ -1831,15 +1850,16 @@ def appendContextFromVariant(self, variantObjectOrList, activeSite=None, colored lp2GroupedMusicList = lyo.LyGroupedMusicList() lp2GroupedMusicList.simultaneousMusic = lp2SimultaneousMusic - contextObject = self.context - currentMusicList = contextObject.contents + currentMusicList = self.contextContents() currentMusicList.append(lp2GroupedMusicList) lp2GroupedMusicList.setParent(self.context) - def lyPrefixCompositeMusicFromRelatedVariants(self, - variantList, - activeSite=None, - coloredVariants=False): + def lyPrefixCompositeMusicFromRelatedVariants( + self, + variantList: list[variant.Variant], + activeSite: stream.Stream|None = None, + coloredVariants: bool = False, + ) -> tuple[lyo.LyPrefixCompositeMusic, stream.Stream]: # noinspection PyShadowingNames r''' @@ -1939,12 +1959,13 @@ def lyPrefixCompositeMusicFromRelatedVariants(self, # Order List - def findOffsetOfFirstNonSpacerElement(inputStream): + def findOffsetOfFirstNonSpacerElement(inputStream: stream.Stream) -> float: for el in inputStream: if isinstance(el, note.Rest) and el.style.hideObjectOnPrint: pass else: - return inputStream.elementOffset(el) + return float(inputStream.elementOffset(el)) + return 0.0 variantList.sort(key=lambda vv: findOffsetOfFirstNonSpacerElement(vv._stream)) @@ -1953,9 +1974,11 @@ def findOffsetOfFirstNonSpacerElement(inputStream): re0 = replacedElements[0] replacedElementsClef = re0.clef or re0.getContextByClass(clef.Clef) - variantContainerStream = variantList[0].getContextByClass(stream.Part) + variantContainerStream: stream.Stream|None = variantList[0].getContextByClass(stream.Part) if variantContainerStream is None: - variantContainerStream = variantList[0].getContextByClass('Stream') + variantContainerStream = variantList[0].getContextByClass(stream.Stream) + if variantContainerStream is None: # pragma: no cover + raise LilyTranslateException(f'Cannot find a container stream for {variantList[0]}') variantList[0].insert(0.0, replacedElementsClef) variantName = variantList[0].groups[0] @@ -1975,9 +1998,9 @@ def findOffsetOfFirstNonSpacerElement(inputStream): ####################### - musicList = [] + musicList: list[lyo.LyObject|str] = [] highestOffsetSoFar = 0.0 - longestVariant = None + longestVariant = variantList[-1] self.variantMode = True @@ -2018,6 +2041,7 @@ def findOffsetOfFirstNonSpacerElement(inputStream): replacedElementsLength = vStripped.replacementQuarterLength variantLength = vStripped.containedHighestTime - firstOffset + lpOssiaMusicVariant: lyo.LyObject if variantLength != replacedElementsLength: numerator, denominator = common.decimalToTuplet( replacedElementsLength / variantLength) @@ -2055,10 +2079,12 @@ def findOffsetOfFirstNonSpacerElement(inputStream): return lpPrefixCompositeMusicVariant, replacedElements - def lyPrefixCompositeMusicFromVariant(self, - variantObject, - replacedElements, - coloredVariants=False): + def lyPrefixCompositeMusicFromVariant( + self, + variantObject: variant.Variant, + replacedElements: stream.Stream, + coloredVariants: bool = False, + ) -> lyo.LyPrefixCompositeMusic: # noinspection PyShadowingNames r''' @@ -2100,9 +2126,11 @@ def lyPrefixCompositeMusicFromVariant(self, ''' replacedElementsClef = replacedElements[0].getContextByClass(clef.Clef) - variantContainerStream = variantObject.getContextByClass(stream.Part) + variantContainerStream: stream.Stream|None = variantObject.getContextByClass(stream.Part) if variantContainerStream is None: - variantContainerStream = variantObject.getContextByClass('Stream') + variantContainerStream = variantObject.getContextByClass(stream.Stream) + if variantContainerStream is None: # pragma: no cover + raise LilyTranslateException(f'Cannot find a container stream for {variantObject}') if replacedElementsClef is not None: if replacedElementsClef not in variantObject.elements: @@ -2126,7 +2154,7 @@ def lyPrefixCompositeMusicFromVariant(self, for n in variantObject._stream.recurse().notesAndRests: n.style.color = color - musicList = [] + musicList: list[lyo.LyObject|str] = [] varFilter = [r for r in variantObject.getElementsByClass(note.Rest) if r.style.hideObjectOnPrint] @@ -2210,7 +2238,7 @@ def lyPrefixCompositeMusicFromVariant(self, # currentMusicList.append(lp2GroupedMusicList) # lp2GroupedMusicList.setParent(self.context) - def lyOssiaMusicFromVariant(self, variantIn): + def lyOssiaMusicFromVariant(self, variantIn: variant.Variant) -> lyo.LyOssiaMusic: r''' returns a LyOssiaMusic object from a stream @@ -2232,7 +2260,7 @@ def lyOssiaMusicFromVariant(self, variantIn): } ''' - musicList = [] + musicList: list[lyo.LyObject|str] = [] lpMusicList = lyo.LyMusicList(contents=musicList) lpOssiaMusic = lyo.LyOssiaMusic(musicList=lpMusicList) @@ -2251,7 +2279,11 @@ def lyOssiaMusicFromVariant(self, variantIn): return lpOssiaMusic - def setHeaderFromMetadata(self, metadataObject=None, lpHeader=None): + def setHeaderFromMetadata( + self, + metadataObject: metadata.Metadata|None = None, + lpHeader: lyo.LyLilypondHeader|None = None, + ) -> lyo.LyLilypondHeader: # noinspection PyShadowingNames r''' Returns a lilypond.lilyObjects.LyLilypondHeader object @@ -2302,7 +2334,7 @@ def setHeaderFromMetadata(self, metadataObject=None, lpHeader=None): lpHeaderBody.assignments = lpHeaderBodyAssignments return lpHeader - def closeMeasure(self, barChecksOnly=False): + def closeMeasure(self, barChecksOnly: bool = False) -> lyo.LyEmbeddedScm|None: # noinspection PyShadowingNames r''' return a LyObject or None for the end of the previous Measure @@ -2348,7 +2380,7 @@ def closeMeasure(self, barChecksOnly=False): lpBarline.content = barString return lpBarline - def getSchemeForPadding(self, measureObject): + def getSchemeForPadding(self, measureObject: stream.Measure) -> lyo.LyEmbeddedScm|None: r''' lilypond partial durations are very strange and are really of type LyMultipliedDuration. You notate how many @@ -2386,7 +2418,7 @@ def getSchemeForPadding(self, measureObject): return lyObject # -------------display and converter routines ---------------------# - def writeLyFile(self, ext='', fp=None): + def writeLyFile(self, ext: str = '', fp: str|pathlib.Path|None = None) -> pathlib.Path: r''' writes the contents of the self.topLevelObject to a file. @@ -2406,8 +2438,13 @@ def writeLyFile(self, ext='', fp=None): return self.tempName # noinspection PyShadowingBuiltins - def runThroughLily(self, format=None, - backend=None, fileName=None, skipWriting=False): + def runThroughLily( + self, + format: str, + backend: str|None = None, + fileName: str|pathlib.Path|None = None, + skipWriting: bool = False, + ) -> pathlib.Path: r''' creates a .ly file from self.topLevelObject via .writeLyFile then runs the file through LilyPond. @@ -2449,7 +2486,7 @@ def runThroughLily(self, format=None, fileForm = fileEnd return pathlib.Path(fileForm) - def createPDF(self, fileName=None): + def createPDF(self, fileName: str|pathlib.Path|None = None) -> pathlib.Path: r''' create a PDF file from self.topLevelObject and return the filepath of the file. @@ -2459,7 +2496,7 @@ def createPDF(self, fileName=None): lilyFile = self.runThroughLily(backend='ps', format='pdf', fileName=fileName) return lilyFile - def showPDF(self): + def showPDF(self) -> None: r''' create an SVG file from self.topLevelObject, show it with your pdf reader (often Adobe Acrobat/Adobe Reader or Apple Preview) @@ -2479,7 +2516,7 @@ def showPDF(self): command = '' os.system(command) - def createPNG(self, fileName=None): + def createPNG(self, fileName: str|pathlib.Path|None = None) -> pathlib.Path: r''' create a PNG file from self.topLevelObject and return the filepath of the file. @@ -2500,7 +2537,7 @@ def createPNG(self, fileName=None): pass # no big deal probably return lilyFile - def showPNG(self): + def showPNG(self) -> t.Any: r''' Take the object, run it through LilyPond, and then show it as a PNG file. On Windows, the PNG file will not be deleted, so you will need to clean out @@ -2515,7 +2552,7 @@ def showPNG(self): # self.showImageDirect(lilyFile) return SubConverter().launch(lilyFile, fmt='png') - def createSVG(self, fileName=None): + def createSVG(self, fileName: str|pathlib.Path|None = None) -> pathlib.Path: r''' create an SVG file from self.topLevelObject and return the filepath of the file. @@ -2525,7 +2562,7 @@ def createSVG(self, fileName=None): lilyFile = self.runThroughLily(format='svg', backend='svg', fileName=fileName) return lilyFile - def showSVG(self, fileName=None): + def showSVG(self, fileName: str|pathlib.Path|None = None) -> t.Any: r''' create an SVG file from self.topLevelObject, show it with your svg reader (often Internet Explorer on PC) @@ -2551,7 +2588,6 @@ def testExplicitConvertChorale(self): # print(lpc.topLevelObject) def testComplexDuration(self): - from music21 import meter s = stream.Stream() n1 = note.Note('C') # test no octave also! n1.duration.quarterLength = 2.5 # BUG 2.3333333333 doesn't work right @@ -2617,7 +2653,6 @@ def xtestSlowConvertOpus(self): fifeOpus.show('lily.png') def xtestBreve(self): - from music21 import meter n = note.Note('C5') n.duration.quarterLength = 8.0 m = stream.Measure() From 3d839a8858bdac366fec9f4b66a41b3bbdb9c43c Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 16:04:02 -1000 Subject: [PATCH 047/312] Address review: move "New in v11" to end of docstring; fix lint pylint flagged the test-local `from music21 import key` as a reimport of the module-level import (W0404/W0621); `meter` is not imported at module level, so that one stays. AI-assisted (Claude) --- music21/lily/translate.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/music21/lily/translate.py b/music21/lily/translate.py index 9f307aaa4e..4105e87fd2 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -1692,9 +1692,6 @@ def lyEmbeddedScmFromMetronomeMark(self, mm: tempo.MetronomeMark) -> lyo.LyEmbed convert a :class:`~music21.tempo.MetronomeMark` object to a lilyObjects.LyEmbeddedScm object - * New in v11: MetronomeMark objects are now written out when - converting a Stream to Lilypond; previously they were silently dropped. - >>> mm = tempo.MetronomeMark(number=87, referent=note.Note(type='quarter')) >>> conv = lily.translate.LilypondConverter() >>> print(conv.lyEmbeddedScmFromMetronomeMark(mm)) @@ -1707,6 +1704,9 @@ def lyEmbeddedScmFromMetronomeMark(self, mm: tempo.MetronomeMark) -> lyo.LyEmbed True >>> conv.lyEmbeddedScmFromMetronomeMark(mm) is None True + + * New in v11: MetronomeMark objects are now written out when + converting a Stream to LilyPond; previously they were silently dropped. ''' if mm.number is None: return None @@ -2645,7 +2645,6 @@ def testMetronomeMark(self): def testMetronomeMarkWrittenInStream(self): # https://github.com/cuthbertLab/music21/issues/1852 - from music21 import key from music21 import meter keysig = key.Key('a-') mm = tempo.MetronomeMark(number=87, referent=note.Note(type='quarter')) From 61a51ff089a0da3113f65095e352354052056df1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 16:30:37 -1000 Subject: [PATCH 048/312] Trim the module docstring; note where the grammar lives now AI-assisted (Claude) --- music21/lily/lilyObjects.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py index 0a1af94602..62d42e53c7 100644 --- a/music21/lily/lilyObjects.py +++ b/music21/lily/lilyObjects.py @@ -13,22 +13,12 @@ local computer, can automatically generate .pdf, .png, and .svg versions of musical files using LilyPond. -The class hierarchy mirrors the LilyPond grammar as published in -http://lilypond.org/doc/v2.14/Documentation/notation/lilypond-grammar - -**Targeted LilyPond version: 2.24 (December 2022).** - -Output is written for LilyPond 2.24 and checked against it. LilyPond's input -syntax changes between stable releases, so a construct taken from the v2.14 -grammar above is not necessarily still valid: `\\markuplines` became -`\\markuplist` in 2.16, tempo ranges moved from `70~100` to `70-100` in 2.18, -and most bar line names were reworked in 2.18 and again in 2.23. When adding -or changing output, check it against a real LilyPond 2.24 run, and use -LilyPond's own `convert-ly` (its `python/convertrules.py` is the authoritative -list of syntax changes) to find anything left over from an older grammar. - -The policy is to support back roughly four years from a music21 release, so -raise this target when 2.24 falls outside that window. +The class hierarchy mirrors the grammar in +https://lilypond.org/doc/v2.14/Documentation/notation/lilypond-grammar +(the last LilyPond to publish one is v2.19; since then it is `lily/parser.yy`). + +Output is written for LilyPond 2.24 and checked against it. Aim to support +3-4 years of LilyPond. ''' from __future__ import annotations From f556d1ac6520b81417c7eca13e9864c3037a6d5e Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 16:37:01 -1000 Subject: [PATCH 049/312] Cut history and prior-bug notes from the docs Version markers keep only what a user acts on. AI-assisted (Claude) --- music21/lily/lilyObjects.py | 15 +++++---------- music21/lily/translate.py | 14 +++++--------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py index 62d42e53c7..e91d3eb7ef 100644 --- a/music21/lily/lilyObjects.py +++ b/music21/lily/lilyObjects.py @@ -46,8 +46,7 @@ class LyObject(prebase.ProtoM21Object): >>> lyo.stringOutput() '' - * Changed in v11: `stringOutput()` always returns a `str`; the subclasses that - returned None for empty contents now return `''`. + * Changed in v11: `stringOutput()` always returns a `str`, never None. ''' supportedClasses: list[str] = [] # ordered list of classes to support m21toLy: dict[str, dict[str, str]] = {} @@ -1355,10 +1354,7 @@ class LyPrefixCompositeMusic(LyObject): modeChanging, modeChangingWith, relative, rhythmed - The 'tuplet' fraction is the LilyPond `\tuplet` fraction, actual/normal: - `3/2` means three notes in the time of two. (The v2.14 grammar below - spells this `\times`, whose fraction is the inverse; `\tuplet` replaced it - in LilyPond 2.18.) + The 'tuplet' fraction is actual/normal: `3/2` is three notes in the time of two. prefix_composite_music: generic_prefix_music_scm | "\context" @@ -1468,7 +1464,7 @@ class LyModeChangingHead(LyObject): >>> print(l2.stringOutput()) \chords - 'note' has no context-creating shorthand in LilyPond, so it always gives `\notemode`: + Mode 'note' always gives `\notemode`: >>> l3 = lily.lilyObjects.LyModeChangingHead(hasContext=False, mode='note') >>> print(l3.stringOutput()) @@ -1858,7 +1854,7 @@ def stringOutput(self) -> str: argOut = arg.stringOutput() if isinstance(arg, LyObject) else str(arg) return self.backslash + ct + ' ' + argOut elif ct == '[': - # \[ and \] are ligature brackets; manual beams are plain [ and ] + # ligature brackets; manual beams are plain [ and ] return self.backslash + '[ ' elif ct == ']': return self.backslash + '] ' @@ -1884,7 +1880,7 @@ def __init__(self, def stringOutput(self) -> str: ct = self.commandType if ct == '~': - # E_TILDE: the pes-or-flexa ligature event, not a tie (a tie is a bare ~) + # E_TILDE, the pes-or-flexa ligature event return self.backslash + '~ ' elif ct == 'mark-default': return self.backslash + 'mark ' + self.backslash + 'default ' @@ -2242,7 +2238,6 @@ def stringOutput(self) -> str: elif mli is None: # pragma: no cover raise LilyObjectsException('need a markup list or identifier') else: - # \markuplines was renamed \markuplist in LilyPond 2.16 return self.backslash + 'markuplist ' + mli.stringOutput() diff --git a/music21/lily/translate.py b/music21/lily/translate.py index 46dac07eb5..35e87bed4b 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -141,8 +141,7 @@ class LilypondConverter: 'half-flat': 'eh', } - # bar line names as defined by LilyPond 2.18 and later; unknown names - # are silently drawn as nothing. + # bar line names as defined in LilyPond's scm/lily/bar-line.scm barlineDict = {'regular': '|', 'dotted': ';', 'dashed': '!', @@ -1716,8 +1715,7 @@ def lyEmbeddedScmFromMetronomeMark(self, mm: tempo.MetronomeMark) -> lyo.LyEmbed >>> conv.lyEmbeddedScmFromMetronomeMark(mm) is None True - * New in v11: MetronomeMark objects are now written out when - converting a Stream to LilyPond; previously they were silently dropped. + * New in v11. ''' if mm.number is None: return None @@ -1762,8 +1760,8 @@ def setContextForTimeFraction( ) -> lyo.LyMusicList: r''' Explicitly starts a new context for scaled music (tuplets, etc.) for the - given LilyPond `\tuplet` fraction, actual/normal: 5/4 means five notes in - the time of four. Either part may be an int or a string. + given fraction, actual/normal: 5/4 is five notes in the time of four. + Either part may be an int or a string. Returns an lpMusicList object contained in an lpSequentialMusic object in an lpPrefixCompositeMusic object which sets the tuplet to a particular @@ -1793,9 +1791,7 @@ def setContextForTimeFraction( >>> lpc.context.getParent().getParent().getParent() is lyTop True - * Changed in v11: emits `\tuplet actual/normal` rather than the - `\times normal/actual` removed after LilyPond 2.16; the arguments - are correspondingly swapped. + * Changed in v11: emits `\tuplet`; the arguments are now actual, normal. ''' fraction = str(actual) + '/' + str(normal) lpMusicList = lyo.LyMusicList() From dae4ea3a0a983d7104daf65da89586869b6530cd Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 6 Aug 2026 16:39:49 -1000 Subject: [PATCH 050/312] Add a writing-docs skill House style for docstrings, comments and version markers: the length target, keeping fixed bugs in the commit message rather than the code, and regression cases belonging in unittests rather than doctests. AI-assisted (Claude) --- .agents/skills/writing-docs/SKILL.md | 80 ++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 .agents/skills/writing-docs/SKILL.md diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md new file mode 100644 index 0000000000..6bc145c825 --- /dev/null +++ b/.agents/skills/writing-docs/SKILL.md @@ -0,0 +1,80 @@ +--- +name: writing-docs +description: >- + House style for docstrings, code comments, and `Changed in`/`New in` version + markers in music21. Use whenever you write or edit a docstring or comment, add + a version marker, or decide where a bug fix's test belongs. Covers the length + target, the rule against narrating bugs you just fixed, and why regression + cases go in unittests rather than doctests. +--- + +# Writing docs and comments + +## Length + +Aim for about 40% of the length an LLM writes by default. Cut qualifiers, +restatements, and the sentence that explains the sentence before it. A comment +earning its place says something the code cannot. + +## Say what is, not what was or what not to do + +Describe current behavior. Do not narrate the bug you just fixed, the old +spelling of an API, or when upstream changed something. + +```python +# yes +# ligature brackets; manual beams are plain [ and ] +return self.backslash + '[ ' + +# no +# \[ used to be emitted for beams, which was wrong -- LilyPond renamed +# this in 2.16 and it silently produced nothing +``` + +The commit message is where a fixed bug belongs: why it was wrong, how it was +found, what it broke. That costs nothing until someone runs `git log`, and +`git blame` leads them there from the line itself. + +The exception is a mistake that is likely to recur — a genuine trap that the +next person would otherwise walk into. Rare. Prefer stating the rule positively +even then. + +## Version markers + +`* Changed in v[X]: one line.` or `* New in v[X].` Only for user-facing changes +to the public interface: a signature, a return type, an output format. Keep to +what a reader must act on; drop the before-picture. + +``` +* Changed in v11: `stringOutput()` always returns a `str`, never None. +* Changed in v11: emits `\tuplet`; the arguments are now actual, normal. +* New in v11. +``` + +A plain bug fix — code now does what it always claimed — gets no marker and no +doctest. It goes in the commit message. + +See the `bump-version` skill for which digit to change and the odd/even +convention. + +## Doctests are not regression tests + +Doctests are documentation that happens to be verified. Every example must earn +its place by teaching the reader something about how to use the object. + +Regression cases go in the module's `Test(unittest.TestCase)` class, where a +comment or the method name can name the issue: + +```python +def testMetronomeMarkWrittenInStream(self): + # https://github.com/cuthbertLab/music21/issues/1852 + ... +``` + +So: a fix for a crash on an edge case, a check that some input no longer +produces invalid output, an assertion tied to an issue number — unittest. An +example a user would want to read — doctest. + +Naming the guarded bug **is** appropriate in a unittest; that is what the test +is for. The rule against narrating old bugs applies to docstrings and to +comments in shipping code, not to tests. From 71fd9b8047b3542f6565144debf7b725afa18f99 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 10 Aug 2026 12:21:40 -1000 Subject: [PATCH 051/312] =?UTF-8?q?roman:=20reject=20x/y/z=20in=20figures;?= =?UTF-8?q?=20use=20=E2=91=AA=E2=91=AC=E2=91=AE=20as=20internal=20placehol?= =?UTF-8?q?ders?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit expandShortHand() collapsed the two-digit figures 11, 13, and 15 to the single characters x, y, and z so that SHORTHAND_RE would find each as one group. That leaked: RomanNumeral('Ix') parsed as I11, 'Iy' as I13, and 'Iz' as a nonsensical two-note C4 C4 chord, since x/y/z pass the isalnum() figure validation. The placeholders are now ⑪ ⑬ ⑮, which read as the numbers they stand for, and a figure containing x, y, or z raises RomanNumeralException. No real figure uses those letters: clercqTemperley rewrites its 'x' to 'o' before constructing a RomanNumeral, and no corpus RomanText file has one. Also strengthens the AGENTS.md / writing-docs rule that regression cases belong in unittests, not docstrings. AI-assisted (Claude) --- .agents/skills/writing-docs/SKILL.md | 6 ++++++ AGENTS.md | 6 ++++++ music21/roman.py | 21 ++++++++++++++------- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 6bc145c825..345b2b150c 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -75,6 +75,12 @@ So: a fix for a crash on an edge case, a check that some input no longer produces invalid output, an assertion tied to an issue number — unittest. An example a user would want to read — doctest. +The trap is the one-line `Traceback` example proving that bad input now raises. +It looks like documentation and costs almost nothing to add, but the input comes +from the bug rather than from anything a user would write, so it teaches nothing +while making an obscure corner one of the first things a reader meets. It is a +unittest. + Naming the guarded bug **is** appropriate in a unittest; that is what the test is for. The rule against narrating old bugs applies to docstrings and to comments in shipping code, not to tests. diff --git a/AGENTS.md b/AGENTS.md index 0ebb83b5e8..fed5f76f5f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,12 @@ if on a single core machine.) - Run `uv run ruff check music21` before making PRs or pushes to open PRs. - Run `uv run mypy music21` before making PRs or pushes to open PRs. +- **Regression cases go in the module's `Test(unittest.TestCase)` class — never in a + docstring.** This is absolute, and it covers the tempting one-liner showing that some + bad input now raises. A doctest sits in the most-read documentation the project has, + so an example built from input no one would ever write teaches nothing and puts an + obscure bug on a billboard. The test: would a first-time reader of this object want + this example? If no, it is a unittest. See the `writing-docs` skill. - Never commit `forceSource=True` to a test or doctest (it re-parses from source every run and slows the suite for everyone). The ONLY exception is the one test that exercises `forceSource` itself. If you hit a stale-parse problem while developing: diff --git a/music21/roman.py b/music21/roman.py index 733261bd29..ca947d3d23 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -44,7 +44,7 @@ # ----------------------------------------------------------------------------- -SHORTHAND_RE = re.compile(r'#*-*b*o*[1-9xyz]') +SHORTHAND_RE = re.compile(r'#*-*b*o*[1-9⑪⑬⑮]') ENDWITHFLAT_RE = re.compile(r'[b\-]$') # cache all Key/Scale objects created or passed in; re-use @@ -232,18 +232,16 @@ def expandShortHand(shorthand): shorthand = shorthand.replace('/', '') # this line actually seems unnecessary. if ENDWITHFLAT_RE.match(shorthand): shorthand += '3' - shorthand = re.sub('11', 'x', shorthand) - shorthand = re.sub('13', 'y', shorthand) - shorthand = re.sub('15', 'z', shorthand) + # single characters for the two-digit figures, so that each group + # SHORTHAND_RE finds is one figure. + shorthand = shorthand.replace('11', '⑪').replace('13', '⑬').replace('15', '⑮') shorthandGroups = SHORTHAND_RE.findall(shorthand) if len(shorthandGroups) == 1 and shorthandGroups[0].endswith('3'): shorthandGroups = ['5', shorthandGroups[0]] shGroupOut = [] for sh in shorthandGroups: - sh = re.sub('x', '11', sh) - sh = re.sub('y', '13', sh) - sh = re.sub('z', '15', sh) + sh = sh.replace('⑪', '11').replace('⑬', '13').replace('⑮', '15') shGroupOut.append(sh) return shGroupOut @@ -2422,6 +2420,9 @@ def __init__( if not all(char.isalnum() or char in '#°+-/[]' for char in figure): # V, b, ø, no, etc. already covered by isalnum() raise RomanNumeralException(f'Invalid figure: {figure}') + if any(char in 'xyz' for char in figure): + # no roman numeral figure contains these letters + raise RomanNumeralException(f'Invalid figure: {figure}') # Store raw figure before calling setKeyOrScale: self._figure = figure @@ -4630,6 +4631,12 @@ def test_get_key_from_cache_mode(self): self.assertEqual(_getKeyFromCache('C').mode, 'major') self.assertEqual(_getKeyFromCache('c').mode, 'minor') + def testXYZAreNotFigures(self): + for fig in ('Ix', 'Iy', 'Iz', 'V7/ix'): + with self.subTest(figure=fig): + with self.assertRaises(RomanNumeralException): + RomanNumeral(fig, 'C') + class TestExternal(unittest.TestCase): show = True From c02ddef6d7d12c40305e6a50cc790e94d1fe1d18 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 12 Aug 2026 12:44:10 -1000 Subject: [PATCH 052/312] misc fixes August 2026 * Agents told not to put bug fix regression tests in doctests * typo fixes and misc spacing cleanups in articulations, lilyObjects * standardize more "Changed in" * not sure when streamIterator __getattr__ will finally be removed -- maybe now? --- AGENTS.md | 1 + music21/articulations.py | 2 +- music21/base.py | 2 +- music21/lily/lilyObjects.py | 66 ++++--------------------------------- music21/pitch.py | 2 +- music21/stream/iterator.py | 2 +- 6 files changed, 12 insertions(+), 63 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fed5f76f5f..3ba5ecca1e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,6 +40,7 @@ - If the stale result could have spread to other users/devs, increment the music21 patch/beta version (see "PRs and Issues" below) — bumping the version invalidates all caches everywhere. + - Doctests are not normally the place to demonstrate bug fixes - nor do simple bug fixes get a "Changed in vXX" message. Doctests demonstrate usage for the future. Unittests are for regression testing. # Setup diff --git a/music21/articulations.py b/music21/articulations.py index 7adbdec18c..087589a1bf 100644 --- a/music21/articulations.py +++ b/music21/articulations.py @@ -94,7 +94,7 @@ # ------------------------------------------------------------------------------ class Articulation(base.Music21Object): ''' - Base class for all Articulation sub-classes. + Base class for all Articulation subclasses. >>> x = articulations.Articulation() >>> x.placement = 'below' diff --git a/music21/base.py b/music21/base.py index 3a3c407d2c..17d9f0a97f 100644 --- a/music21/base.py +++ b/music21/base.py @@ -3620,7 +3620,7 @@ def _getMeasureOffset(self, includeMeasurePadding: bool = True) -> OffsetQL: ''' from music21 import stream - # TODO: v8 -- expose as public. + # TODO: expose as public. activeS = self.activeSite if activeS is not None and isinstance(activeS, stream.Measure): # environLocal.printDebug(['found activeSite as Measure, using for offset']) diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py index e91d3eb7ef..7b354b8fb1 100644 --- a/music21/lily/lilyObjects.py +++ b/music21/lily/lilyObjects.py @@ -270,6 +270,7 @@ def quoteString(self, stringIn: str) -> str: stringNew = stringIn.replace('"', r'\"') return '"' + stringNew + '" ' + # noinspection GrazieInspection def comment(self, stringIn: str) -> str: r''' returns a comment that is %{ stringIn.strip() %} @@ -282,7 +283,6 @@ def comment(self, stringIn: str) -> str: class LyMock(LyObject): r''' A test object for trying various music21 to Lily conversions - ''' supportedClasses = ['Mock', 'Mocker'] m21toLy = {'Mock': {'mockAttribute': 'mock-attribute', @@ -341,7 +341,6 @@ class LyTopLevelExpression(LyObject): >>> str(lyTopLevel) '\\book { } ' ''' - def __init__(self, lilypondHeader: LyLilypondHeader|None = None, bookBlock: LyBookBlock|None = None, @@ -379,7 +378,6 @@ class LyLilypondHeader(LyObject): >>> str(lyh) '\\header { } ' ''' - def __init__(self, lilypondHeaderBody: LyLilypondHeaderBody|None = None) -> None: super().__init__() self.lilypondHeaderBody = lilypondHeaderBody @@ -397,14 +395,13 @@ class LyEmbeddedScm(LyObject): Note that if any LyEmbeddedScm is found in an output then the output SHOULD be marked as unsafe. But a lot of standard lilypond functions are actually embedded scheme. For instance, \clef, which - as http://lilypond.org/doc/v2.12/input/lsr/lilypond-snippets/Pitches#Tweaking-clef-properties + as https://lilypond.org/doc/v2.12/input/lsr/lilypond-snippets/Pitches#Tweaking-clef-properties shows is a macro to run a lot of \set commands. >>> lyScheme = lily.lilyObjects.LyEmbeddedScm('##t') >>> str(lyScheme) '##t' ''' - def __init__(self, content: str = '') -> None: super().__init__() self.content = content @@ -430,7 +427,6 @@ class LyAssignmentId(LyObject): >>> str(lyAssignmentId) 'title' ''' - def __init__(self, content: str = '', isLyricString: bool = False) -> None: super().__init__() self.content = content @@ -460,7 +456,6 @@ class LyAssignment(LyObject): Note that you could also pass assignmentId a LyAssignmentId object, but that's overkill for a lot of things. ''' - def __init__(self, assignmentId: LyAssignmentId|str|None = None, identifierInit: LyIdentifierInit|None = None, @@ -497,7 +492,6 @@ class LyIdentifierInit(LyObject): >>> print(lyIdInit) "hello" ''' - def __init__(self, scoreBlock: LyScoreBlock|None = None, bookBlock: LyBookBlock|None = None, @@ -575,7 +569,6 @@ class LyContextDefSpecBody(LyObject): >>> lyContextBody.stringOutput() 'body \\grobdescriptions #t' ''' - def __init__(self, contextDefIdentifier: str|None = None, contextDefSpecBody: str|None = None, @@ -651,7 +644,6 @@ class LyBookBody(LyObject): b c ''' - def __init__(self, contents: list[LyObject|str]|None = None, bookIdentifier: str|None = None, @@ -677,7 +669,6 @@ class LyBookpartBlock(LyObject): >>> lbb.stringOutput() '\\bookpart { \n\n } \n' ''' - def __init__(self, bookpartBody: LyBookpartBody|None = None) -> None: super().__init__() self.bookpartBody = bookpartBody @@ -716,7 +707,6 @@ class LyBookpartBody(LyObject): b c ''' - def __init__(self, contents: list[LyObject|str]|None = None, bookIdentifier: str|None = None, @@ -746,7 +736,6 @@ class LyScoreBlock(LyObject): >>> print(lyScoreBlock) \score { hello } ''' - def __init__(self, scoreBody: LyScoreBody|str|None = None) -> None: super().__init__() self.scoreBody = scoreBody @@ -770,7 +759,6 @@ class LyScoreBody(LyObject): >>> str(lsb) 'score' ''' - def __init__(self, music: LyMusic|None = None, scoreIdentifier: str|None = None, @@ -834,7 +822,6 @@ class LyOutputDef(LyObject): r''' This is an ugly grammar, since it does not close the curly bracket. ''' - def __init__(self, outputDefBody: LyOutputDefBody|None = None) -> None: super().__init__() self.outputDefBody = outputDefBody @@ -856,7 +843,6 @@ class LyOutputDefHead(LyObject): According to Appendix C, is the same as LyOutputDefHeadWithModeSwitch ''' - def __init__(self, defType: str|None = None) -> None: super().__init__() self.defType = defType @@ -879,7 +865,6 @@ class LyOutputDefBody(LyObject): | output_def_body context_def_spec_block | output_def_body error ''' - def __init__(self, outputDefHead: LyOutputDefHead|None = None, outputDefIdentifier: str|None = None, @@ -947,7 +932,6 @@ class LyTempoEvent(LyObject): >>> str(lte) '\\tempo 85 4 = 70-100 ' ''' - def __init__(self, tempoRange: LyTempoRange|None = None, stenoDuration: LyStenoDuration|None = None, @@ -987,7 +971,6 @@ class LyMusicList(LyObject): r''' can take any number of LyMusic, LyEmbeddedScm, or LyError objects ''' - def __init__(self, contents: list[LyObject|str]|None = None) -> None: super().__init__() if contents is None: @@ -1067,7 +1050,6 @@ class LySequentialMusic(LyObject): Can be explicitly tagged with "\sequential" if displayTag is True ''' - def __init__(self, musicList: LyObject|None = None, displayTag: bool = False, @@ -1084,7 +1066,7 @@ def stringOutput(self) -> str: else: musicListSO = '' tag = '' - if self.displayTag is True: + if self.displayTag: tag = self.backslash + 'sequential ' if self.beforeMatter == 'startStaff': @@ -1102,14 +1084,13 @@ class LyOssiaMusic(LyObject): Can be tagged with \startStaff and \stopStaff if startstop is True ''' - def __init__(self, musicList: LyMusicList|None = None, startstop: bool = True) -> None: super().__init__() self.musicList = musicList self.startstop = startstop def stringOutput(self) -> str: - if self.startstop is True: + if self.startstop: start = self.backslash + 'startStaff ' stop = self.backslash + 'stopStaff' else: @@ -1130,7 +1111,6 @@ class LySimultaneousMusic(LyObject): Can be explicitly tagged with '\simultaneous' if displayTag is True otherwise encloses in double angle brackets ''' - def __init__(self, musicList: LyMusicList|None = None, displayTag: bool = False) -> None: super().__init__() self.musicList = musicList @@ -1176,7 +1156,6 @@ class LyContextModification(LyObject): but not context_mod!!!!! ''' - def __init__(self, contextModList: LyContextModList|list[str]|None = None, contextModIdentifier: str|None = None, @@ -1200,7 +1179,6 @@ class LyContextModList(LyObject): r''' contains zero or more LyContextMod objects and an optional contextModIdentifier ''' - def __init__(self, contents: list[LyContextMod]|None = None, contextModIdentifier: str|None = None, @@ -1223,7 +1201,6 @@ class LyCompositeMusic(LyObject): r''' one of LyPrefixCompositeMusic or LyGroupedMusicList stored in self.contents ''' - def __init__(self, prefixCompositeMusic: LyPrefixCompositeMusic|None = None, groupedMusicList: LyObject|None = None, @@ -1261,7 +1238,6 @@ class LyGroupedMusicList(LyObject): r''' one of LySimultaneousMusic or LySequentialMusic ''' - def __init__(self, simultaneousMusic: LySimultaneousMusic|None = None, sequentialMusic: LySequentialMusic|None = None, @@ -1318,7 +1294,6 @@ class LySchemeFunction(LyObject): We have usually been using LyEmbeddedScm for this ''' - def __init__(self, content: LyObject|str|None = None) -> None: super().__init__() self.content = content @@ -1334,7 +1309,6 @@ class LyOptionalId(LyObject): r''' an optional id setting ''' - def __init__(self, content: str|None = None) -> None: super().__init__() self.content = content @@ -1494,7 +1468,6 @@ class LyRelativeMusic(LyObject): r''' relative music ''' - def __init__(self, content: LyObject|None = None) -> None: super().__init__() self.content = content @@ -1509,7 +1482,6 @@ class LyNewLyrics(LyObject): r''' contains a list of LyGroupedMusicList objects or identifiers ''' - def __init__(self, groupedMusicLists: list[LyGroupedMusicList|str]|None = None) -> None: if groupedMusicLists is None: groupedMusicLists = [] @@ -1556,7 +1528,6 @@ class LyContextChange(LyObject): >>> str(lcc) '\\change x = y ' ''' - def __init__(self, before: str = '', after: str = '') -> None: super().__init__() self.before = before @@ -1572,7 +1543,6 @@ class LyPropertyPath(LyObject): has one or more of LyEmbeddedScm objects ''' - def __init__(self, embeddedScheme: list[LyEmbeddedScm]|None = None) -> None: if embeddedScheme is None: embeddedScheme = [] @@ -1608,10 +1578,8 @@ class LyPropertyOperation(LyObject): >>> lpo = lily.lilyObjects.LyPropertyOperation('revert', 'x', 'y') >>> str(lpo) '\\revert x.y ' - - TODO: should \set be given? ''' - + # TODO: should \set be given? def __init__(self, mode: str|None = None, value1: str = '', @@ -1642,7 +1610,6 @@ class LyContextDefMod(LyObject): r''' one of consists, remove, accepts, defaultchild, denies, alias, type, description, name ''' - def __init__(self, contextDef: str = '') -> None: super().__init__() self.contextDef = contextDef @@ -1704,7 +1671,6 @@ class LyEventChord(LyObject): LyNewChord or LyFigureSpec + Duration once that is done. But there is no LySimpleChordElements object yet. ''' - def __init__(self, simpleChordElements: LySimpleElement|None = None, postEvents: list[LyObject|str]|None = None, @@ -1802,10 +1768,8 @@ class LyChordBodyElement(LyObject): post_events | DRUM_PITCH post_events | music_function_chord_body - - TODO: only the first form is currently supported in creation ''' - + # TODO: only the first form is currently supported in creation def __init__(self, parts: list[LyObject|str]|None = None) -> None: if parts is None: parts = [] @@ -1925,7 +1889,6 @@ class LyDirectionLessEvent(LyObject): represents ['[', ']', '~', '(', ')', '\!', '\(', '\)', '\>', '\<'] or an EVENT_IDENTIFIER or a tremolo_type ''' - def __init__(self, event: LyObject|str|None = None) -> None: super().__init__() self.event = event @@ -1965,7 +1928,6 @@ class LyPitch(LyObject): represents a pitch name and zero or more sup or sub quotes also used for steno_pitch and steno_tonic_pitch ''' - def __init__(self, noteNamePitch: str = '', quotes: str = '') -> None: super().__init__() self.noteNamePitch = noteNamePitch @@ -1981,7 +1943,6 @@ class LyGenTextDef(LyObject): r''' holds either full_markup, string, or DIGIT ''' - def __init__(self, value: LyFullMarkup|str|int|None = None) -> None: super().__init__() self.value = value @@ -1997,7 +1958,6 @@ class LyScriptAbbreviation(LyObject): ^ + - | > . _ ''' - def __init__(self, value: str = '') -> None: super().__init__() self.value = value @@ -2008,12 +1968,11 @@ def stringOutput(self) -> str: class LyScriptDir(LyObject): r''' - Holds a script direction abbreviation (above below etc), one of:: + Holds a script direction abbreviation (above, below etc.), one of:: _ ^ - ''' - def __init__(self, value: str = '') -> None: super().__init__() self.value = value @@ -2034,9 +1993,7 @@ class LyStenoDuration(LyObject): >>> lsd = lily.lilyObjects.LyStenoDuration('2', 2) >>> print(lsd) 2.. - ''' - def __init__(self, durationNumber: int|str|None = None, numDots: int = 0) -> None: super().__init__() self.durationNumber = durationNumber @@ -2055,7 +2012,6 @@ class LyMultipliedDuration(LyObject): if stenoDur is None then output is empty -- thus also represents optional_notemode_duration ''' - def __init__(self, stenoDur: LyStenoDuration|None = None, multiply: list[int|str]|None = None, @@ -2117,7 +2073,6 @@ class LySimpleElement(LyObject): | RESTNAME optional_notemode_duration | lyric_element optional_notemode_duration ''' - def __init__(self, parts: list[LyObject|str]|None = None) -> None: if parts is None: parts = [] @@ -2140,7 +2095,6 @@ class LyLyricElement(LyObject): >>> print(lle) hel_ ''' - def __init__(self, lyMarkupOrString: LyMarkup|str|None = None) -> None: super().__init__() self.lyMarkupOrString = lyMarkupOrString @@ -2156,7 +2110,6 @@ class LyTempoRange(LyObject): >>> print(lily.lilyObjects.LyTempoRange(70, 100)) 70-100 ''' - def __init__(self, lowestOrOnlyTempo: int|str|None = None, highestTempoOrNone: int|str|None = None, @@ -2176,7 +2129,6 @@ class LyNumberExpression(LyObject): r''' any list of numbers or LyNumberTerms separated by '+' or '-' objects. ''' - def __init__(self, numberAndSepList: list[LyNumberTerm|int|str]|None = None) -> None: if numberAndSepList is None: numberAndSepList = [] @@ -2192,7 +2144,6 @@ class LyNumberTerm(LyObject): r''' any list of numbers separated by '*' or '/' strings. ''' - def __init__(self, numberAndSepList: list[int|str]|None = None) -> None: if numberAndSepList is None: numberAndSepList = [] @@ -2226,7 +2177,6 @@ class LyFullMarkupList(LyObject): >>> print(lily.lilyObjects.LyFullMarkupList(markupList)) \markuplist { "a" "b" } ''' - def __init__(self, markupListOrIdentifier: LyMarkupList|str|None = None) -> None: super().__init__() self.markupListOrIdentifier = markupListOrIdentifier @@ -2325,7 +2275,6 @@ class LySimpleMarkup(LyObject): takes 1 required arg, 2nd for markup_function ''' - def __init__(self, simpleType: str = 'string', argument1: str = '', @@ -2417,7 +2366,6 @@ def testOneNoteTheHardWay(self): r''' make a dotted-half note c. ''' - lyPitch = LyPitch('c', "''") stenoDuration = LyStenoDuration('2', 1) diff --git a/music21/pitch.py b/music21/pitch.py index 40680a0923..8f6763064f 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -4984,7 +4984,7 @@ def updateAccidentalDisplay( >>> a4.accidental is None True - v8 -- made keyword-only and added `otherSimultaneousPitches`. + * Changed in v8: all parameteres are keyword-only; added `otherSimultaneousPitches`. ''' # N.B. -- this is a very complex method # do not alter it without significant testing. diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py index 06063512fb..238ffff890 100644 --- a/music21/stream/iterator.py +++ b/music21/stream/iterator.py @@ -219,7 +219,7 @@ def __next__(self) -> M21ObjType: def __getattr__(self, attr): ''' - DEPRECATED in v8 -- will be removed in v9. + DEPRECATED in v8 -- will be removed in soon. In case an attribute is defined on Stream but not on a StreamIterator, create a Stream and then return that attribute. This is NOT performance From ae896d06ab77e699f20bbfbbe087631f7c6b43b8 Mon Sep 17 00:00:00 2001 From: youdie006 Date: Wed, 19 Aug 2026 10:57:15 +0900 Subject: [PATCH 053/312] Fix MEI importer silently dropping and shifting subsequent onsets music21.mei.base._processEmbeddedElements dispatches each child element through a per-container tag->function mapping. A child whose tag is not in the mapping is silently dropped and never recursed into (only a debug message is emitted). (measured/bowed tremolo, which wraps the / carrying the notated duration) was registered in none of the dispatch dicts, so the tremolo note/chord and its duration disappeared and every later onset in the layer shifted earlier - data corruption, not a cosmetic loss. Add bTremFromElement, mirroring beamFromElement/tupletFromElement: it imports the wrapped / with its notated duration and returns it, so timing is preserved. When @unitdur is present it attaches an expressions.Tremolo with numberOfMarks derived from the unit duration. Register in the layer, beam, and tuplet dispatch tables. Scope limited to ; the two-note is intentionally out of scope. Docstring implemented/not-implemented lists updated to match. Fixes #1994. --- music21/mei/base.py | 112 ++++++++++++++++++++++++++++++++++++--- music21/mei/test_base.py | 59 +++++++++++++++++++++ 2 files changed, 165 insertions(+), 6 deletions(-) diff --git a/music21/mei/base.py b/music21/mei/base.py index ead4acb67c..80d06296db 100644 --- a/music21/mei/base.py +++ b/music21/mei/base.py @@ -188,6 +188,7 @@ from music21 import duration from music21 import environment from music21 import exceptions21 +from music21 import expressions from music21 import instrument from music21 import interval from music21 import key @@ -272,6 +273,21 @@ class MeiElementError(exceptions21.Music21Exception): _BAD_VERSE_NUMBER = 'Verse number must be an int (got "{}")' +# Mapping from an MEI @unitdur (the note value of one tremolo repetition) to the number of +# tremolo marks used by :class:`~music21.expressions.Tremolo`: an eighth-note unit is one mark, a +# sixteenth two, and so on (log2(unitdur) - 2). Durations outside this table produce no marks. +_MEI_UNITDUR_TO_TREMOLO_MARKS = { + '8': 1, + '16': 2, + '32': 3, + '64': 4, + '128': 5, + '256': 6, + '512': 7, + '1024': 8, +} + + # Module-level Functions # ----------------------------------------------------------------------------- class MeiToM21Converter: @@ -2632,7 +2648,7 @@ def beamFromElement(elem, slurBundle=None): **Attributes/Elements Implemented:** - - , , , , , , , + - , , , , , , , , **Attributes/Elements Ignored:** @@ -2660,7 +2676,7 @@ def beamFromElement(elem, slurBundle=None): **Contained Elements not Implemented:** - - MEI.cmn: bTrem beatRpt fTrem halfmRpt meterSig meterSigGrp + - MEI.cmn: beatRpt fTrem halfmRpt meterSig meterSigGrp - MEI.critapp: app - MEI.edittrans: (all) - MEI.mensural: ligature mensur proport @@ -2677,6 +2693,7 @@ def beamFromElement(elem, slurBundle=None): f'{MEI_NS}rest': restFromElement, f'{MEI_NS}tuplet': tupletFromElement, f'{MEI_NS}beam': beamFromElement, + f'{MEI_NS}bTrem': bTremFromElement, f'{MEI_NS}space': spaceFromElement, f'{MEI_NS}barLine': barLineFromElement, } @@ -2753,7 +2770,7 @@ def tupletFromElement(elem, slurBundle=None): **Attributes/Elements Implemented:** - - , , , , , , , + - , , , , , , , , - @num and @numbase **Attributes/Elements in Testing:** none @@ -2782,7 +2799,7 @@ def tupletFromElement(elem, slurBundle=None): **Contained Elements not Implemented:** - - MEI.cmn: bTrem beatRpt fTrem halfmRpt meterSig meterSigGrp + - MEI.cmn: beatRpt fTrem halfmRpt meterSig meterSigGrp - MEI.critapp: app - MEI.edittrans: (all) - MEI.mensural: ligature mensur proport @@ -2796,6 +2813,7 @@ def tupletFromElement(elem, slurBundle=None): f'{MEI_NS}rest': restFromElement, f'{MEI_NS}chord': chordFromElement, f'{MEI_NS}clef': clefFromElement, + f'{MEI_NS}bTrem': bTremFromElement, f'{MEI_NS}space': spaceFromElement, f'{MEI_NS}barLine': barLineFromElement, } @@ -2835,6 +2853,87 @@ def tupletFromElement(elem, slurBundle=None): return tuple(tupletMembers) +def bTremFromElement(elem, slurBundle=None): + ''' + A measured tremolo on a single note or chord (also called a bowed tremolo), in which + the enclosed note or chord is rapidly repeated in performance. + + In MEI 2013 (MEI.cmn module). + + :param elem: The ```` element to process. + :type elem: :class:`~xml.etree.ElementTree.Element` + :returns: An iterable of the objects contained within the ```` container. The wrapped + ```` or ```` is imported with its notated duration, so that the onsets of + following events are not shifted. + :rtype: list of :class:`~music21.base.Music21Object` + + **Example** + + A quarter-note chord notated as a measured tremolo of eighth notes (``@unitdur="8"``, one + tremolo mark). The function returns the chord itself, carrying its notated duration and a + :class:`~music21.expressions.Tremolo` expression. + + >>> from xml.etree import ElementTree as ET + >>> meiSnippet = """ + ... + ... + ... + ... + ... """ + >>> meiSnippet = ET.fromstring(meiSnippet) + >>> result = mei.base.bTremFromElement(meiSnippet) + >>> len(result) + 1 + >>> result[0] + + >>> result[0].quarterLength + 1.0 + >>> result[0].expressions + [] + >>> result[0].expressions[0].numberOfMarks + 1 + + **Attributes/Elements Implemented:** + + - , + - @unitdur (mapped to :attr:`~music21.expressions.Tremolo.numberOfMarks`) + + **Attributes/Elements in Testing:** none + + **Attributes not Implemented:** + + - att.common (@label, @n, @xml:base) (att.id (@xml:id)) + - att.typography (@fontfam, @fontname, @fontsize, @fontstyle, @fontweight) + - att.bTrem.log (att.tremmeasured (@num, @numbase)) + - att.bTrem.vis (all) + - att.bTrem.ges (att.duration.performed (@dur.ges)) + - att.bTrem.anl (all) + + **Contained Elements not Implemented:** none + ''' + # mapping from tag name to our converter function + tagToFunction = { + f'{MEI_NS}chord': chordFromElement, + f'{MEI_NS}note': noteFromElement, + } + + # Import the wrapped / as an ordinary note/chord so its notated duration survives; + # otherwise the tremolo (and every onset after it in the layer) would be silently dropped. + tremoloStuff = _processEmbeddedElements(elem.findall('*'), tagToFunction, elem.tag, slurBundle) + + # Attach a measured Tremolo, deriving the number of marks from @unitdur when it is a usable + # duration; timing correctness is preserved even when @unitdur is absent or unrecognized. + numberOfMarks = _MEI_UNITDUR_TO_TREMOLO_MARKS.get(elem.get('unitdur', '')) + if numberOfMarks is not None: + for eachObj in tremoloStuff: + if isinstance(eachObj, note.NotRest): + tremolo = expressions.Tremolo() + tremolo.numberOfMarks = numberOfMarks + eachObj.expressions.append(tremolo) + + return tremoloStuff + + def layerFromElement(elem, overrideN=None, slurBundle=None): ''' An independent stream of events on a staff. @@ -2860,7 +2959,7 @@ def layerFromElement(elem, overrideN=None, slurBundle=None): **Attributes/Elements Implemented:** - - , , , , , , , , , and + - , , , , , , , , , , and contained within - @n, from att.common @@ -2881,7 +2980,7 @@ def layerFromElement(elem, overrideN=None, slurBundle=None): **Contained Elements not Implemented:** - - MEI.cmn: arpeg bTrem beamSpan beatRpt bend breath fTrem fermata gliss hairpin halfmRpt + - MEI.cmn: arpeg beamSpan beatRpt bend breath fTrem fermata gliss hairpin halfmRpt harpPedal mRpt mRpt2 meterSig meterSigGrp multiRest multiRpt octave pedal reh slur tie tuplet tupletSpan - MEI.cmnOrnaments: mordent trill turn @@ -2906,6 +3005,7 @@ def layerFromElement(elem, overrideN=None, slurBundle=None): f'{MEI_NS}mRest': mRestFromElement, f'{MEI_NS}beam': beamFromElement, f'{MEI_NS}tuplet': tupletFromElement, + f'{MEI_NS}bTrem': bTremFromElement, f'{MEI_NS}space': spaceFromElement, f'{MEI_NS}mSpace': mSpaceFromElement, f'{MEI_NS}barLine': barLineFromElement, diff --git a/music21/mei/test_base.py b/music21/mei/test_base.py index 68c973f319..cf3e8c2258 100644 --- a/music21/mei/test_base.py +++ b/music21/mei/test_base.py @@ -36,6 +36,7 @@ from music21 import bar from music21 import clef from music21 import duration +from music21 import expressions from music21 import instrument from music21 import interval from music21 import key @@ -2223,6 +2224,64 @@ def testIntegration1cLayerFromElement(self): except base.MeiAttributeError as maError: self.assertEqual(base._MISSING_VOICE_ID, maError.args[0]) + # ----------------------------------------------------------------------------- + # Tests for bTremFromElement() + + def testIntegration1BTremFromElement(self): + ''' + bTremFromElement(): the wrapped is imported with its notated duration, and a + Tremolo expression is attached with the number of marks derived from @unitdur. + ''' + inputXML = ''' + + + + + ''' + elem = ETree.fromstring(inputXML) + + actual = base.bTremFromElement(elem) + + self.assertIsInstance(actual, list) + self.assertEqual(1, len(actual)) + self.assertEqual(1.0, actual[0].quarterLength) + self.assertEqual(['E4', 'E5'], [p.nameWithOctave for p in actual[0].pitches]) + self.assertEqual(1, len(actual[0].expressions)) + self.assertIsInstance(actual[0].expressions[0], expressions.Tremolo) + self.assertEqual(1, actual[0].expressions[0].numberOfMarks) + + def testIntegration2BTremFromElement(self): + ''' + bTremFromElement() within a : a must not be silently dropped. The tremolo + note/chord contributes its notated duration so the onsets of following events do not shift + (regression test for https://github.com/cuthbertLab/music21/issues/1994). + ''' + inputXML = ''' + + + + + + + + + ''' + elem = ETree.fromstring(inputXML) + + actual = base.layerFromElement(elem) + + self.assertEqual(3, len(actual)) + # onsets are not shifted: the tremolo chord keeps its place on beat 2 + self.assertEqual([0.0, 1.0, 2.0], [obj.offset for obj in actual]) + self.assertEqual([1.0, 1.0, 2.0], [obj.quarterLength for obj in actual]) + self.assertEqual('C4', actual[0].nameWithOctave) + # the beat-2 element is the E4/E5 chord, imported with a Tremolo expression + self.assertEqual(['E4', 'E5'], [p.nameWithOctave for p in actual[1].pitches]) + self.assertEqual(1, len(actual[1].expressions)) + self.assertIsInstance(actual[1].expressions[0], expressions.Tremolo) + self.assertEqual(1, actual[1].expressions[0].numberOfMarks) + self.assertEqual('G4', actual[2].nameWithOctave) + # ----------------------------------------------------------------------------- # Tests for staffFromElement() From ef8ebcb955f19bfd757d117b2d6449001b5fd7cb Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 19 Aug 2026 14:52:53 -1000 Subject: [PATCH 054/312] human prose rewrite. add editorconfig --- .../issue-1349-romanNumeral-direction.md | 2 +- .editorconfig | 2 + music21/humdrum/spineParser.py | 2 +- music21/roman.py | 289 +++++++++++------- 4 files changed, 175 insertions(+), 120 deletions(-) diff --git a/.agents/memory/issue-1349-romanNumeral-direction.md b/.agents/memory/issue-1349-romanNumeral-direction.md index 7831b06ad8..bd4c1ce957 100644 --- a/.agents/memory/issue-1349-romanNumeral-direction.md +++ b/.agents/memory/issue-1349-romanNumeral-direction.md @@ -13,7 +13,7 @@ the tsvConverter `localKeyAsRn` workaround was removed. (Naming settled after two rounds: name the parameter for the chord-level fact it carries, not for its consequence like "uppercaseNumeral" — the numeral doesn't exist yet when the function runs.) Myke decided NOT to -flip the `romanNumeralFromChord` default from CAUTIONARY to QUALITY (her 2022 +flip the `romanNumeralFromChord` default from CAUTIONARY to QUALITY (the 2022 idea in the issue) for now. Still open, to discuss with contributor pablopupo and Malcolm Sailor on the issue: (2) adding `sixthMinor`/`seventhMinor` parameters to `romanNumeralFromChord` and possibly flipping its default, and diff --git a/.editorconfig b/.editorconfig index d68a9b9326..abdedbc5c4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,3 +18,5 @@ charset = utf-8 indent_style = space indent_size = 4 trim_trailing_whitespace = true +max_line_length = 100 +ij_visual_guides = 80,100 diff --git a/music21/humdrum/spineParser.py b/music21/humdrum/spineParser.py index 504532c817..66aaa12f3f 100644 --- a/music21/humdrum/spineParser.py +++ b/music21/humdrum/spineParser.py @@ -165,7 +165,7 @@ def __init__(self, dataStream: str|list[str]) -> None: self.protoSpines: list[ProtoSpine] = [] self.eventCollections: list[EventCollection] = [] self.spineCollection: SpineCollection|None = None - # populated by insertGlobalEvents(), consumed by parseMetadata() + # populated by insertGlobalEvents(), used by parseMetadata() self.globalReferences: list[GlobalReference] = [] if isinstance(dataStream, str): diff --git a/music21/roman.py b/music21/roman.py index 2648406eaa..146f99a062 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -30,6 +30,7 @@ from music21 import key from music21 import note from music21 import pitch +from music21.pitch import Pitch from music21 import scale class FigureTuple(t.NamedTuple): @@ -38,7 +39,7 @@ class FigureTuple(t.NamedTuple): alteration, and the accidental string to print before a figure. Produced by :meth:`~music21.roman.FigureTuple.fromPitchAndReference` - and consumed by :func:`~music21.roman.romanNumeralFromChord` and + and used in :func:`~music21.roman.romanNumeralFromChord` and :func:`~music21.roman.correctRNAlterationForMinor`. >>> ft = roman.FigureTuple(degFromRefPitch=6, alter=1.0, prefix='#') @@ -49,26 +50,24 @@ class FigureTuple(t.NamedTuple): `degFromRefPitch` is the generic (diatonic) degree of the pitch counted from whatever reference pitch was supplied, reduced to a simple - interval: always 1-7, 1-based as intervals and scale degrees are (the - reference itself is 1, not 0; an octave and a third is 3; an octave and - a seventh is 7). - Descending intervals invert, so a pitch a second *below* the reference - registers as 7. The reference is the chord's sounding bass - in :func:`~music21.roman.figureTuples`, but the *tonic* of the key in - `romanNumeralFromChord` -- there the pitch measured is the chord's - root, so `degFromRefPitch` is the scale degree of the root (which is - how `correctRNAlterationForMinor` reads it). + interval: always 1-7, 1-based (unison, octave = 1, 10th = 3, etc.). + Descending intervals report complements, so a pitch a + second *below* the reference is 7. + + We use the vague term "RefPitch" because it varies based on use. + For instance, the reference pitch is the chord's sounding *bass* + in :func:`~music21.roman.figureTuples` (for generating figured bass + numerals). But the reference is the *tonic of the key* in + `romanNumeralFromChord`, where we are trying to figure out the + scale degree of the root. `alter` is the chromatic alteration in semitones (a float) of the pitch - relative to the diatonic scale of the key. For minor keys the natural - minor scale is always the reference, so in c minor, A-flat has - `alter=0.0` and A-natural has `alter=1.0`. It is an alteration of a - scale step, never an interval above the bass. + relative to normal pitch for the degree in the diatonic scale of the key. + For minor keys the natural minor scale is always the reference, + so in c minor, A-flat has `alter=0.0` and A-natural has `alter=1.0`. `prefix` is the string of accidentals ('#', '##', 'b', 'bb', ...) to - print before the figure. It is a figured-bass-style alteration in - music21's Roman-numeral spelling: it says how the pitch differs from - the pitches implied by the key signature (the key's diatonic scale), + print before the figure. It is a figured-bass-style alteration, with '#' meaning raised a semitone and 'b' lowered, regardless of what accidental would appear on the note in staff notation. So A-natural on ^6 in c minor gets `prefix='#'` even though the note itself would @@ -79,19 +78,11 @@ class FigureTuple(t.NamedTuple): The `sixthMinor`/`seventhMinor` conventions (:class:`~music21.roman.Minor67Default`) are likewise *not* consulted when a FigureTuple is created: `fromPitchAndReference` renders `prefix` - purely from `alter`. A convention enters only afterwards, when - :func:`~music21.roman.correctRNAlterationForMinor` rewrites the tuple - for a chord root on ^6 or ^7 in minor -- and that function hardcodes - the CAUTIONARY style (it takes no Minor67Default argument; see issue - #1349). That rewrite is where `prefix` and `alter` deliberately - diverge: for instance, `FigureTuple(degFromRefPitch=6, alter=0.0, prefix='b')` - means "diatonic in natural minor, but print a cautionary flat anyway." - Do not assume that `prefix` can be recomputed from `alter`. + purely from `alter`. * Changed in v11: became a typed NamedTuple (was a collections.namedtuple), and the first field was renamed from `aboveBass` to `degFromRefPitch`, - since the reference pitch is not always the bass and the count is - 1-based. This documentation was AI-assisted (Claude). + since the reference pitch is not always the bass. ''' degFromRefPitch: int alter: float @@ -100,18 +91,17 @@ class FigureTuple(t.NamedTuple): @classmethod def fromPitchAndReference( cls, - pitchObj: pitch.Pitch, + pitchObj: Pitch, keyObj: key.Key, - refPitch: pitch.Pitch, + refPitch: Pitch, ) -> FigureTuple: ''' - Measure a pitch against a key and a reference pitch, returning the - FigureTuple of the 1-based degree of the pitch counted from - `refPitch`, its alteration from a step in the given key, and an - accidental prefix string. (The reference need not be a sounding - bass: `romanNumeralFromChord` passes the tonic of the key here.) + Return a FigureTuple of the `pitchObj` above + `refPitch` in a given key - For instance, in C major, an A-3 above an F# bass would be: + E.g., get the FigureTuple for an A-flat above an F# bass in C major, showing that + it is a third above the F# and a half step down from the normally expected + note A in C major. >>> roman.FigureTuple.fromPitchAndReference( ... pitch.Pitch('A-3'), @@ -120,8 +110,10 @@ def fromPitchAndReference( ... ) FigureTuple(degFromRefPitch=3, alter=-1.0, prefix='b') - These figures can be more complex in minor, so this is a good - reference, showing that natural minor is always used. + These figures can be more complex in minor, but the natural minor is always used. + + Here are the various forms of ^3, ^6, and ^7 (including weird ones) that might + appear in C minor, above the tonic "C", expressed as FigureTuples. >>> c = key.Key('c') >>> c_as_bass = pitch.Pitch('C3') @@ -168,36 +160,57 @@ def fromPitchAndReference( class PitchFigureTuple(t.NamedTuple): ''' - Like a :class:`~music21.roman.FigureTuple` but also carrying the pitch - it describes, one entry per pitch of a chord. - - Produced by :func:`~music21.roman.figureTuples`, where -- unlike the - tonic-based use of `FigureTuple` in `romanNumeralFromChord` -- the - reference pitch for `degFromRefPitch` is the sounding bass of the chord. - See `FigureTuple` for the meaning of the shared fields. - - Here is an A-flat a sixth above a C bass in c minor: diatonic there - (`alter=0.0`), so no accidental prefix is needed: + Like a :class:`~music21.roman.FigureTuple` but which also stores the pitch + it describes as `.pitch`. >>> pft = roman.PitchFigureTuple(6, 0.0, '', pitch.Pitch('A-4')) - >>> pft.pitch - + >>> pft + PitchFigureTuple(degFromRefPitch=6, alter=0.0, prefix='', pitch=) * Changed in v11: became a typed NamedTuple (was a collections.namedtuple); - renamed from `ChordFigureTuple`, since one tuple describes a single - pitch (a list of them describes a chord); and the first field was - renamed from `aboveBass` to `degFromRefPitch`, since the reference - pitch is not always the bass and the count is 1-based. - This documentation was AI-assisted (Claude). + renamed from `ChordFigureTuple`, since this tuple describes a single + pitch; first field renamed to `degFromRefPitch` as with FigureTuple above. ''' degFromRefPitch: int alter: float prefix: str - pitch: 'pitch.Pitch' # needs quotes since 'pitch' is redefined by this field + pitch: Pitch + + @classmethod + def fromPitchAndReference( + cls, + pitchObj: Pitch, + keyObj: key.Key, + refPitch: Pitch, + ) -> PitchFigureTuple: + ''' + Return a PitchFigureTuple of the `pitchObj` above `refPitch` in a given key. + + See FigureTuple.fromPitchAndReference for details. + + Here is a B-flat above an E in the key of F major (perhaps a member of a V65 chord) + + >>> roman.PitchFigureTuple.fromPitchAndReference(pitch.Pitch('B-4'), + ... key.Key('F'), + ... pitch.Pitch('E4')) + PitchFigureTuple(degFromRefPitch=5, alter=0.0, prefix='', + pitch=) + + And the same in G major: + + >>> roman.PitchFigureTuple.fromPitchAndReference(pitch.Pitch('B-4'), + ... key.Key('G'), + ... pitch.Pitch('E4')) + PitchFigureTuple(degFromRefPitch=5, alter=-1.0, prefix='b', + pitch=) + ''' + ft = FigureTuple.fromPitchAndReference(pitchObj, keyObj, refPitch) + return cls(ft.degFromRefPitch, ft.alter, ft.prefix, pitchObj) + environLocal = environment.Environment('roman') -# TODO: setting inversion should change the figure +# TODO: make inversion read-only and use a separate setInversion to change # ----------------------------------------------------------------------------- @@ -566,12 +579,9 @@ def _postFigureFromChordAndKey(chordObj: chord.Chord, keyObj: key.Key) -> str: def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigureTuple]: ''' Return a list of :class:`~music21.roman.PitchFigureTuple` objects, one - per pitch of the chord, each showing the 1-based degree of the note - counted from the chord's bass, its alteration (float) from a step in the - given key, an accidental prefix string, and the pitch object. - - Note though that for roman numerals, the applicable key is almost always - the root. + per pitch of the chord, each showing the simplied (1-7) generic interval of the note + above the chord's bass, etc. (see PitchFigureTuple and :class:`~music21.roman.FigureTuple` + for reference). For instance, in C major, F# D A- C# would be: @@ -596,7 +606,7 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=), PitchFigureTuple(degFromRefPitch=5, alter=1.0, prefix='#', pitch=)] - A C dominant-seventh chord in c minor alters the bass but not the 7th degree. + A C dominant seventh chord in c minor alters the bass but not the 7th degree. >>> roman.figureTuples( ... chord.Chord(['E3', 'C4', 'G4', 'B-5']), @@ -607,32 +617,31 @@ def figureTuples(chordObject: chord.Chord, keyObject: key.Key) -> list[PitchFigu PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=), PitchFigureTuple(degFromRefPitch=5, alter=0.0, prefix='', pitch=)] + The same degree can appear more than once with the same or different alters and prefixes: + >>> roman.figureTuples( - ... chord.Chord(['C4', 'E4', 'G4', 'C#4']), + ... chord.Chord(['C4', 'E4', 'G4', 'C#4', 'E5']), ... key.Key('C'), ... ) [PitchFigureTuple(degFromRefPitch=1, alter=0.0, prefix='', pitch=), PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=), PitchFigureTuple(degFromRefPitch=5, alter=0.0, prefix='', pitch=), - PitchFigureTuple(degFromRefPitch=1, alter=1.0, prefix='#', pitch=)] + PitchFigureTuple(degFromRefPitch=1, alter=1.0, prefix='#', pitch=), + PitchFigureTuple(degFromRefPitch=3, alter=0.0, prefix='', pitch=)] ''' result = [] bass = chordObject.bass() for thisPitch in chordObject.pitches: - shortTuple = FigureTuple.fromPitchAndReference(thisPitch, keyObject, bass) - appendTuple = PitchFigureTuple(shortTuple.degFromRefPitch, - shortTuple.alter, - shortTuple.prefix, - thisPitch) + appendTuple = PitchFigureTuple.fromPitchAndReference(thisPitch, keyObject, bass) result.append(appendTuple) return result @common.deprecated('v11', 'v12', 'use FigureTuple.fromPitchAndReference() instead') def figureTupleSolo( - pitchObj: pitch.Pitch, + pitchObj: Pitch, keyObj: key.Key, - bass: pitch.Pitch + bass: Pitch ) -> FigureTuple: ''' Deprecated: use :meth:`~music21.roman.FigureTuple.fromPitchAndReference` @@ -787,7 +796,7 @@ def correctRNAlterationForMinor( chordHasMajorThird: bool = False, ) -> FigureTuple: ''' - (This will become a private function in a future version) + (This may become a private function in a future version) Takes in a FigureTuple and a Key object and returns the same or a new FigureTuple correcting for the fact that, for instance, Ab in c minor @@ -814,23 +823,44 @@ def correctRNAlterationForMinor( FigureTuple(degFromRefPitch=6, alter=0, prefix='b') For a chord whose quality is minor, diminished, or half-diminished, a raised - root is implied by the lowercase numeral, so the sharp prefix is removed: + root is implied by the lowercase numeral, so the sharp prefix is removed. - >>> ft7 = roman.FigureTuple(degFromRefPitch=7, alter=1, prefix='#') - >>> roman.correctRNAlterationForMinor(ft7, key.Key('c')) - FigureTuple(degFromRefPitch=7, alter=0, prefix='') + Take for instance, the figure tuple of a B-natural in C minor: + + >>> ft_b_in_c_minor = roman.FigureTuple.fromPitchAndReference( + ... pitch.Pitch('B5'), + ... key.Key('c'), + ... pitch.Pitch('C3'), + ... ) + + Because B-natural is raised compared to the B-flat of the natural + minor scale, it has an alter of 1.0 and a prefix of '#' + + >>> ft_b_in_c_minor + FigureTuple(degFromRefPitch=7, alter=1.0, prefix='#') + + When used as the root of viio or viio6, for the purposes of + labeling as a Roman numeral (where QUALITY of chord determines + scale degrees), the Roman numeral needs no alter or prefix: + + >>> roman.correctRNAlterationForMinor(ft_b_in_c_minor, key.Key('c')) + FigureTuple(degFromRefPitch=7, alter=0.0, prefix='') But when the chord has a major third (and thus an uppercase numeral), - pass `chordHasMajorThird=True` so that the sharp is kept: an uppercase - VI or VII in minor already refers to the chord on the *lowered* (natural - minor) degree, so a major chord on the raised degree needs its sharp to - be distinguished from it: + pass `chordHasMajorThird=True` so that the sharp is kept on pitches which + might otherwise lose them. For instance, when generating a Roman numeral from + a figure like (uppercase) VI or VII in minor, the figure generally refers to the chord + on the *lowered* (natural minor) degree, so + if we want to show a major chord on the *raised* degree + the figure needs a sharp to distinguish it. Take, for instance, + a B-natural in C minor that will be the root of a B-major triad + (B-D#-F# -- strange but possible). + In this case we want to retain the sharp on #VII. - >>> ft8 = roman.FigureTuple(degFromRefPitch=7, alter=1, prefix='#') - >>> roman.correctRNAlterationForMinor(ft8, key.Key('c'), chordHasMajorThird=True) - FigureTuple(degFromRefPitch=7, alter=1, prefix='#') + >>> roman.correctRNAlterationForMinor(ft_b_in_c_minor, key.Key('c'), chordHasMajorThird=True) + FigureTuple(degFromRefPitch=7, alter=1.0, prefix='#') - Does nothing for major and passes in the original Figure Tuple unchanged: + This function does nothing to FigureTuples for major keys: >>> ft1 = roman.FigureTuple(degFromRefPitch=6, alter=-1, prefix='b') >>> ft2 = roman.correctRNAlterationForMinor(ft1, key.Key('C')) @@ -839,7 +869,8 @@ def correctRNAlterationForMinor( >>> ft1 is ft2 True - Does nothing for steps other than 6 or 7: + The function also does nothing to FigureTuples on degFromRefPitch + other than 6 or 7 (typically used to filter out chords not built on ^6 or ^7). >>> ft3 = roman.FigureTuple(degFromRefPitch=4, alter=-1, prefix='b') >>> ft4 = roman.correctRNAlterationForMinor(ft3, key.Key('c')) @@ -851,14 +882,13 @@ def correctRNAlterationForMinor( * Changed in v11: the keyword-only argument `chordHasMajorThird` was added, so that major-quality chords keep their sharp prefix on raised ^6 and ^7 in minor (issue #1349). - This fix was AI-assisted (Claude). ''' # Maintenance note: this is one of three places that implement the # "what does an accidental mean on ^6/^7 in minor, given chord quality" # convention, each with its own rules. The other two are the prefix # suppression for triads in _postFigureFromChordAndKey (generation of the # upper figures) and RomanNumeral._adjustMinorVIandVIIByQuality (parsing, - # in the reverse direction). Change one and check the others. + # in the reverse direction). If one changes, the others need to be checked. if keyObj.mode != 'minor': return figureTuple if figureTuple.degFromRefPitch not in (6, 7): @@ -874,10 +904,10 @@ def correctRNAlterationForMinor( rootAlterationString = figureTuple.prefix if alter == 1.0: - alter = 0 + alter = 0.0 rootAlterationString = '' elif alter == 0.0: - alter = 0 # NB! does not change! + # alter does not change, just prefix rootAlterationString = 'b' # more exotic: elif alter > 1.0: @@ -1066,8 +1096,8 @@ def romanNumeralFromChord( >>> romanNumeral11 - A major triad on the raised sixth or seventh degree of a minor key keeps - its sharp, so that it is distinguished from the major triad on the lowered + A (highly unusual) major triad on the raised sixth or seventh degree + of a minor key keeps its sharp, so that it is distinguished from the major triad on the lowered (natural minor) degree, both in the figure and in :attr:`~music21.roman.RomanNumeral.romanNumeral`: @@ -1209,10 +1239,7 @@ def romanNumeralFromChord( This kind of context-sensitivity is not currently included. * Changed in v11: chords with a major third built on the raised sixth or - seventh scale degrees of a minor key keep their sharp prefix (previously - the alteration was buried in the inversion figures, e.g. `VI#63`, and - lost entirely from `.romanNumeral`; issue #1349). - This fix was AI-assisted (Claude). + seventh scale degrees of a minor key keep their sharp prefix. OMIT_FROM_DOCS @@ -1607,7 +1634,7 @@ class RomanNumeral(harmony.Harmony): >>> V.scaleDegree 5 - Default key is C Major + The default key is C Major >>> for p in V.pitches: ... p @@ -3500,17 +3527,22 @@ def transpose(self, value, *, inPlace=False) -> t.Self|None: @property def romanNumeral(self) -> str: ''' - Read-only property that returns either the romanNumeralAlone (e.g. just - II) or the frontAlterationAccidental.modifier (with 'b' for '-') + romanNumeralAlone - (e.g. #II, bII) + Read-only property that returns the roman numeral stripped of figures (7, 65, etc.) with + any frontAlterationAccidental.modifier with 'b' for '-' (but with some caveats + for minor that make it easier to for other parsers, see below). >>> rn = roman.RomanNumeral('#II7') >>> rn.romanNumeral '#II' - >>> rn = roman.RomanNumeral('Ger+6') + Diminished and augmented symbols are removed. + + >>> rn = roman.RomanNumeral('viio') >>> rn.romanNumeral - 'Ger' + 'vii' + + Double flats can be written as either `bb` or `--` in instantiation, + but will show `bb` on output, and applied chord's tonicized goals are removed. >>> rn = roman.RomanNumeral('bbII/V') >>> rn.romanNumeral @@ -3519,29 +3551,50 @@ def romanNumeral(self) -> str: >>> rn.romanNumeral 'bbII' - Note that the alteration returned is the *sounding* alteration of the - root relative to the diatonic scale of the key -- for minor keys, the - natural minor scale -- which is not necessarily the accidental written - in the figure. So a scale degree that is chromatically raised gains a - sharp even if none was written, because of the interpretation of - `sixthMinor` and `seventhMinor` (see :class:`~music21.roman.Minor67Default`): + Augmented-sixth chord symbols (German, French, etc.) are considered Roman numerals. + This behavior may change at a future date. - >>> roman.RomanNumeral('vi', 'c').romanNumeral + >>> rn = roman.RomanNumeral('Ger+6') + >>> rn.romanNumeral + 'Ger' + + + The accidental in `.romanNumeral` is the accidental needed compared to the standard scale + degree in major or *natural minor*. It will resolve to the same root + only if sixthMinor=FLAT and seventhMinor=FLAT are both used. + + For instance, in c minor, with natural-minor diatonic 6̂ = A♭, + RomanNumeral('vi', 'c') has root A♮ — a raised sixth degree — interpreted using QUALITY + since this is the only normally used minor triad on the sixth scale degree. + However because A♮ is raised compared to the natural minor, .romanNumeral returns '#vi' + even though no # was used in the construction. + + >>> a_min = roman.RomanNumeral('vi', 'c') + >>> ' '.join(p.name for p in a_min.pitches) + 'A C E' + >>> a_min.romanNumeral '#vi' - while a merely cautionary accidental (one that does not change the - sounding pitch) is dropped: + On the other hand, RomanNumeral('bVI', 'c', sixthMinor=CAUTIONARY) has + root A♭ which is part of the natural minor scale, so .romanNumeral returns + 'VI' even though a b *was* written in the original. - >>> rn = roman.RomanNumeral('bVI', 'c', - ... sixthMinor=roman.Minor67Default.CAUTIONARY) - >>> ' '.join(p.name for p in rn.pitches) + >>> ab_maj = roman.RomanNumeral('bVI', 'c', + ... sixthMinor=roman.Minor67Default.CAUTIONARY) + >>> ' '.join(p.name for p in ab_maj.pitches) 'A- C E-' - >>> rn.romanNumeral + >>> ab_maj.romanNumeral 'VI' - For the notation as originally written, use - :attr:`~music21.roman.RomanNumeral.figure` or combine - `frontAlterationString` with `romanNumeralAlone`. + To get back the notation as it was originally written, use + :attr:`~music21.roman.RomanNumeral.figure` + (if you also want any inversion marks, etc.) or combine + `frontAlterationString` with `romanNumeralAlone`: + + >>> a_min.frontAlterationString + a_min.romanNumeralAlone + 'vi' + >>> ab_maj.frontAlterationString + ab_maj.romanNumeralAlone + 'bVI' OMIT_FROM_DOCS From 738b3c12d171bc4d4ee9cbe5bd0c2715289ff58f Mon Sep 17 00:00:00 2001 From: Rook Date: Fri, 21 Aug 2026 11:23:11 +0000 Subject: [PATCH 055/312] Fix typo in usingGit developer docs --- documentation/source/developerReference/usingGit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/source/developerReference/usingGit.rst b/documentation/source/developerReference/usingGit.rst index 9ab7588a50..ca153c32f7 100644 --- a/documentation/source/developerReference/usingGit.rst +++ b/documentation/source/developerReference/usingGit.rst @@ -103,7 +103,7 @@ Making pull requests is easy: functionality? When you've finished writing your description, click on the ``Send pull - request`` button. You've sent your pull pull request! + request`` button. You've sent your pull request! .. image:: images/usingGit/github__pull_requests__3.png From 4f71a996789e9ab635ba3efe577887e84f0d20bf Mon Sep 17 00:00:00 2001 From: hill Date: Fri, 21 Aug 2026 14:15:27 +0200 Subject: [PATCH 056/312] Fix malformed ratio in sparschuh-stanhope.scl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Degree 8 is written `697//441`, with a doubled slash. No Scala parser can read it: the file is unparseable from that line on. The intended value is unambiguous. The comment on the same line documents the degree as (128/81) * (6273/6272), and that product is exactly 697/441: 128 * 6273 = 802944 81 * 6272 = 508032 802944 / 508032 = 697/441 (both divide by 1152) It is also consistent with the file's own arithmetic — 6273/6272 is +0.276 cents, as the comment states — and it sits correctly between its neighbours 3/2 (1.5) and 82/49 (1.6735) at 1.5805. This changes one character and preserves the column alignment of the surrounding degrees. No other file in the archive contains a doubled slash. Note that music21 still cannot parse this file after the fix, for an unrelated reason: ScalaData.parse only skips lines that *start* with `!` and does not strip trailing inline `!` comments, which every degree line here carries. That is a separate parser issue, not a data one. --- music21/scale/scala/scl/sparschuh-stanhope.scl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/music21/scale/scala/scl/sparschuh-stanhope.scl b/music21/scale/scala/scl/sparschuh-stanhope.scl index 65f5b9bb2f..619ecd7192 100644 --- a/music21/scale/scala/scl/sparschuh-stanhope.scl +++ b/music21/scale/scala/scl/sparschuh-stanhope.scl @@ -9,7 +9,7 @@ Sparschuh's (2010) septenarian variant of Stanhopes (1806) idea 4/3 ! F 620/441 ! F# (45/32) * (3968/3969 ~-0.436...Cents flattend ) 3/2 ! G -697//441 ! G# (128/81) * (6273/6272 ~+0.276...Cents sharper ) +697/441 ! G# (128/81) * (6273/6272 ~+0.276...Cents sharper ) 82/49 ! A (5/3) * (246/245 ~+7.052...Cents sharper ) 16/9 ! Bb 15/8 ! B From 71c384b69ed715aa93524b91a8773e5682e79ea3 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 21 Aug 2026 13:46:44 -1000 Subject: [PATCH 057/312] Protect against unlinked infinite duration Infinite duration notes cause havoc. They are already rejected for normal notes. Reject for unlinked also,. Deprecate tinyNotation 0 value = bar+fermata Small boolean cleanups --- music21/duration.py | 37 +++++++++++++++++++++---------------- music21/tinyNotation.py | 6 ++++-- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/music21/duration.py b/music21/duration.py index de957f688a..811087db97 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -52,7 +52,7 @@ import fractions from functools import lru_cache import io -from math import inf, isnan +from math import inf, isnan, isfinite import typing as t import unittest @@ -1173,7 +1173,7 @@ def _reprInternal(self): return base_rep def _checkFrozen(self): - if self.frozen is True: + if self.frozen: raise TupletException( 'A frozen tuplet (or one attached to a duration) has immutable length.') @@ -1797,7 +1797,7 @@ def __eq__(self, other): return True def _reprInternal(self): - if self.linked is True: + if self.linked: return str(self.quarterLength) else: return f'unlinked type:{self.type} quarterLength:{self.quarterLength}' @@ -1870,9 +1870,9 @@ def _setLinked(self, value: bool): raise TypeError(f'Linked can only be True or False, not {value}') if self._quarterLengthNeedsUpdating: self._updateQuarterLength() - if value is False and self._linked is True: + if value is False and self._linked: self._unlinkedType = self.type - elif value is True and self._linked is False: + elif value is True and not self._linked: self._quarterLengthNeedsUpdating = True self._componentsNeedUpdating = True @@ -2290,7 +2290,7 @@ def getGraceDuration( # create grace duration gd: GraceDuration - if appoggiatura is True: + if appoggiatura: gd = AppoggiaturaDuration() else: gd = GraceDuration() @@ -2321,7 +2321,7 @@ def informClient(self) -> bool: Returns False if there was no need to inform the client (like nothing has changed) or if `.client` is None. Otherwise returns True. ''' - if self._quarterLengthNeedsUpdating is True: + if self._quarterLengthNeedsUpdating: old_qtrLength = self._qtrLength self._updateQuarterLength() if self._qtrLength == old_qtrLength: @@ -2511,7 +2511,7 @@ def _updateQuarterLength(self): * Changed in v7: made private and faster. ''' - if self.linked is False: + if not self.linked: return if self._dotGroups == (0,) and not self.tuplets and len(self.components) == 1: @@ -2712,7 +2712,7 @@ def dots(self, value: int): if not common.isNum(value): raise TypeError('only numeric dot values can be used with this method.') - # easter egg + # Easter egg if value == inf: self.type = nextLargerType(self.type) self.dots = 0 @@ -2723,7 +2723,7 @@ def dots(self, value: int): components[i] = durationTupleFromTypeDots(dt.type, value) self._components = tuple(components) self._quarterLengthNeedsUpdating = True - if self.linked is True: + if self.linked: self.expressionIsInferred = False self.informClient() @@ -2921,13 +2921,18 @@ def _getQuarterLength(self) -> OffsetQL: return self._qtrLength def _setQuarterLength(self, value: OffsetQLIn): - if self.linked is False: + if not self.linked: + # linked durations get this check for free from opFrac below. + if not isfinite(value): + raise ValueError( + f'quarterLength must be a finite number, not {value!r}' + ) self._qtrLength = value elif (self._qtrLength != value or self._componentsNeedUpdating # skip a type update for next type check or self.type == 'inexpressible'): value = opFrac(value) - if value == 0.0 and self.linked is True: + if value == 0.0 and self.linked: self.clear() self._qtrLength = value self.expressionIsInferred = True @@ -3055,7 +3060,7 @@ def type(self) -> str: >>> a.quarterLength 0.25 ''' - if self.linked is False: + if not self.linked: if self._unlinkedType is None: # pragma: no cover raise DurationException( 'linked property setter failed to set _unlinkedType, please open a bug report' @@ -3074,7 +3079,7 @@ def type(self, value: str): if value not in ordinalTypeFromNum and value not in ('inexpressible', 'complex'): raise ValueError(f'no such type exists: {value}') - if self.linked is True: + if self.linked: nt = durationTupleFromTypeDots(value, self.dots) self.components = (nt,) self._quarterLengthNeedsUpdating = True @@ -3347,12 +3352,12 @@ def findTupletGroups(self, incorporateGroupings: bool = False) -> list[list[note tupletActive = False for n in self.streamIn.notesAndRests: if not n.duration.tuplets: # most common case first - if tupletActive is True: + if tupletActive: self.allTupletGroups.append(currentTupletGroup) currentTupletGroup = [] tupletActive = False continue - if tupletActive is False: + if not tupletActive: tupletActive = True currentTupletGroup.append(n) if incorporateGroupings and n.duration.tuplets[0].type == 'stop': diff --git a/music21/tinyNotation.py b/music21/tinyNotation.py index a9b3f21c6a..a4e899d96e 100644 --- a/music21/tinyNotation.py +++ b/music21/tinyNotation.py @@ -517,6 +517,8 @@ def durationType(self, element, search, pm, t, parent): self.durationFound = True typeNum = int(search.group(1)) if typeNum == 0: + # NOTE: using 0 to mean full-bar + fermata was never fully documented. + # it is considered deprecated. if parent.stateDict['currentTimeSignature'] is not None: element.duration = copy.deepcopy( parent.stateDict['currentTimeSignature'].barDuration @@ -1133,7 +1135,7 @@ def __init__( self.modifierSquare = None self.modifierUnderscore = LyricModifier - self.makeNotation = makeNotation + self.makeNotation: bool = makeNotation self.raiseExceptions = raiseExceptions # will be filled by self.setupRegularExpressions() self._tokenMapRe: list[tuple[typing.Pattern, type]] = [] @@ -1408,7 +1410,7 @@ def postParse(self): It currently runs `.makeMeasures()` on `.stream` unless `.makeNotation` is `False`. ''' - if self.makeNotation is not False: + if self.makeNotation: self.stream.makeMeasures(inPlace=True) From fcdb67847c96ff0fb95138de7440b4951ddb7cc1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 21 Aug 2026 14:28:41 -1000 Subject: [PATCH 058/312] simplify bools --- documentation/docbuild/documenters.py | 4 +- .../usersGuide/usersGuide_20_examples2.ipynb | 2 +- music21/alpha/analysis/fixer.py | 14 +++---- music21/analysis/enharmonics.py | 6 +-- music21/analysis/neoRiemannian.py | 10 ++--- music21/analysis/reduction.py | 4 +- music21/audioSearch/recording.py | 5 ++- music21/audioSearch/scoreFollower.py | 4 +- music21/audioSearch/transcriber.py | 10 +++-- music21/chord/__init__.py | 4 +- music21/converter/subConverters.py | 6 +-- music21/defaults.py | 1 + music21/freezeThaw.py | 12 +++--- music21/graph/axis.py | 8 ++-- music21/instrument.py | 8 ++-- music21/key.py | 2 +- music21/musicxml/m21ToXml.py | 38 +++++++++---------- music21/note.py | 8 ++-- music21/repeat.py | 4 +- music21/search/base.py | 2 +- music21/stream/base.py | 8 +++- music21/stream/filters.py | 24 ++++++------ music21/stream/iterator.py | 8 ++-- music21/tree/verticality.py | 10 ++--- music21/variant.py | 26 ++++++------- 25 files changed, 118 insertions(+), 110 deletions(-) diff --git a/documentation/docbuild/documenters.py b/documentation/docbuild/documenters.py index 52cd94aaf9..b1043d6378 100644 --- a/documentation/docbuild/documenters.py +++ b/documentation/docbuild/documenters.py @@ -1564,13 +1564,13 @@ def getRstComposerWorksFormat(self, corpusWork): # worksAreVirtual = corpusWork.virtual # if worksAreVirtual: # workTitle += ' (*virtual*)' - if isSingleWork is False: + if not isSingleWork: result.append(workTitle) result.append('') procedure = self.getRstWorkFileDictFormat # if worksAreVirtual: # procedure = self.getRstVirtualWorkFileDictFormat - if isSingleWork is False: + if not isSingleWork: for corpusFile in corpusWork.files: result.extend(['- ' + procedure(corpusFile), '']) else: diff --git a/documentation/source/usersGuide/usersGuide_20_examples2.ipynb b/documentation/source/usersGuide/usersGuide_20_examples2.ipynb index 1ff52bcff0..23043c1113 100644 --- a/documentation/source/usersGuide/usersGuide_20_examples2.ipynb +++ b/documentation/source/usersGuide/usersGuide_20_examples2.ipynb @@ -703,7 +703,7 @@ " if analyzedKey.mode != 'minor':\n", " return False\n", " lastChord = getLastChord(score)\n", - " if lastChord.isMinorTriad() is False:\n", + " if not lastChord.isMinorTriad():\n", " return False\n", " if lastChord.root().name != analyzedKey.tonic.name:\n", " return False\n", diff --git a/music21/alpha/analysis/fixer.py b/music21/alpha/analysis/fixer.py index b69ae4df25..457ced740e 100644 --- a/music21/alpha/analysis/fixer.py +++ b/music21/alpha/analysis/fixer.py @@ -57,7 +57,7 @@ class DeleteFixer(OMRMidiFixer): def fix(self): super().fix() for (midiRef, omrRef, op) in self.changes: - if self.checkIfNoteInstance(midiRef, omrRef) is False: + if not self.checkIfNoteInstance(midiRef, omrRef): continue # if they are the same, don't bother to try changing it # 3 is the number of noChange Ops @@ -140,7 +140,7 @@ class EnharmonicFixer(OMRMidiFixer): >>> fixer3.fix() >>> omrNote3.pitch.accidental - TEST 4 (case 2-1) e.g MIDI = g#, ground truth = a-, OMR = an + TEST 4 (case 2-1) e.g. MIDI = g#, ground truth = a-, OMR = an >>> midiNote4 = note.Note('G#4') >>> omrNote4 = note.Note('An4') @@ -156,7 +156,7 @@ class EnharmonicFixer(OMRMidiFixer): >>> omrNote4.pitch.accidental - TEST 5 (case 2-2) e.g midi = g-, gt = f#, omr = fn + TEST 5 (case 2-2) e.g. midi = g-, gt = f#, omr = fn >>> midiNote5 = note.Note('G-4') >>> omrNote5 = note.Note('Fn4') @@ -237,7 +237,7 @@ def fix(self): for (midiRef, omrRef, op) in self.changes: omrRef.style.color = 'black' # if they're not notes, don't bother with rest - if self.checkIfNoteInstance(midiRef, omrRef) is False: + if not self.checkIfNoteInstance(midiRef, omrRef): continue # if they are the same, don't bother to try changing it # 3 is the number of noChange Ops @@ -253,7 +253,7 @@ def fix(self): omrRef.pitch.accidental = None else: # case 2-1: midi note is sharp, omr note is one step higher and natural, - # should be a flat instead. e.g midi = g#, gt = a-, omr = an + # should be a flat instead. e.g. midi = g#, gt = a-, omr = an # omr note has higher ps than midi-- on a higher # line or space than midi note if omrRef.pitch > midiRef.pitch: @@ -261,7 +261,7 @@ def fix(self): ).isEnharmonic(midiRef.pitch): omrRef.pitch.accidental = pitch.Accidental('flat') # case 2-2: midi note is flat, omr note is one step lower and natural, - # should be a flat instead. e.g midi = g-, gt = f#, omr = fn + # should be a flat instead. e.g. midi = g-, gt = f#, omr = fn # omr note has lower ps than midi-- on a higher line # or space than midi note elif omrRef.pitch < midiRef.pitch: @@ -399,7 +399,7 @@ def addOrnament(self, * show: True when note should be colored blue Returns True if added successfully, or False if there was already an - ornament on the note and it wasn't added. + ornament on the note, and it wasn't added. ''' if not any(isinstance(e, expressions.Ornament) for e in selectedNote.expressions): selectedNote.expressions.append(ornament) diff --git a/music21/analysis/enharmonics.py b/music21/analysis/enharmonics.py index 8cb66bf098..5f30c3cb86 100644 --- a/music21/analysis/enharmonics.py +++ b/music21/analysis/enharmonics.py @@ -99,7 +99,7 @@ def getAlterationScore(self, possibility): Returns a score according to the number of sharps and flats in a possible spelling. The score is the sum of the flats and sharps + 1, multiplied by the alterationPenalty. ''' - if self.ruleObject.alterationPenalty is False: + if not self.ruleObject.alterationPenalty: return 1 joinedPossibility = ''.join([p.name for p in possibility]) @@ -114,7 +114,7 @@ def getMixSharpFlatsScore(self, possibility): the score is given by the number of the lesser used accidental (sharps or flats) multiplied by the mixSharpsFlatsPenalty. ''' - if self.ruleObject.mixSharpsFlatsPenalty is False: + if not self.ruleObject.mixSharpsFlatsPenalty: return 1 joinedPossibility = ''.join([p.name for p in possibility]) @@ -128,7 +128,7 @@ def getAugDimScore(self, possibility): Returns a score based on the number of augmented and diminished intervals between successive pitches in the given spelling. ''' - if self.ruleObject.augDimPenalty is False: + if not self.ruleObject.augDimPenalty: return 1 intervalStr = '' diff --git a/music21/analysis/neoRiemannian.py b/music21/analysis/neoRiemannian.py index e16bb731c5..d610965cda 100644 --- a/music21/analysis/neoRiemannian.py +++ b/music21/analysis/neoRiemannian.py @@ -97,7 +97,7 @@ def L(c, raiseException=True): transposeInterval = 'm2' changingPitch = c.fifth else: - if raiseException is True: + if raiseException: raise LRPException('Cannot perform L on this chord: not a major or minor triad') return c @@ -134,7 +134,7 @@ def P(c, raiseException=True): transposeInterval = 'A1' changingPitch = c.third else: - if raiseException is True: + if raiseException: raise LRPException('Cannot perform P on this chord: not a Major or Minor triad') return c @@ -171,7 +171,7 @@ def R(c, raiseException=True): transposeInterval = '-M2' changingPitch = c.root() else: - if raiseException is True: + if raiseException: raise LRPException('Cannot perform R on this chord: not a Major or Minor triad') return c @@ -358,7 +358,7 @@ def LRP_combinations(c, ''' if not c.isMajorTriad() and not c.isMinorTriad(): # First to avoid doing anything else if fail - if raiseException is True: + if raiseException: raise LRPException( f'Cannot perform transformations on chord {c}: not a major or minor triad') return c @@ -435,7 +435,7 @@ def completeHexatonic(c, simplifyEnharmonics=False, raiseException=True): hexatonicList.append(lastChord) return hexatonicList else: - if raiseException is True: + if raiseException: raise LRPException( 'Cannot perform transformations on this chord: not a major or minor triad') diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py index 56bbadb4c5..58befa6daa 100644 --- a/music21/analysis/reduction.py +++ b/music21/analysis/reduction.py @@ -1121,7 +1121,7 @@ def testPartReductionB(self, show=False): s.insert(0, p) pCount += 1 - if show is True: + if show: s.show() pr = analysis.reduction.PartReduction(s, normalize=False) @@ -1140,7 +1140,7 @@ def testPartReductionB(self, show=False): self._matchWeightedData(match, target) - if show is True: + if show: p = graph.plot.Dolan(s, title='Dynamics') p.run() diff --git a/music21/audioSearch/recording.py b/music21/audioSearch/recording.py index 4225b1397d..c390bd32bf 100644 --- a/music21/audioSearch/recording.py +++ b/music21/audioSearch/recording.py @@ -38,7 +38,8 @@ default_recordChunkLength = 1024 -def samplesFromRecording(seconds=10.0, storeFile=True, +def samplesFromRecording(seconds=10.0, + storeFile: bool = True, recordFormat=None, recordChannels=default_recordChannels, recordSampleRate=default_recordSampleRate, @@ -77,7 +78,7 @@ def samplesFromRecording(seconds=10.0, storeFile=True, st.close() p_audio.terminate() - if storeFile is not False: + if not storeFile: if isinstance(storeFile, str): waveFilename = storeFile else: diff --git a/music21/audioSearch/scoreFollower.py b/music21/audioSearch/scoreFollower.py index 2191fc02ae..5d6584957d 100644 --- a/music21/audioSearch/scoreFollower.py +++ b/music21/audioSearch/scoreFollower.py @@ -178,7 +178,7 @@ def repeatTranscription(self): # print('3') self.processing_time = time() - time_start environLocal.printDebug('and even to here.') - if END_OF_SCORE is True: + if END_OF_SCORE: exitType = 'endOfScore' # 'endOfScore' return exitType @@ -239,7 +239,7 @@ def silencePeriodDetection(self, notesList): if i.name != 'rest': onlyRests = False - if onlyRests is True: + if onlyRests: self.silencePeriod = True self.notesCounter = 0 self.silencePeriodCounter += 1 diff --git a/music21/audioSearch/transcriber.py b/music21/audioSearch/transcriber.py index 1f00e13f7e..d8ea6e799d 100644 --- a/music21/audioSearch/transcriber.py +++ b/music21/audioSearch/transcriber.py @@ -19,8 +19,12 @@ environLocal = environment.Environment('audioSearch.transcriber') -def runTranscribe(show=True, plot=True, useMic=True, - seconds=20.0, useScale=None, saveFile=True): # pragma: no cover +def runTranscribe(show: bool = True, + plot: bool = True, + useMic: bool = True, + seconds: float = 20.0, + useScale=None, + saveFile: bool|str = True): # pragma: no cover ''' runs all the methods to record from audio for `seconds` length (default 10.0) and transcribe the resulting melody returning a music21.Score object @@ -54,7 +58,7 @@ def runTranscribe(show=True, plot=True, useMic=True, waveFilename = saveFile # the rest of the score - if useMic is True: + if useMic: freqFromAQList = audioSearchBase.getFrequenciesFromMicrophone( length=seconds, storeWaveFilename=str(waveFilename)) diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index e024ed1909..d258b3746a 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -4005,7 +4005,7 @@ def semiClosedPosition( c2 = self.closedPosition(forceOctave=forceOctave, inPlace=inPlace, leaveRedundantPitches=leaveRedundantPitches) - if inPlace is True: + if inPlace: c2 = self # closedPosition() only returns None when inPlace=True, in which case c2 @@ -4028,7 +4028,7 @@ def semiClosedPosition( c2.clearCache() c2.sortAscending(inPlace=True) - if inPlace is False: + if not inPlace: return c2 def semitonesFromChordStep(self, chordStep, testRoot=None): diff --git a/music21/converter/subConverters.py b/music21/converter/subConverters.py index fd17db0a13..884167c492 100644 --- a/music21/converter/subConverters.py +++ b/music21/converter/subConverters.py @@ -100,7 +100,7 @@ def parseFile(self, loading the file and putting the data into parseData then there is no need to implement this method. Just set self.readBinary to True | False. ''' - if self.readBinary is False: + if not self.readBinary: import locale with open(filePath, encoding=locale.getpreferredencoding()) as f: dataStream = f.read() @@ -289,7 +289,7 @@ def writeDataStream(self, if fp is None: fp = self.getTemporaryFile() - if self.readBinary is False: + if not self.readBinary: writeFlags = 'w' else: writeFlags = 'wb' @@ -330,7 +330,7 @@ def toData( and return the object (str or bytes) returned. ''' fp = self.write(obj, fmt=fmt, subformats=subformats, **keywords) - if self.readBinary is False: + if not self.readBinary: readFlags = 'r' else: readFlags = 'rb' diff --git a/music21/defaults.py b/music21/defaults.py index 89c09a0557..4e41c2ef6a 100644 --- a/music21/defaults.py +++ b/music21/defaults.py @@ -17,6 +17,7 @@ import typing as t from music21 import _version + # note: this module should not import any higher level modules type StepName = t.Literal['C', 'D', 'E', 'F', 'G', 'A', 'B'] # restating so as not to import. diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py index afef17f046..86cf877fee 100644 --- a/music21/freezeThaw.py +++ b/music21/freezeThaw.py @@ -210,7 +210,7 @@ def __init__(self, streamObj=None, fastButUnsafe=False, topLevel=True, streamIds self.subStreamFreezers = {} # this will keep track of sub freezers for spanners - if streamObj is not None and fastButUnsafe is False: + if streamObj is not None and not fastButUnsafe: # deepcopy necessary because we mangle sites in the objects # before serialization self.stream = copy.deepcopy(streamObj) @@ -267,7 +267,7 @@ def setupSerializationScaffold(self, streamObj=None): # might not work when recurse yields allEls = list(streamObj.recurse(restoreActiveSites=False)) - if self.topLevel is True: + if self.topLevel: self.findActiveStreamIdsInHierarchy(streamObj) for el in allEls: @@ -297,7 +297,7 @@ def setupSerializationScaffold(self, streamObj=None): # removing seems to create problems for jsonPickle with Spanners self.setupStoredElementOffsetTuples(streamObj) - if self.topLevel is True: + if self.topLevel: self.recursiveClearSites(streamObj) def removeStreamStatusClient(self, streamObj): @@ -578,11 +578,11 @@ def findActiveStreamIdsInHierarchy( includeSelf=True) streamIds = [id(s) for s in streamsFoundGenerator] - if getSpanners is True: + if getSpanners: spannerBundle = streamObj.spannerBundle streamIds += spannerBundle.getSpannerStorageIds() - if getVariants is True: + if getVariants: for el in streamObj.recurse(includeSelf=True).getElementsByClass(variant.Variant): streamIds += self.findActiveStreamIdsInHierarchy(el._stream) @@ -853,7 +853,7 @@ def restoreElementsFromTuples(self, streamObj): streamObj.coreElementsChanged() for subElement in streamObj: - if subElement.isStream is True: + if subElement.isStream: # note that the elements may have already been restored # if the spanner stores a part or something in the Stream # for instance in a StaffGroup object diff --git a/music21/graph/axis.py b/music21/graph/axis.py index d35833ec60..30630ebd85 100644 --- a/music21/graph/axis.py +++ b/music21/graph/axis.py @@ -317,7 +317,7 @@ class PitchAxis(Axis): def __init__(self, client=None, axisName='x'): super().__init__(client, axisName) - self.showOctaves = 'few' + self.showOctaves: bool|t.Literal['few'] = 'few' self.showEnharmonic = True self.blankLabelUnused = True self.hideUnused = True @@ -425,7 +425,7 @@ def unweightedSortHelper(x): sub.append(accidentalLabelToUnicode(name)) label = '/'.join(sub) - if self.showOctaves is False: + if not self.showOctaves: label = re.sub(r'\d', '', label) elif self.showOctaves == 'few': matchOctave = re.search(r'\d', label) @@ -451,7 +451,7 @@ class PitchClassAxis(PitchAxis): quantities: tuple[str, ...] = ('pitchClass', 'pitchclass', 'pc') def __init__(self, client=None, axisName='x'): - self.showOctaves = False + self.showOctaves: bool|t.Literal['few'] = False super().__init__(client, axisName) self.minValue = 0 self.maxValue = 11 @@ -1129,7 +1129,7 @@ class QuarterLengthAxis(PositionAxis): def __init__(self, client=None, axisName='x'): super().__init__(client, axisName) - self.useLogScale = True + self.useLogScale: bool|int = True self.useDurationNames = False def extractOneElement(self, n, formatDict): diff --git a/music21/instrument.py b/music21/instrument.py index 24a61261ec..799d5c1a8a 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -65,7 +65,7 @@ def unbundleInstruments(streamIn: stream.Stream, {1.0} {1.0} ''' - if inPlace is True: + if inPlace: s = streamIn else: s = streamIn.coreCopyAsDerivation('unbundleInstruments') @@ -78,7 +78,7 @@ def unbundleInstruments(streamIn: stream.Stream, off = thisObj.offset s.insert(off, i) - if inPlace is False: + if not inPlace: return s @@ -106,7 +106,7 @@ def bundleInstruments(streamIn: stream.Stream, Cowbell ''' - if inPlace is True: + if inPlace: s = streamIn else: s = streamIn.coreCopyAsDerivation('bundleInstruments') @@ -120,7 +120,7 @@ def bundleInstruments(streamIn: stream.Stream, elif isinstance(thisObj, note.NotRest): thisObj.storedInstrument = lastInstrument - if inPlace is False: + if not inPlace: return s diff --git a/music21/key.py b/music21/key.py index 12f285f2f0..6085d9a4d3 100644 --- a/music21/key.py +++ b/music21/key.py @@ -1295,7 +1295,7 @@ def transpose(self, >>> changingKey ''' - if inPlace is True: + if inPlace: super().transpose(value, inPlace=inPlace) post = self else: diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 5098428eb1..24ef075cf6 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -795,7 +795,7 @@ def setStyleAttributes(self, mxObject, m21Object, musicXMLNames, m21Names=None): ''' if isinstance(m21Object, style.Style): stObj = m21Object - elif m21Object.hasStyleInformation is False: + elif not m21Object.hasStyleInformation: return else: stObj = m21Object.style @@ -1000,7 +1000,7 @@ def setEditorial(self, mxObject, m21Object): hello ''' - if m21Object.hasEditorialInformation is False: + if not m21Object.hasEditorialInformation: return # MusicXML allows only one footnote or level, so we take the first. @@ -2186,9 +2186,9 @@ def staffGroupToXmlPartGroup(self, staffGroup): self.setPosition(mxGroupSymbol, staffGroup) mxGroupBarline = SubElement(mxPartGroup, 'group-barline') - if staffGroup.barTogether is True: + if staffGroup.barTogether is True: # not a bool. mxGroupBarline.text = 'yes' - elif staffGroup.barTogether is False: + elif staffGroup.barTogether is False: # not a bool. mxGroupBarline.text = 'no' elif staffGroup.barTogether == 'Mensurstrich': mxGroupBarline.text = 'Mensurstrich' @@ -2268,7 +2268,7 @@ def setIdentification(self) -> Element: self.mxIdentification = mxId # creators - foundOne = False + foundOne: bool = False if self.scoreMetadata is not None: # We ignore the name ('namespace:name') here, and use # c.role instead so we can represent non-standard roles. @@ -2282,7 +2282,7 @@ def setIdentification(self) -> Element: mxId.append(mxCreator) foundOne = True - if foundOne is False and defaults.author: + if not foundOne and defaults.author: mxCreator = SubElement(mxId, 'creator') mxCreator.set('type', 'composer') mxCreator.text = defaults.author @@ -3417,12 +3417,12 @@ def parseOneElement( parsedObject = True break - if parsedObject is False: + if not parsedObject: for className in classes: if className in self.ignoreOnParseClasses: parsedObject = True break - if parsedObject is False: + if not parsedObject: environLocal.printDebug(['did not convert object', obj]) else: # appendSpanners == AppendSpanners.RELATED_ONLY @@ -3812,10 +3812,10 @@ def objectAttachedSpannersToNotations( isFirstOrLast = True # print('Trill is last') - if isFirstOrLast is False: + if not isFirstOrLast: continue # do not put a wavy-line tag on mid-trill notes ornaments.append(mxWavyLine) - if isFirstANDLast is True: + if isFirstANDLast: # make another one: mxWavyLine = Element('wavy-line') mxWavyLine.set('number', str(su.idLocal)) @@ -4034,7 +4034,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): TODO: Test with spanners ''' - addChordTag = (noteIndexInChord != 0) + addChordTag: bool = (noteIndexInChord != 0) setb = setAttributeFromAttribute mxNote = Element('note') @@ -4106,7 +4106,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): elif n.isRest: SubElement(mxNote, 'rest') - if d.isGrace is not True: + if d.isGrace: mxDuration = self.durationXml(d) mxNote.append(mxDuration) # divisions only @@ -4168,7 +4168,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): stemDirection = None # if we are not in a chord, or we are the first note of a chord, get stem # direction from the chordOrNote object - if (addChordTag is False + if (not addChordTag and isinstance(chordOrN, note.NotRest) and chordOrN.stemDirection != 'unspecified'): chordOrN = t.cast(note.NotRest, chordOrN) @@ -4211,7 +4211,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): # TODO: notehead-text # beam - if addChordTag is False: + if not addChordTag: if isinstance(chordOrN, note.NotRest) and chordOrN.beams is not None: nBeamsList = self.beamsToXml(chordOrN.beams) for mxB in nBeamsList: @@ -4220,7 +4220,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): mxNotationsList = self.noteToNotations(n, noteIndexInChord, chordParent) # add tuplets if it's a note or the first of a chord. - if addChordTag is False: + if not addChordTag: for i, tup in enumerate(d.tuplets): tupTagList = self.tupletToXmlTuplet(tup, i + 1) mxNotationsList.extend(tupTagList) @@ -4231,7 +4231,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): mxNotations.append(mxN) # lyric - if addChordTag is False: + if not addChordTag: for lyricObj in chordOrN.lyrics: if lyricObj.text is None: continue # happens sometimes! @@ -4815,7 +4815,7 @@ def dealWithNotehead( Returns nothing. The mxNote is modified in place. ''' - foundANotehead = False + foundANotehead: bool = False if (isinstance(n, note.NotRest) and (n.notehead != 'normal' or n.noteheadParenthesis @@ -4825,7 +4825,7 @@ def dealWithNotehead( foundANotehead = True mxNotehead = self.noteheadToXml(n) mxNote.append(mxNotehead) - if foundANotehead is False and chordParent is not None: + if not foundANotehead and chordParent is not None: if (hasattr(chordParent, 'notehead') and (chordParent.notehead != 'normal' or chordParent.noteheadParenthesis @@ -5737,7 +5737,7 @@ def noChordToXml(self, cs: harmony.NoChord) -> Element: quarter ''' - if cs.writeAsChord is True: + if cs.writeAsChord: r = note.Rest(duration=cs.duration) return self.restToXml(r) diff --git a/music21/note.py b/music21/note.py index 7a2ac2d9fb..aaed8c01d5 100644 --- a/music21/note.py +++ b/music21/note.py @@ -542,13 +542,13 @@ def setTextAndSyllabic(self, rawText: str, applyRaw: bool = False) -> None: rawText = str(rawText) # check for hyphens - if applyRaw is False and rawText.startswith('-') and not rawText.endswith('-'): + if not applyRaw and rawText.startswith('-') and not rawText.endswith('-'): self.text = rawText[1:] self.syllabic = 'end' - elif applyRaw is False and not rawText.startswith('-') and rawText.endswith('-'): + elif not applyRaw and not rawText.startswith('-') and rawText.endswith('-'): self.text = rawText[:-1] self.syllabic = 'begin' - elif applyRaw is False and rawText.startswith('-') and rawText.endswith('-'): + elif not applyRaw and rawText.startswith('-') and rawText.endswith('-'): self.text = rawText[1:-1] self.syllabic = 'middle' else: # assume single @@ -823,7 +823,7 @@ def addLyric(self, thisLyric.text = text foundLyric = True break - if foundLyric is False: + if not foundLyric: self.lyrics.append(Lyric(text, lyricNumber, applyRaw=applyRaw, identifier=lyricIdentifier)) diff --git a/music21/repeat.py b/music21/repeat.py index 02f614b23a..22dca1761a 100644 --- a/music21/repeat.py +++ b/music21/repeat.py @@ -452,7 +452,7 @@ def insertRepeatEnding(s, start, end, endingNumber: int = 1, *, inPlace=False): rbOffset = measures[0].getOffsetBySite(s) s.insert(rbOffset, rb) - if inPlace is True: + if inPlace: return else: return s @@ -774,7 +774,7 @@ def process(self, deepcopy: bool = True) -> StreamType: # need to copy source measures, as may later measures before copying # them, and this can result in orphaned spanners - if deepcopy is not False: + if deepcopy: srcStream = self._srcMeasureStream.coreCopyAsDerivation('expandRepeats') else: srcStream = self._srcMeasureStream diff --git a/music21/search/base.py b/music21/search/base.py index c69485b352..b111c9197b 100644 --- a/music21/search/base.py +++ b/music21/search/base.py @@ -267,7 +267,7 @@ def run(self) -> list[SearchMatch]: break if result is False: break - if result is True: + if result: result = None if result is not False: diff --git a/music21/stream/base.py b/music21/stream/base.py index a44db74e67..121bcc0bfd 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -351,7 +351,7 @@ def __init__(self, self._atSoundingPitch: bool|t.Literal['unknown'] = 'unknown' # experimental - self._mutable = True + self._mutable: bool = True if givenElements is None: return @@ -8295,7 +8295,7 @@ def makeImmutable(self): Clean this Stream: for self and all elements, purge all dead locations and remove all non-contained sites. Further, restore all active sites. ''' - if self._mutable is not False: + if self._mutable: self.sort() # must sort before making immutable for e in self.recurse(streamsOnly=True, includeSelf=False): # e.purgeLocations(rescanIsDead=True) @@ -8308,6 +8308,10 @@ def makeImmutable(self): self._mutable = False def makeMutable(self, recurse=True): + ''' + Soft-Deprecated -- this will return a New Stream at some point -- + once immutable, never mutable. + ''' self._mutable = True if recurse: for e in self.recurse(streamsOnly=True): diff --git a/music21/stream/filters.py b/music21/stream/filters.py index ef13f1e491..aee1f5553f 100644 --- a/music21/stream/filters.py +++ b/music21/stream/filters.py @@ -361,15 +361,13 @@ def __init__(self, super().__init__() self.offsetStart = opFrac(offsetStart) + self.zeroLengthSearch: bool = True if offsetEnd is None: self.offsetEnd = offsetStart - self.zeroLengthSearch = True else: self.offsetEnd = opFrac(offsetEnd) if offsetEnd > offsetStart: self.zeroLengthSearch = False - else: - self.zeroLengthSearch = True self.mustFinishInSpan = mustFinishInSpan self.mustBeginInSpan = mustBeginInSpan @@ -432,35 +430,35 @@ def isElementOffsetInRange(self, e, offset, *, stopAfterEnd=False) -> bool: else: elementIsZeroLength = False - if self.zeroLengthSearch is True and elementIsZeroLength is True: + if self.zeroLengthSearch and elementIsZeroLength: # zero Length Searches -- include all zeroLengthElements return True - if self.mustFinishInSpan is True: + if self.mustFinishInSpan: if elementEnd > self.offsetEnd: # environLocal.warn([elementEnd, offsetEnd, e]) return False - if self.includeEndBoundary is False: + if not self.includeEndBoundary: # we include the end boundary if the search is zeroLength -- # otherwise nothing can be retrieved if elementEnd == self.offsetEnd: return False - if self.mustBeginInSpan is True: + if self.mustBeginInSpan: if offset < self.offsetStart: return False - if self.includeEndBoundary is False and offset == self.offsetEnd: + if not self.includeEndBoundary and offset == self.offsetEnd: return False - elif (elementIsZeroLength is False + elif (not elementIsZeroLength and elementEnd == self.offsetEnd - and self.zeroLengthSearch is True): + and self.zeroLengthSearch): return False - if self.includeEndBoundary is False and offset == self.offsetEnd: + if not self.includeEndBoundary and offset == self.offsetEnd: return False - if self.includeElementsThatEndAtStart is False and elementEnd == self.offsetStart: + if not self.includeElementsThatEndAtStart and elementEnd == self.offsetStart: return False return True @@ -473,7 +471,7 @@ class OffsetHierarchyFilter(OffsetFilter): Finds elements that match a given offset range in the hierarchy. - Do not call .stream() afterwards or unstable results can occur. + Do not call .stream() afterward or unstable results can occur. ''' derivationStr = 'getElementsByOffsetInHierarchy' diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py index 238ffff890..95118c114a 100644 --- a/music21/stream/iterator.py +++ b/music21/stream/iterator.py @@ -204,10 +204,10 @@ def __next__(self) -> M21ObjType: continue self.elementIndex += 1 - if self.matchesFilters(e) is False: + if not self.matchesFilters(e): continue - if self.restoreActiveSites is True: + if self.restoreActiveSites: self.srcStream.coreSelfActiveSite(e) self.updateActiveInformation() @@ -1827,10 +1827,10 @@ def __next__(self) -> M21ObjType: childRecursiveIterator.iteratorStartOffsetInHierarchy = newStartOffset self.childRecursiveIterator = childRecursiveIterator - if self.matchesFilters(e) is False: + if not self.matchesFilters(e): continue - if self.restoreActiveSites is True: + if self.restoreActiveSites: self.srcStream.coreSelfActiveSite(e) self.updateActiveInformation() diff --git a/music21/tree/verticality.py b/music21/tree/verticality.py index b37257bb66..789d6466a6 100644 --- a/music21/tree/verticality.py +++ b/music21/tree/verticality.py @@ -1051,7 +1051,7 @@ def getAllVoiceLeadingQuartets( if not hasattr(pairedMotion[0][0], 'pitches'): continue # not a PitchedTimespan - if includeNoMotion is False: + if not includeNoMotion: if (pairedMotion[0][0].pitches == pairedMotion[0][1].pitches and pairedMotion[1][0].pitches == pairedMotion[1][1].pitches): continue @@ -1070,7 +1070,7 @@ def getAllVoiceLeadingQuartets( if not isAppropriate: continue - if returnObjects is False: + if not returnObjects: filteredList.append(pairedMotion) else: n11 = pairedMotion[0][0].element @@ -1160,15 +1160,15 @@ def getPairedMotion( if previousTs is None or not isinstance(previousTs, spans.PitchedTimespan): continue # first not in piece in this part - if includeRests is False: + if not includeRests: if previousTs not in stopTss: continue - if includeOblique is False and startingTs.pitches == previousTs.pitches: + if not includeOblique and startingTs.pitches == previousTs.pitches: continue tsTuple = (previousTs, startingTs) allPairedMotions.append(tsTuple) - if includeOblique is True: + if includeOblique: for overlapTs in overlapTss: if not isinstance(overlapTs, spans.PitchedTimespan): continue diff --git a/music21/variant.py b/music21/variant.py index cf1f6ae392..9adbfd2a41 100644 --- a/music21/variant.py +++ b/music21/variant.py @@ -773,7 +773,7 @@ def mergeVariantScores(aScore, vScore, variantName='variant', *, inPlace=False): raise VariantException( 'These scores do not have the same number of parts and cannot be merged.') - if inPlace is True: + if inPlace: returnObj = aScore else: returnObj = aScore.coreCopyAsDerivation('mergeVariantScores') @@ -781,7 +781,7 @@ def mergeVariantScores(aScore, vScore, variantName='variant', *, inPlace=False): for returnPart, vPart in zip(returnObj.parts, vScore.parts): mergeVariantMeasureStreams(returnPart, vPart, variantName, inPlace=True) - if inPlace is False: + if not inPlace: return returnObj @@ -933,7 +933,7 @@ def mergeVariantMeasureStreams(streamX, streamY, variantName='variant', *, inPla >>> parisStream[variant.Variant][2].replacementQuarterLength 8.0 ''' - if inPlace is True: + if inPlace: returnObj = streamX else: returnObj = streamX.coreCopyAsDerivation('mergeVariantMeasureStreams') @@ -972,7 +972,7 @@ def mergeVariantMeasureStreams(streamX, streamY, variantName='variant', *, inPla addVariant(returnObj, startOffset, yRegion, variantName=variantName, replacementQuarterLength=replacementQuarterLength) - if inPlace is True: + if inPlace: return else: return returnObj @@ -1182,7 +1182,7 @@ def mergeVariantsEqualDuration(streams, variantNames, *, inPlace=False): which are of different lengths ''' - if inPlace is True: + if inPlace: returnObj = streams[0] else: returnObj = streams[0].coreCopyAsDerivation('mergeVariantsEqualDuration') @@ -1333,7 +1333,7 @@ def mergePartAsOssia(mainPart, ossiaPart, ossiaName, ... ''' - if inPlace is True: + if inPlace: returnObj = mainPart else: returnObj = mainPart.coreCopyAsDerivation('mergePartAsOssia') @@ -1376,7 +1376,7 @@ def mergePartAsOssia(mainPart, ossiaPart, ossiaName, variantName=ossiaName, variantGroups=None, replacementQuarterLength=None) - if inPlace is True: + if inPlace: return else: return returnObj @@ -1598,7 +1598,7 @@ def refineVariant(s, sVariant, *, inPlace=False): if sVariant not in s.getElementsByClass(Variant): raise VariantException(f'{sVariant} not found in stream {s}.') - if inPlace is True: + if inPlace: returnObject = s variantRegion = sVariant else: @@ -1689,7 +1689,7 @@ def _mergeVariantMeasureStreamsCarefully(streamX, streamY, variantName, *, inPla ''' # stream that will be returned - if inPlace is True: + if inPlace: returnObject = streamX variantObject = streamY else: @@ -2057,7 +2057,7 @@ def _mergeVariants(streamA, streamB, *, variantName=None, inPlace=False): '_mergeVariants cannot merge streams which are of different lengths' ) - if inPlace is True: + if inPlace: returnObj = streamA else: returnObj = copy.deepcopy(streamA) @@ -2138,7 +2138,7 @@ def _mergeVariants(streamA, streamB, *, variantName=None, inPlace=False): inVariant = False noteBuffer = [] - if inPlace is True: + if inPlace: return None else: return returnObj @@ -2237,7 +2237,7 @@ def makeAllVariantsReplacements(streamWithVariants, ''' - if inPlace is True: + if inPlace: returnStream = streamWithVariants else: returnStream = copy.deepcopy(streamWithVariants) @@ -2249,7 +2249,7 @@ def makeAllVariantsReplacements(streamWithVariants, _doVariantFixingOnStream(returnStream, variantNames=variantNames) - if inPlace is True: + if inPlace: return else: return returnStream From 97d9ba6fb9d1adc3d6ddc8e5c84e8d528ccea67a Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 21 Aug 2026 14:35:27 -1000 Subject: [PATCH 059/312] Fix two inverted conditions from the bool simplification `x is not True` and `x is not False` are negations; fcdb678 rewrote both as bare truthiness tests, flipping their sense. m21ToXml.noteToXml: `d.isGrace is not True` became `d.isGrace`, so was written only for grace notes -- where MusicXML forbids it -- and omitted from every ordinary note. All MusicXML output was invalid. recording.samplesFromRecording: `storeFile is not False` became `not storeFile`, so the wave file was written only when the caller asked for no file, and the `isinstance(storeFile, str)` filename branch became unreachable. Annotation widened to bool|str to match that branch; the function is `# pragma: no cover`, so nothing caught this. AI-assisted (Claude) --- music21/audioSearch/recording.py | 4 ++-- music21/musicxml/m21ToXml.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/music21/audioSearch/recording.py b/music21/audioSearch/recording.py index c390bd32bf..c934d22513 100644 --- a/music21/audioSearch/recording.py +++ b/music21/audioSearch/recording.py @@ -39,7 +39,7 @@ def samplesFromRecording(seconds=10.0, - storeFile: bool = True, + storeFile: bool|str = True, recordFormat=None, recordChannels=default_recordChannels, recordSampleRate=default_recordSampleRate, @@ -78,7 +78,7 @@ def samplesFromRecording(seconds=10.0, st.close() p_audio.terminate() - if not storeFile: + if storeFile: if isinstance(storeFile, str): waveFilename = storeFile else: diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 24ef075cf6..034c215571 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -4106,7 +4106,7 @@ def noteToXml(self, n: note.GeneralNote, noteIndexInChord=0, chordParent=None): elif n.isRest: SubElement(mxNote, 'rest') - if d.isGrace: + if not d.isGrace: mxDuration = self.durationXml(d) mxNote.append(mxDuration) # divisions only From fc440f0e3922ec575744ab94fc3e34afba4f0620 Mon Sep 17 00:00:00 2001 From: Sankalp Thakur Date: Sat, 22 Aug 2026 13:45:06 +0530 Subject: [PATCH 060/312] Add Stream.isAtEnd() --- music21/stream/base.py | 23 +++++++++++++++++++++++ music21/stream/tests.py | 26 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/music21/stream/base.py b/music21/stream/base.py index 121bcc0bfd..77bb32c3ce 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -2699,6 +2699,29 @@ def storeAtEnd(self, itemOrList, ignoreSort=False): # Streams cannot reside in end elements, thus do not update is flat self.coreElementsChanged(updateIsFlat=False) + def isAtEnd(self, element: base.Music21Object) -> bool: + ''' + Return True if `element` is stored at the end of this Stream + (via :meth:`~music21.stream.base.Stream.storeAtEnd`). + + >>> s = stream.Stream() + >>> n = note.Note('C') + >>> s.append(n) + >>> b = bar.Barline() + >>> s.storeAtEnd(b) + >>> s.isAtEnd(b) + True + >>> s.isAtEnd(n) + False + + The element must already be in this Stream. + + * New in v11. + + AI-assisted. + ''' + return self.elementOffset(element, returnSpecial=True) is OffsetSpecial.AT_END + # -------------------------------------------------------------------------- # all the following call either insert() or append() diff --git a/music21/stream/tests.py b/music21/stream/tests.py index 5f28356852..52e06d0916 100644 --- a/music21/stream/tests.py +++ b/music21/stream/tests.py @@ -5063,6 +5063,32 @@ def testStoreAtEndFailures(self): with self.assertRaises(StreamException): s.storeAtEnd(b2) + def testIsAtEnd(self): + ''' + https://github.com/cuthbertLab/music21/issues/1069 + + AI-assisted. + ''' + s = Stream() + n = note.Note('C') + s.append(n) + b = bar.Barline() + s.storeAtEnd(b) + self.assertTrue(s.isAtEnd(b)) + self.assertFalse(s.isAtEnd(n)) + + m = Measure() + nM = note.Note('D') + m.append(nM) + rb = bar.Barline('final') + m.storeAtEnd(rb) + self.assertTrue(m.isAtEnd(rb)) + self.assertFalse(m.isAtEnd(nM)) + + outsider = note.Note('E') + with self.assertRaises(sites.SitesException): + s.isAtEnd(outsider) + def testElementsHighestTimeB(self): ''' Test adding elements at the highest time position From dba4ee5cf6b2f9853b546233136bb8e8914b667a Mon Sep 17 00:00:00 2001 From: Sankalp Thakur Date: Sun, 23 Aug 2026 10:28:51 +0530 Subject: [PATCH 061/312] docs: show SitesException when isAtEnd() is called on a missing element --- music21/stream/base.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/music21/stream/base.py b/music21/stream/base.py index 77bb32c3ce..0fa99662bf 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -2714,7 +2714,13 @@ def isAtEnd(self, element: base.Music21Object) -> bool: >>> s.isAtEnd(n) False - The element must already be in this Stream. + If the element is not in this Stream, :exc:`~music21.sites.SitesException` + is raised: + + >>> s.isAtEnd(note.Note('E')) + Traceback (most recent call last): + music21.sites.SitesException: an entry for this object 0x... is not stored in stream + * New in v11. From c9bd0d07c94e204dcfd816ae3a915903b401dee1 Mon Sep 17 00:00:00 2001 From: Sankalp Thakur Date: Mon, 24 Aug 2026 06:58:14 +0530 Subject: [PATCH 062/312] Use isAtEnd() in quantize() to skip elements stored at the end quantize() iterated the private _elements to avoid touching elements stored at the end. Iterate the public .elements instead and skip them with the new isAtEnd(), which is the use case the method was added for. This is not only cosmetic: elementOffset() returns a concrete offset for an at-end element, so quantizing one would overwrite its AT_END marker while leaving it in _endElements. The added test fails without the skip. --- music21/stream/base.py | 5 ++++- music21/stream/tests.py | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/music21/stream/base.py b/music21/stream/base.py index 0fa99662bf..a8f2a9bcf3 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -9529,7 +9529,10 @@ def findNextElementNotCoincident( # is unsorted originally, this "looking ahead" could become O(n^2). originallySorted = useStream.isSorted rests_lacking_durations: list[note.Rest] = [] - for i, e in enumerate(useStream._elements): + for i, e in enumerate(useStream.elements): + if useStream.isAtEnd(e): + # Elements stored at the end carry no quantizable offset. + continue if processOffsets: o = useStream.elementOffset(e) sign = 1 diff --git a/music21/stream/tests.py b/music21/stream/tests.py index 52e06d0916..0686170f98 100644 --- a/music21/stream/tests.py +++ b/music21/stream/tests.py @@ -5089,6 +5089,26 @@ def testIsAtEnd(self): with self.assertRaises(sites.SitesException): s.isAtEnd(outsider) + def testQuantizeKeepsElementsStoredAtEnd(self): + ''' + quantize() iterates .elements and skips elements stored at the end, so a + barline keeps its AT_END position instead of being given a real offset. + + AI-assisted. + ''' + s = Stream() + n = note.Note() + n.quarterLength = 0.26 + s.repeatInsert(n, [0.1, 0.49, 0.9]) + b = bar.Barline() + s.storeAtEnd(b) + + q = s.quantize(processOffsets=True, processDurations=True, inPlace=False) + + quantizedBarline = q._endElements[0] + self.assertTrue(q.isAtEnd(quantizedBarline)) + self.assertEqual([q.elementOffset(e) for e in q._elements], [0.0, 0.5, 1.0]) + def testElementsHighestTimeB(self): ''' Test adding elements at the highest time position From 14acfa0f29f18585a64a5b25a836e83395f86411 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:28:05 -1000 Subject: [PATCH 063/312] KeySignature.sharps is always an int; isNonTraditional is a settable attribute `sharps` was `int|None`, with `None` meaning "non-traditional key signature." That made every caller either handle a None it would never see or paper over it (`self.sharps or 0` in asKey), and it left `isNonTraditional` as a derived read-only property. Now `sharps` is always an int and `isNonTraditional` is a plain bool attribute that users set directly. The property was declared as `sharps = property(_getSharps, _setSharps, ...)`, which mypy cannot see through -- `ks.sharps` revealed as `Any`, so annotating the getter alone bought nothing. Converted to the decorator form, and `reveal_type(ks.sharps)` is now `int`. `KeySignature(sharps=None)` still works for this cycle: it warns Music21DeprecationWarning and sets sharps=0, isNonTraditional=True. mypy flags it at the call site, which is the intended pressure during the deprecation. Only MusicXML supports non-traditional key signatures; MEI, ABC, humdrum, capella, musedata, noteworthy and romanText all build from an int. Annotating nonTraditionalKeySignature surfaced three latent errors in its body, where Element.text is str|None: an unannotated accidentals list, float(c.text), and a list[Pitch] assigned through the invariant alteredPitches setter (that setter now takes an Iterable). Also drops the unreachable "sharps None" braille message and the None fixup in sharpsToPitch. Version bumped for the pickle cache: the cache is version-keyed, and a master checkout reading b8 pickles written by this code fails 123 tests with "property 'isNonTraditional' of 'Key' object has no setter". Separately, fixes music21/duration.py's TupletFixer.fixBrokenTupletDuration docstring, which set humdrum.spineParser.flavors['JRP'] = True and never restored it. `flavors` is a module-level dict, so the leak was global and permanent for the process: under pytest's alphabetical order duration.py runs before humdrum/tests.py, and testSingleNote then parsed `40..` in JRP flavor, putting the two dots on the note (dots == 2) instead of the tuplet's durationNormal (dots == 0). CI never saw it because testSingleCoreAll sorts modules by mtime, and on a fresh clone all mtimes tie and it falls back to reverse-alphabetical, running humdrum before duration -- but any PR touching duration.py would have pushed it to the front and tripped the failure. AI-assisted (Claude) --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/braille/basic.py | 2 +- music21/duration.py | 2 + music21/key.py | 144 +++++++++++++++++------------------ music21/musicxml/m21ToXml.py | 5 +- music21/musicxml/xmlToM21.py | 17 +++-- 7 files changed, 87 insertions(+), 87 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index 240ab9a1f5..d411b29446 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b8' +__version__ = '11.0.0b9' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 17d9f0a97f..c00c214eb8 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b8' +'11.0.0b9' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/braille/basic.py b/music21/braille/basic.py index 7eb02e4efc..22d600e972 100644 --- a/music21/braille/basic.py +++ b/music21/braille/basic.py @@ -453,7 +453,7 @@ def keySigToBraille(music21KeySignature, outgoingKeySig=None): f'Incoming Key Signature {music21KeySignature} cannot be transcribed to braille.' ) music21KeySignature.editorial.brailleEnglish.append( - f'Key Signature {music21KeySignature} sharps None' + f'Key Signature {music21KeySignature} cannot be transcribed' ) return symbols['basic_exception'] diff --git a/music21/duration.py b/music21/duration.py index 811087db97..38afca3375 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -3452,6 +3452,7 @@ def fixBrokenTupletDuration(self, tupletGroup: list[note.GeneralNote]) -> None: There is a side format of humdrum that the Josquin Research Project uses for long notes like the 3%2. + >>> saved_JRP_flavor = humdrum.spineParser.flavors['JRP'] #_DOCS_HIDE >>> humdrum.spineParser.flavors['JRP'] = True Since Humdrum parsing is going to apply TupletFixer, we will temporarily @@ -3475,6 +3476,7 @@ def fixBrokenTupletDuration(self, tupletGroup: list[note.GeneralNote]) -> None: {10.6667 - 12.0} >>> duration.TupletFixer.fixBrokenTupletDuration = saved_fixed_broken + >>> humdrum.spineParser.flavors['JRP'] = saved_JRP_flavor #_DOCS_HIDE >>> tf = duration.TupletFixer(m1) >>> tupletGroups = tf.findTupletGroups(incorporateGroupings=True) >>> tupletGroups diff --git a/music21/key.py b/music21/key.py index 6085d9a4d3..05a2d69a01 100644 --- a/music21/key.py +++ b/music21/key.py @@ -85,7 +85,7 @@ def convertKeyStringToMusic21KeyString(textString): return textString -def sharpsToPitch(sharpCount): +def sharpsToPitch(sharpCount: int) -> pitch.Pitch: ''' Given a positive/negative number of sharps, return a Pitch object set to the appropriate major key value. @@ -122,9 +122,6 @@ def sharpsToPitch(sharpCount): >>> key._sharpsToPitchCache[1] ''' - if sharpCount is None: - sharpCount = 0 # fix for C major - if sharpCount in _sharpsToPitchCache: # return a deepcopy of the pitch return copy.deepcopy(_sharpsToPitchCache[sharpCount]) @@ -302,28 +299,16 @@ class KeySignature(base.Music21Object): >>> legal - To set a non-traditional Key Signature, create a KeySignature object - with `sharps=None`, and then set the `alteredPitches` list: - - >>> unusual = key.KeySignature(sharps=None) - >>> unusual.alteredPitches = ['E-', 'G#'] - >>> unusual - - >>> unusual.isNonTraditional - True - - To set a pitch as displayed in a particular octave, create a non-traditional - KeySignature and then set pitches with octaves: + To set a non-traditional Key Signature, set `isNonTraditional` to True and + give the `alteredPitches` list. A pitch with an octave is displayed in that + octave; set `accidentalsApplyOnlyToOctave` to True if it should affect only + that octave: - >>> unusual = key.KeySignature(sharps=None) - >>> unusual.alteredPitches = ['F#4'] + >>> unusual = key.KeySignature() + >>> unusual.isNonTraditional = True + >>> unusual.alteredPitches = ['E-', 'G#4'] >>> unusual - - - If the accidental applies to all octaves but is being displayed differently - then you are done, but if you want them to apply only to the octave displayed - in then set `.accidentalsApplyOnlyToOctave` to `True`: - + >>> unusual.accidentalsApplyOnlyToOctave False >>> unusual.accidentalsApplyOnlyToOctave = True @@ -343,9 +328,11 @@ class KeySignature(base.Music21Object): * Changed in v7: `sharps` defaults to 0 (key of no flats/sharps) rather than `None` for nontraditional keys. + * Changed in v11: `sharps` is always an int; `isNonTraditional` is a + settable attribute. Passing `sharps=None` is deprecated. ''' _styleClass = style.TextStyle - equalityAttributes = ('sharps',) + equalityAttributes = ('sharps', 'isNonTraditional') # note that musicxml permits non-traditional keys by specifying # one or more altered tones; these are given as pairs of @@ -353,12 +340,20 @@ class KeySignature(base.Music21Object): classSortOrder = 2 - def __init__(self, sharps: int|None = 0, **keywords): + def __init__(self, sharps: int = 0, **keywords): super().__init__(**keywords) - # position on the circle of fifths, where 1 is one sharp, -1 is one flat + self.isNonTraditional: bool = False + + if sharps is None: + warnings.warn( + 'sharps=None is deprecated: set isNonTraditional to True instead.', + exceptions21.Music21DeprecationWarning, + stacklevel=2) + sharps = 0 + self.isNonTraditional = True try: - if sharps is not None and (sharps != int(sharps)): + if sharps != int(sharps): raise KeySignatureException( f'Cannot get a KeySignature from this "number" of sharps: {sharps!r}; ' + 'did you mean to use a key.Key() object instead?') @@ -368,6 +363,7 @@ def __init__(self, sharps: int|None = 0, **keywords): + 'did you mean to use a key.Key() object instead?' ) from ve + # position on the circle of fifths, where 1 is one sharp, -1 is one flat self._sharps = sharps # need to store a list of pitch objects, used for creating a # non-traditional key @@ -379,7 +375,7 @@ def __init__(self, sharps: int|None = 0, **keywords): def _strDescription(self): output = '' ns = self.sharps - if ns is None: + if self.isNonTraditional: output = 'pitches: [' + ', '.join([str(p) for p in self.alteredPitches]) + ']' elif ns > 1: output = f'{ns} sharps' @@ -396,7 +392,7 @@ def _strDescription(self): def _reprInternal(self): return 'of ' + self._strDescription() - def asKey(self, mode: str|None = None, tonic: str|None = None): + def asKey(self, mode: str|None = None, tonic: str|None = None) -> Key: ''' Return a `key.Key` object representing this KeySignature object as a key in the given mode or in the given tonic. If `mode` is None, and `tonic` is not provided, @@ -429,7 +425,6 @@ def asKey(self, mode: str|None = None, tonic: str|None = None): * New in v7: `tonic` argument to solve for mode. ''' - our_sharps = self.sharps or 0 # || 0 in case of None -- non-standard key-signature if mode is not None and tonic is not None: warnings.warn(f'ignoring provided tonic: {tonic}', KeyWarning, stacklevel=2) if mode is None and tonic is None: @@ -438,7 +433,7 @@ def asKey(self, mode: str|None = None, tonic: str|None = None): majorSharpsToMode = {v: k for k, v in modeSharpsAlter.items()} majorSharps = pitchToSharps(tonic) try: - mode = majorSharpsToMode[our_sharps - majorSharps] + mode = majorSharpsToMode[self.sharps - majorSharps] except KeyError as ke: raise KeyException( f'Could not solve for mode from sharps={self.sharps}, tonic={tonic}') from ke @@ -448,7 +443,7 @@ def asKey(self, mode: str|None = None, tonic: str|None = None): raise KeyException(f'Mode {mode} is unknown') sharpAlterationFromMajor = modeSharpsAlter[mode] - pitchObj = sharpsToPitch(our_sharps - sharpAlterationFromMajor) + pitchObj = sharpsToPitch(self.sharps - sharpAlterationFromMajor) return Key(pitchObj.name, mode) @@ -491,7 +486,8 @@ def alteredPitches(self) -> list[pitch.Pitch]: Non-standard, non-traditional key signatures can set their own altered pitches cache. - >>> nonTrad = key.KeySignature(sharps=None) + >>> nonTrad = key.KeySignature() + >>> nonTrad.isNonTraditional = True >>> nonTrad.alteredPitches = ['B-', 'F#', 'E-', 'G#'] >>> nonTrad.alteredPitches [, @@ -503,7 +499,8 @@ def alteredPitches(self) -> list[pitch.Pitch]: Ensure at least something is provided when the user hasn't provided enough info: - >>> nonTrad2 = key.KeySignature(sharps=None) + >>> nonTrad2 = key.KeySignature() + >>> nonTrad2.isNonTraditional = True >>> nonTrad2.alteredPitches [] @@ -512,7 +509,7 @@ def alteredPitches(self) -> list[pitch.Pitch]: return self._alteredPitches post: list[pitch.Pitch] = [] - if self.sharps is None: + if self.isNonTraditional: return post if self.sharps > 0: @@ -536,7 +533,7 @@ def alteredPitches(self) -> list[pitch.Pitch]: return post @alteredPitches.setter - def alteredPitches(self, newAlteredPitches: list[str|pitch.Pitch|note.Note] + def alteredPitches(self, newAlteredPitches: t.Iterable[str|pitch.Pitch|note.Note] ) -> None: self.clearCache() newList: list[pitch.Pitch] = [] @@ -549,31 +546,6 @@ def alteredPitches(self, newAlteredPitches: list[str|pitch.Pitch|note.Note] newList.append(copy.deepcopy(p.pitch)) self._alteredPitches = newList - @property - def isNonTraditional(self) -> bool: - ''' - Returns bool if this is a non-traditional KeySignature: - - >>> g = key.KeySignature(3) - >>> g.isNonTraditional - False - - >>> g = key.KeySignature(sharps=None) - >>> g.alteredPitches = [pitch.Pitch('E`')] - >>> g.isNonTraditional - True - - >>> g - - - >>> g.accidentalByStep('E') - - ''' - if self.sharps is None and self.alteredPitches: - return True - else: - return False - def accidentalByStep(self, step: StepName) -> pitch.Accidental|None: ''' Given a step (C, D, E, F, etc.) return the accidental @@ -844,16 +816,9 @@ def getScale(self, mode='major'): # -------------------------------------------------------------------------- # properties - def _getSharps(self) -> int|None: - return self._sharps - - def _setSharps(self, value: int|None): - if value != self._sharps: - self._sharps = value - self.clearCache() - - sharps = property(_getSharps, _setSharps, - doc=''' + @property + def sharps(self) -> int: + ''' Get or set the number of sharps. If the number is negative then it sets the number of flats. Equivalent to musicxml's 'fifths' attribute. @@ -864,9 +829,14 @@ def _setSharps(self, value: int|None): >>> ks1.sharps = -4 >>> ks1 + ''' + return self._sharps - Can be set to None for a non-traditional key signature - ''') + @sharps.setter + def sharps(self, value: int) -> None: + if value != self._sharps: + self._sharps = value + self.clearCache() class Key(KeySignature, scale.DiatonicScale): @@ -1416,6 +1386,32 @@ def testAsKey(self): # test exception chained from KeyError self.assertIsInstance(cm.exception.__cause__, KeyError) + def testNonTraditional(self): + ''' + AI-assisted (Claude). + ''' + ks = KeySignature(3) + self.assertFalse(ks.isNonTraditional) + + ks = KeySignature() + ks.isNonTraditional = True + ks.alteredPitches = [pitch.Pitch('E`')] + self.assertEqual(repr(ks), '') + 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, KeySignature()) + + def testSharpsNoneDeprecated(self): + ''' + AI-assisted (Claude). + ''' + with self.assertWarns(exceptions21.Music21DeprecationWarning): + ks = KeySignature(sharps=None) + self.assertEqual(ks.sharps, 0) + self.assertTrue(ks.isNonTraditional) + # ------------------------------------------------------------------------------ # define presented order in documentation diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 034c215571..d859d1ff67 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -7340,7 +7340,7 @@ def timeSignatureToXml(self, ts: meter.TimeSignature|meter.SenzaMisuraTimeSignat self.setPrintObject(mxTime, ts) return mxTime - def keySignatureToXml(self, keyOrKeySignature): + def keySignatureToXml(self, keyOrKeySignature: key.KeySignature) -> Element: # noinspection PyShadowingNames ''' returns a key tag from a music21 @@ -7365,7 +7365,8 @@ def keySignatureToXml(self, keyOrKeySignature): major - >>> ksNonTrad = key.KeySignature(sharps=None) + >>> ksNonTrad = key.KeySignature() + >>> ksNonTrad.isNonTraditional = True >>> ksNonTrad.alteredPitches = ['C#', 'E-4'] >>> ksNonTrad diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index fdc7b0a222..8c0970be67 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -6250,7 +6250,7 @@ def mxKeyOctaves(self, mxKey, ks): ks.alteredPitches = alteredPitches - def nonTraditionalKeySignature(self, mxKey): + def nonTraditionalKeySignature(self, mxKey: ET.Element) -> key.KeySignature: # noinspection PyShadowingNames ''' Returns a KeySignature object that represents a nonTraditional Key Signature @@ -6279,17 +6279,17 @@ def nonTraditionalKeySignature(self, mxKey): children = list(mxKey) lastTag = None - steps = [] - alters = [] - accidentals = [] + steps: list[str] = [] + alters: list[float] = [] + accidentals: list[str|None] = [] for c in children: tag = c.tag if lastTag == 'key-alter' and tag == 'key-step': accidentals.append(None) - if tag == 'key-step': + if tag == 'key-step' and c.text: steps.append(c.text) - elif tag == 'key-alter': + elif tag == 'key-alter' and c.text: alters.append(float(c.text)) elif tag == 'key-accidental': accidentals.append(c.text) @@ -6301,9 +6301,10 @@ def nonTraditionalKeySignature(self, mxKey): raise MusicXMLImportException( 'For non traditional signatures each step must have an alter') - ks = key.KeySignature(sharps=None) + ks = key.KeySignature() + ks.isNonTraditional = True - alteredPitches = [] + alteredPitches: list[pitch.Pitch] = [] for step, alter, accidental in zip(steps, alters, accidentals): p = pitch.Pitch(step) if accidental is not None: From 68f2c01670023dc3fcd6ba2641d57b7bce9a6e9d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:41:31 -1000 Subject: [PATCH 064/312] Convert residual property(_getX, _setX) to decorators Roughly sixty properties across 25 modules move from the old `x = property(_getX, _setX, doc=...)` form to `@property` / `@x.setter`. The doc= text becomes the getter's docstring; internal callers of the private helpers (Stream.finalBarline recursing into parts, Measure's barline setters, Music21Object.purgeOrphans) now go through the property. Left as-is where property() is doing something a decorator cannot: - key.sharps: being handled on the sharps-int branch. - tempo.MetronomeMark .text/.number: the setters take an extra optional keyword, and _updateTextFromNumber() calls _setText with it. - scale .pitches/.chord: built from the public getPitches()/getChord(), which take arguments and are also called directly. - Stream.secondsMap: _getSecondsMap takes arguments. - braille/test.py: write-only property(fset=...). Annotating the getters made their types visible to mypy for the first time, which surfaced nineteen latent errors, all fixed here: - Stream.metadata is Metadata|None, so tsvConverter, clercqTemperley and humdrum.spineParser were dereferencing a possible None. Each now holds the Metadata it just inserted in a local. - mixedNumeral took numbers.Real, which mypy accepts for neither float nor Fraction; it now takes int|float|Fraction. - Metadata.fileNumber's setter claimed str while converter passes int|None. - meter.bestTimeSignature rebound the int `numerator` to a float mid-loop. - Duration.consolidate's currentTupletDuration needed OffsetQL, not float. - xmlToM21 assigned RepeatBracket.number from a MusicXML attribute that may be absent; None reached the setter and was caught by the except clause, which is now unnecessary for that case. AI-assisted (Claude) --- music21/analysis/reduction.py | 45 ++-- music21/articulations.py | 24 +- music21/bar.py | 18 +- music21/base.py | 158 ++++++------ music21/common/numberTools.py | 3 +- music21/converter/subConverters.py | 13 +- music21/corpus/chorales.py | 29 ++- music21/duration.py | 67 ++--- music21/dynamics.py | 87 ++++--- music21/expressions.py | 23 +- music21/humdrum/spineParser.py | 21 +- music21/instrument.py | 119 ++++----- music21/layout.py | 56 ++-- music21/metadata/__init__.py | 2 +- music21/meter/base.py | 7 +- music21/musicxml/xmlToM21.py | 2 +- music21/romanText/clercqTemperley.py | 69 ++--- music21/romanText/tsvConverter.py | 24 +- music21/sites.py | 8 +- music21/spanner.py | 185 +++++++------- music21/stream/base.py | 367 +++++++++++++-------------- music21/style.py | 164 ++++++------ music21/tempo.py | 204 +++++++-------- music21/variant.py | 54 ++-- music21/voiceLeading.py | 142 ++++++----- 25 files changed, 951 insertions(+), 940 deletions(-) diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py index 58befa6daa..7628593d9c 100644 --- a/music21/analysis/reduction.py +++ b/music21/analysis/reduction.py @@ -211,7 +211,20 @@ def __init__(self, **keywords): self._chordReduction = None # store a chordal reduction of available - def _setScore(self, value): + @property + def score(self): + ''' + Get or set the Score. Setting the score set a deepcopy of the score; the score + set here will not be altered. + + >>> s = corpus.parse('bwv66.6') + >>> sr = analysis.reduction.ScoreReduction() + >>> sr.score = s + ''' + return self._score + + @score.setter + def score(self, value): if not isinstance(value, stream.Stream): raise ScoreReductionException('cannot set a non Stream') if value.hasPartLikeStreams: @@ -223,20 +236,16 @@ def _setScore(self, value): self._score = s self._score.setDerivationMethod('ScoreReduction', recurse=True) - def _getScore(self): - return self._score - - score = property(_getScore, _setScore, doc=''' - Get or set the Score. Setting the score set a deepcopy of the score; the score - set here will not be altered. - - >>> s = corpus.parse('bwv66.6') - >>> sr = analysis.reduction.ScoreReduction() - >>> sr.score = s - ''') - + @property + def chordReduction(self): + ''' + Get or set a Chord reduction as a Stream or Score. Setting the this values + set a deepcopy of the reduction; the reduction set here will not be altered. + ''' + return self._chordReduction - def _setChordReduction(self, value): + @chordReduction.setter + def chordReduction(self, value): if not isinstance(value, stream.Stream): raise ScoreReductionException('cannot set a non Stream') if value.hasPartLikeStreams(): @@ -247,14 +256,6 @@ def _setChordReduction(self, value): s.insert(0, copy.deepcopy(value)) self._chordReduction = s - def _getChordReduction(self): - return self._chordReduction - - chordReduction = property(_getChordReduction, _setChordReduction, doc=''' - Get or set a Chord reduction as a Stream or Score. Setting the this values - set a deepcopy of the reduction; the reduction set here will not be altered. - ''') - def _extractReductionEvents(self, score, removeAfterParsing=True): diff --git a/music21/articulations.py b/music21/articulations.py index 087589a1bf..95b03e1f11 100644 --- a/music21/articulations.py +++ b/music21/articulations.py @@ -170,10 +170,20 @@ class name without the leading letter lowercase. className = self.__class__.__name__ return common.camelCaseToHyphen(className, replacement=' ') - def _getVolumeShift(self): + @property + def volumeShift(self): + ''' + Get or set the volumeShift of this Articulation. This value, between -1 and 1, + that is used to shift the final Volume of the object it is attached to. + + >>> at1 = articulations.StrongAccent() + >>> at1.volumeShift > 0.1 + True + ''' return self._volumeShift - def _setVolumeShift(self, value): + @volumeShift.setter + def volumeShift(self, value): # value should be between 0 and 1 if value > 1: value = 1 @@ -181,16 +191,6 @@ def _setVolumeShift(self, value): value = -1 self._volumeShift = value - volumeShift = property(_getVolumeShift, _setVolumeShift, doc=''' - Get or set the volumeShift of this Articulation. This value, between -1 and 1, - that is used to shift the final Volume of the object it is attached to. - - - >>> at1 = articulations.StrongAccent() - >>> at1.volumeShift > 0.1 - True - ''') - # ------------------------------------------------------------------------------ class LengthArticulation(Articulation): ''' diff --git a/music21/bar.py b/music21/bar.py index a8104c3f40..05b1c35f01 100644 --- a/music21/bar.py +++ b/music21/bar.py @@ -150,14 +150,9 @@ def _reprInternal(self): return f'type={self.type}' - def _getType(self): - return self._type - - def _setType(self, value): - self._type = standardizeBarType(value) - - type = property(_getType, _setType, - doc=''' + @property + def type(self): + ''' Get and set the Barline type property. >>> b = bar.Barline() @@ -171,7 +166,12 @@ def _setType(self, value): >>> b.type = 'light-light' >>> b.type 'double' - ''') + ''' + return self._type + + @type.setter + def type(self, value): + self._type = standardizeBarType(value) def musicXMLBarStyle(self): ''' diff --git a/music21/base.py b/music21/base.py index 17d9f0a97f..1f01c7aa0e 100644 --- a/music21/base.py +++ b/music21/base.py @@ -1307,10 +1307,9 @@ def purgeOrphans(self, excludeStorageStreams=True) -> None: orphans.append(id(s)) for i in orphans: self.sites.removeById(i) - p = self._getActiveSite() # this can be simplified. + p = self.activeSite if p is not None and id(p) == i: - # noinspection PyArgumentList - self._setActiveSite(None) + self.activeSite = None def purgeLocations(self, rescanIsDead=False) -> None: ''' @@ -2419,20 +2418,48 @@ def previous(self, # ------------------------------------------------------------------------- # properties - def _getActiveSite(self): + @property + def activeSite(self): + ''' + A reference to the most-recent object used to + contain this object. In most cases, this will be a + Stream or Stream sub-class. In most cases, an object's + activeSite attribute is automatically set when the + object is attached to a Stream. + + + >>> n = note.Note('C#4') + >>> p = stream.Part() + >>> p.insert(20.0, n) + >>> n.activeSite is p + True + >>> n.offset + 20.0 + + >>> m = stream.Measure() + >>> m.insert(10.0, n) + >>> n.activeSite is m + True + >>> n.offset + 10.0 + >>> n.activeSite = p + >>> n.offset + 20.0 + ''' # can be None if WEAKREF_ACTIVE: if self._activeSite is None: # leave None return None else: # even if current activeSite is not a weakref, this will work - # environLocal.printDebug(['_getActiveSite() called:', + # environLocal.printDebug(['activeSite getter called:', # 'self._activeSite', self._activeSite]) return common.unwrapWeakref(self._activeSite) else: # pragma: no cover return self._activeSite - def _setActiveSite(self, site: stream.Stream|None): - # environLocal.printDebug(['_setActiveSite() called:', 'self', self, 'site', site]) + @activeSite.setter + def activeSite(self, site: stream.Stream|None): + # environLocal.printDebug(['activeSite setter called:', 'self', self, 'site', site]) # NOTE: this is a performance intensive call if site is not None: @@ -2461,35 +2488,6 @@ def _setActiveSite(self, site: stream.Stream|None): else: # pragma: no cover self._activeSite = site - activeSite = property(_getActiveSite, - _setActiveSite, - doc=''' - A reference to the most-recent object used to - contain this object. In most cases, this will be a - Stream or Stream sub-class. In most cases, an object's - activeSite attribute is automatically set when the - object is attached to a Stream. - - - >>> n = note.Note('C#4') - >>> p = stream.Part() - >>> p.insert(20.0, n) - >>> n.activeSite is p - True - >>> n.offset - 20.0 - - >>> m = stream.Measure() - >>> m.insert(10.0, n) - >>> n.activeSite is m - True - >>> n.offset - 10.0 - >>> n.activeSite = p - >>> n.offset - 20.0 - ''') - @property def offset(self) -> OffsetQL: ''' @@ -2826,24 +2824,9 @@ def informSites(self, changedInformation=None): # noinspection PyCallingNonCallable s.coreElementsChanged(updateIsFlat=False, keepIndex=True) - def _getPriority(self): - return self._priority - - def _setPriority(self, value): - ''' - value is an int. - - Informs all sites of the change. + @property + def priority(self): ''' - if not isinstance(value, int): - raise ElementException('priority values must be integers.') - if self._priority != value: - self._priority = value - self.informSites({'changedElement': 'priority', 'priority': value}) - - priority = property(_getPriority, - _setPriority, - doc=''' Get and set the priority integer value. Priority specifies the order of processing from left (lowest number) @@ -2872,7 +2855,21 @@ def _setPriority(self, value): >>> a.priority = 'high' Traceback (most recent call last): music21.base.ElementException: priority values must be integers. - ''') + ''' + return self._priority + + @priority.setter + def priority(self, value): + ''' + value is an int. + + Informs all sites of the change. + ''' + if not isinstance(value, int): + raise ElementException('priority values must be integers.') + if self._priority != value: + self._priority = value + self.informSites({'changedElement': 'priority', 'priority': value}) # ------------------------------------------------------------------------- # display and writing @@ -3948,31 +3945,9 @@ def beatStrength(self) -> float: except Music21ObjectException: return float('nan') - def _getSeconds(self) -> float: - from music21 import tempo - # do not search if duration is zero - if self.duration.quarterLength == 0.0: - return 0.0 - - ti = self.getContextByClass(tempo.TempoIndication) - if ti is None: - return float('nan') - mm = ti.getSoundingMetronomeMark() - # once we have mm, simply pass in this duration - return mm.durationToSeconds(self.duration) - - def _setSeconds(self, value: int|float) -> None: - from music21 import tempo - ti = self.getContextByClass(tempo.TempoIndication) - if ti is None: - raise Music21ObjectException('this object does not have a TempoIndication in Sites') - mm = ti.getSoundingMetronomeMark() - self.duration = mm.secondsToDuration(value) - for s in self.sites.get(excludeNone=True): - if self in s.elements: - s.coreElementsChanged() # highest time is changed. - - seconds = property(_getSeconds, _setSeconds, doc=''' + @property + def seconds(self) -> float: + ''' 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` @@ -4051,7 +4026,30 @@ def _setSeconds(self, value: int|float) -> None: 'half' * Changed in v6.3: return `nan` instead of raising an exception. - ''') + ''' + from music21 import tempo + # do not search if duration is zero + if self.duration.quarterLength == 0.0: + return 0.0 + + ti = self.getContextByClass(tempo.TempoIndication) + if ti is None: + return float('nan') + mm = ti.getSoundingMetronomeMark() + # once we have mm, simply pass in this duration + return mm.durationToSeconds(self.duration) + + @seconds.setter + def seconds(self, value: int|float) -> None: + from music21 import tempo + ti = self.getContextByClass(tempo.TempoIndication) + if ti is None: + raise Music21ObjectException('this object does not have a TempoIndication in Sites') + mm = ti.getSoundingMetronomeMark() + self.duration = mm.secondsToDuration(value) + for s in self.sites.get(excludeNone=True): + if self in s.elements: + s.coreElementsChanged() # highest time is changed. # ------------------------------------------------------------------------------ diff --git a/music21/common/numberTools.py b/music21/common/numberTools.py index fa465cd536..21c798fad1 100644 --- a/music21/common/numberTools.py +++ b/music21/common/numberTools.py @@ -14,7 +14,6 @@ from functools import cache import math from math import isclose, gcd -import numbers import random from typing import overload, TYPE_CHECKING import unittest @@ -358,7 +357,7 @@ def opFrac(num: OffsetQLIn) -> OffsetQL: raise TypeError(f'Cannot convert num: {num}') -def mixedNumeral(expr: numbers.Real, +def mixedNumeral(expr: int|float|Fraction, limitDenominator: int = defaults.limitOffsetDenominator) -> str: ''' Returns a string representing a mixedNumeral form of a number diff --git a/music21/converter/subConverters.py b/music21/converter/subConverters.py index 884167c492..2cfea252b0 100644 --- a/music21/converter/subConverters.py +++ b/music21/converter/subConverters.py @@ -113,16 +113,17 @@ def parseFile(self, return self.stream - def _getStream(self): + @property + def stream(self): + ''' + Returns or sets the stream in the converter. Must be defined for subConverter to work. + ''' return self._stream - def _setStream(self, newStream): + @stream.setter + def stream(self, newStream): self._stream = newStream - stream = property(_getStream, _setStream, doc=''' - Returns or sets the stream in the converter. Must be defined for subConverter to work. - ''') - def checkShowAbility(self, **keywords) -> bool: ''' return bool on whether the *system* is diff --git a/music21/corpus/chorales.py b/music21/corpus/chorales.py index 16436b514d..fa709545fb 100644 --- a/music21/corpus/chorales.py +++ b/music21/corpus/chorales.py @@ -1394,12 +1394,25 @@ def _initializeNumberList(self): # ---Properties # - Numbering System - def _getNumberingSystem(self): + @property + def numberingSystem(self): + ''' + This property determines which numbering + system to iterate through chorales with. + It can be set to 'bwv', 'kalmus', 'baerenreiter', + 'budapest', or 'riemenschneider'. + It can also be set to 'title' in which case the + iterator needs to be given a list + of chorale titles in .titleList. At this time, + the titles need to be exactly as they + appear in the dictionary it queries. + ''' if self._numberingSystem is None: raise BachException('Numbering System not set.') return self._numberingSystem - def _setNumberingSystem(self, value): + @numberingSystem.setter + def numberingSystem(self, value): if value in ['bwv', 'kalmus', 'baerenreiter', 'budapest', 'riemenschneider']: self._numberingSystem = value # initializes the number list and sets current and highest numbers / indices @@ -1410,18 +1423,6 @@ def _setNumberingSystem(self, value): else: raise BachException(f'{value} is not a valid numbering system for Bach Chorales.') - numberingSystem = property(_getNumberingSystem, _setNumberingSystem, - doc=''' - This property determines which numbering - system to iterate through chorales with. - It can be set to 'bwv', 'kalmus', 'baerenreiter', - 'budapest', or 'riemenschneider'. - It can also be set to 'title' in which case the - iterator needs to be given a list - of chorale titles in .titleList. At this time, - the titles need to be exactly as they - appear in the dictionary it queries.''') - # - Title List @property diff --git a/music21/duration.py b/music21/duration.py index 811087db97..0a0d152d79 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -1841,7 +1841,8 @@ def _updateComponents(self): self._componentsNeedUpdating = False # PUBLIC METHODS # - def _getLinked(self) -> bool: + @property + def linked(self) -> bool: ''' Gets or sets the `.linked` property -- if linked (default) then type, dots, tuplets are always coherent with quarterLength. If not, then they are separate. @@ -1865,7 +1866,8 @@ def _getLinked(self) -> bool: ''' return self._linked - def _setLinked(self, value: bool): + @linked.setter + def linked(self, value: bool): if value not in (True, False): raise TypeError(f'Linked can only be True or False, not {value}') if self._quarterLengthNeedsUpdating: @@ -1878,8 +1880,6 @@ def _setLinked(self, value: bool): self._linked = value - linked = property(_getLinked, _setLinked) - def addDurationTuple(self, dur: DurationTuple|Duration|str|OffsetQLIn, *, @@ -2915,33 +2915,9 @@ def quarterLengthNoTuplets(self) -> float: tot += c.quarterLength return tot - def _getQuarterLength(self) -> OffsetQL: - if self._quarterLengthNeedsUpdating: - self._updateQuarterLength() - return self._qtrLength - - def _setQuarterLength(self, value: OffsetQLIn): - if not self.linked: - # linked durations get this check for free from opFrac below. - if not isfinite(value): - raise ValueError( - f'quarterLength must be a finite number, not {value!r}' - ) - self._qtrLength = value - elif (self._qtrLength != value - or self._componentsNeedUpdating # skip a type update for next type check - or self.type == 'inexpressible'): - value = opFrac(value) - if value == 0.0 and self.linked: - self.clear() - self._qtrLength = value - self.expressionIsInferred = True - self._componentsNeedUpdating = True - self._quarterLengthNeedsUpdating = False - - self.informClient() - - quarterLength = property(_getQuarterLength, _setQuarterLength, doc=''' + @property + def quarterLength(self) -> OffsetQL: + ''' Returns the quarter note length or Sets the quarter note length to the specified value. May be expressed as a float or Fraction. @@ -2990,7 +2966,32 @@ def _setQuarterLength(self, value: OffsetQLIn): >>> d.quarterLength = 1/3 >>> d.quarterLength Fraction(1, 3) - ''') + ''' + if self._quarterLengthNeedsUpdating: + self._updateQuarterLength() + return self._qtrLength + + @quarterLength.setter + def quarterLength(self, value: OffsetQLIn): + if not self.linked: + # linked durations get this check for free from opFrac below. + if not isfinite(value): + raise ValueError( + f'quarterLength must be a finite number, not {value!r}' + ) + self._qtrLength = value + elif (self._qtrLength != value + or self._componentsNeedUpdating # skip a type update for next type check + or self.type == 'inexpressible'): + value = opFrac(value) + if value == 0.0 and self.linked: + self.clear() + self._qtrLength = value + self.expressionIsInferred = True + self._componentsNeedUpdating = True + self._quarterLengthNeedsUpdating = False + + self.informClient() @property def tuplets(self) -> tuple[Tuplet, ...]: @@ -3517,7 +3518,7 @@ def fixBrokenTupletDuration(self, tupletGroup: list[note.GeneralNote]) -> None: return firstTup = tupletGroup[0].duration.tuplets[0] totalTupletDuration = opFrac(firstTup.totalTupletLength()) - currentTupletDuration = 0.0 + currentTupletDuration: OffsetQL = 0.0 smallestTupletTypeOrdinal: int = SMALL_SENTINEL largestTupletTypeOrdinal: int = LARGE_SENTINEL diff --git a/music21/dynamics.py b/music21/dynamics.py index 8261a3ba75..ecd20297a6 100644 --- a/music21/dynamics.py +++ b/music21/dynamics.py @@ -237,23 +237,9 @@ def __init__(self, value=None, **keywords): def _reprInternal(self): return str(self.value) - def _getValue(self): - return self._value - - def _setValue(self, value): - self._value = value - if self._value in longNames: - self.longName = longNames[self._value] - else: - self.longName = None - - if self._value in englishNames: - self.englishName = englishNames[self._value] - else: - self.englishName = None - - value = property(_getValue, _setValue, - doc=''' + @property + def value(self): + ''' Get or set the value of this dynamic, which sets the long and English names of this Dynamic. The value is a string specification. @@ -272,35 +258,25 @@ def _setValue(self, value): 'loud' >>> p.longName 'forte' - ''') + ''' + return self._value - def _getVolumeScalar(self): - if self._volumeScalar is not None: - return self._volumeScalar - # use default - elif self._value in dynamicStrToScalar: - return dynamicStrToScalar[self._value] + @value.setter + def value(self, value): + self._value = value + if self._value in longNames: + self.longName = longNames[self._value] else: - thisDynamic = self._value - # ignore leading s like in sf - if 's' in thisDynamic: - thisDynamic = thisDynamic[1:] - # ignore closing z like in fz - if thisDynamic[-1] == 'z': - thisDynamic = thisDynamic[:-1] - if thisDynamic in dynamicStrToScalar: - return dynamicStrToScalar[thisDynamic] - else: - return dynamicStrToScalar[None] + self.longName = None - def _setVolumeScalar(self, value): - # we can manually set this to be anything, overriding defaults - if common.isNum(value) and 0 <= value <= 1: - self._volumeScalar = value + if self._value in englishNames: + self.englishName = englishNames[self._value] else: - raise DynamicException(f'cannot set as volume scalar to: {value}') + self.englishName = None - volumeScalar = property(_getVolumeScalar, _setVolumeScalar, doc=r''' + @property + def volumeScalar(self): + r''' Get or set the volume scalar for this dynamic. If not explicitly set, a default volume scalar will be provided. Any number between 0 and 1 can be used to set the volume scalar, overriding the expected behavior. @@ -308,7 +284,6 @@ def _setVolumeScalar(self, value): As mezzo is at 0.5, the unit interval range is doubled for generating final output. The default output is 0.5. - >>> d = dynamics.Dynamic('mf') >>> d.volumeScalar 0.55... @@ -319,7 +294,6 @@ def _setVolumeScalar(self, value): >>> d.value 'mf' - int(volumeScalar \* 127) gives the MusicXML tag >>> xmlOut = musicxml.m21ToXml.GeneralObjectExporter().parse(d).decode('utf-8') @@ -333,7 +307,32 @@ def _setVolumeScalar(self, value): ... - ''') + ''' + if self._volumeScalar is not None: + return self._volumeScalar + # use default + elif self._value in dynamicStrToScalar: + return dynamicStrToScalar[self._value] + else: + thisDynamic = self._value + # ignore leading s like in sf + if 's' in thisDynamic: + thisDynamic = thisDynamic[1:] + # ignore closing z like in fz + if thisDynamic[-1] == 'z': + thisDynamic = thisDynamic[:-1] + if thisDynamic in dynamicStrToScalar: + return dynamicStrToScalar[thisDynamic] + else: + return dynamicStrToScalar[None] + + @volumeScalar.setter + def volumeScalar(self, value): + # we can manually set this to be anything, overriding defaults + if common.isNum(value) and 0 <= value <= 1: + self._volumeScalar = value + else: + raise DynamicException(f'cannot set as volume scalar to: {value}') # ------------------------------------------------------------------------------ diff --git a/music21/expressions.py b/music21/expressions.py index af46e55eec..23674a0d42 100644 --- a/music21/expressions.py +++ b/music21/expressions.py @@ -2724,16 +2724,9 @@ def __init__(self, *spannedElements, **keywords): self._placement = None # can above or below or None, after musicxml - def _getPlacement(self): - return self._placement - - def _setPlacement(self, value): - if value is not None and value.lower() not in ['above', 'below']: - raise TrillExtensionException(f'incorrect placement value: {value}') - if value is not None: - self._placement = value.lower() - - placement = property(_getPlacement, _setPlacement, doc=''' + @property + def placement(self): + ''' Get or set the placement as either above, below, or None. >>> s = stream.Stream() @@ -2745,7 +2738,15 @@ def _setPlacement(self, value): A setting of None implies that the placement will be determined by notation software and no particular placement is demanded. - ''') + ''' + return self._placement + + @placement.setter + def placement(self, value): + if value is not None and value.lower() not in ['above', 'below']: + raise TrillExtensionException(f'incorrect placement value: {value}') + if value is not None: + self._placement = value.lower() class TremoloSpanner(spanner.Spanner): diff --git a/music21/humdrum/spineParser.py b/music21/humdrum/spineParser.py index 66aaa12f3f..3c93861b23 100644 --- a/music21/humdrum/spineParser.py +++ b/music21/humdrum/spineParser.py @@ -364,12 +364,19 @@ def parseOpusDataCollections(self, dataCollections: list[list[str]]) -> stream.O hdc = HumdrumDataCollection(dc) sc = hdc.parse() sc.id = 'section_' + str(i + 1) - sc.metadata.number = i + 1 + scoreMetadata = sc.metadata + if scoreMetadata is None: + scoreMetadata = metadata.Metadata() + sc.insert(0, scoreMetadata) + scoreMetadata.number = str(i + 1) opus.append(sc) if dataCollections: - opus.metadata = copy.deepcopy(opus.scores[0].metadata) - opus.metadata.number = 0 + opusMetadata = copy.deepcopy(opus.scores[0].metadata) + if opusMetadata is None: + opusMetadata = metadata.Metadata() + opusMetadata.number = '0' + opus.metadata = opusMetadata self.stream = opus return opus @@ -1198,7 +1205,8 @@ def _getParentSpineType(self) -> str: return '' return parentSpine.spineType - def _getSpineType(self) -> str: + @property + def spineType(self) -> str: ''' Searches the current and parent spineType. ''' @@ -1215,11 +1223,10 @@ def _getSpineType(self) -> str: raise HumdrumException('Could not determine spineType ' + 'for spine with id ' + str(self.id)) - def _setSpineType(self, newSpineType: str = '') -> None: + @spineType.setter + def spineType(self, newSpineType: str = '') -> None: self._spineType = newSpineType - spineType = property(_getSpineType, _setSpineType) - def moveElementsIntoMeasures(self, streamIn: stream.Stream) -> stream.Stream: # noinspection PyShadowingNames ''' diff --git a/music21/instrument.py b/music21/instrument.py index 799d5c1a8a..0d819a0a90 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -500,7 +500,32 @@ def __init__(self, **keywords): self.midiProgram = 48 - def _getStringPitches(self): + @property + def stringPitches(self): + ''' + stringPitches is a property that stores a list of Pitches (or pitch names, + such as "C4") that represent the pitch of the open strings from lowest to + highest.[*] + + >>> vln1 = instrument.Violin() + >>> [str(p) for p in vln1.stringPitches] + ['G3', 'D4', 'A4', 'E5'] + + instrument.stringPitches are full pitch objects, not just names: + + >>> [x.octave for x in vln1.stringPitches] + [3, 4, 4, 5] + + Scordatura for Scelsi's violin concerto *Anahit*. + (N.B. that string to pitch conversion is happening automatically) + + >>> vln1.stringPitches = ['G3', 'G4', 'B4', 'D4'] + + (`[*]In some tuning methods such as reentrant tuning on the ukulele, + lute, or five-string banjo the order might not strictly be from lowest to + highest. The same would hold true for certain violin scordatura pieces, such + as some of Biber's *Mystery Sonatas*`) + ''' if hasattr(self, '_cachedPitches') and self._cachedPitches is not None: return self._cachedPitches elif not hasattr(self, '_stringPitches'): @@ -509,7 +534,8 @@ def _getStringPitches(self): self._cachedPitches = [pitch.Pitch(x) for x in self._stringPitches] return self._cachedPitches - def _setStringPitches(self, newPitches): + @stringPitches.setter + def stringPitches(self, newPitches): if newPitches and (hasattr(newPitches[0], 'step') or newPitches[0] is None): # newPitches is pitchObjects or something self._stringPitches = newPitches @@ -518,31 +544,6 @@ def _setStringPitches(self, newPitches): self._cachedPitches = None self._stringPitches = newPitches - stringPitches = property(_getStringPitches, _setStringPitches, doc=''' - stringPitches is a property that stores a list of Pitches (or pitch names, - such as "C4") that represent the pitch of the open strings from lowest to - highest.[*] - - >>> vln1 = instrument.Violin() - >>> [str(p) for p in vln1.stringPitches] - ['G3', 'D4', 'A4', 'E5'] - - instrument.stringPitches are full pitch objects, not just names: - - >>> [x.octave for x in vln1.stringPitches] - [3, 4, 4, 5] - - Scordatura for Scelsi's violin concerto *Anahit*. - (N.B. that string to pitch conversion is happening automatically) - - >>> vln1.stringPitches = ['G3', 'G4', 'B4', 'D4'] - - (`[*]In some tuning methods such as reentrant tuning on the ukulele, - lute, or five-string banjo the order might not strictly be from lowest to - highest. The same would hold true for certain violin scordatura pieces, such - as some of Biber's *Mystery Sonatas*`) - ''') - class Violin(StringInstrument): def __init__(self, **keywords): @@ -1125,10 +1126,40 @@ def __init__(self, **keywords): self._percMapPitchToModifier = {} self.midiChannel = 9 # 0-indexed, i.e. MIDI channel 10 - def _getModifier(self): + @property + def modifier(self): + ''' + Returns or sets the modifier for this instrument. A modifier could + be something like "low-floor" for a TomTom or "rimshot" for a SnareDrum. + + If the modifier is in the object's ._modifierToPercMapPitch dictionary + then changing the modifier also changes the .percMapPitch for the object + + >>> bd = instrument.BongoDrums() + >>> bd.modifier + 'high' + + >>> bd.percMapPitch + 60 + >>> bd.modifier = 'low' + >>> bd.percMapPitch + 61 + + Variations on modifiers can also be used and they get normalized: + + >>> wb1 = instrument.Woodblock() + >>> wb1.percMapPitch + 76 + >>> wb1.modifier = 'LO' + >>> wb1.percMapPitch + 77 + >>> wb1.modifier # n.b. -- not LO + 'low' + ''' return self._modifier - def _setModifier(self, modifier): + @modifier.setter + def modifier(self, modifier): modifier = modifier.lower().strip() # BEN: to-do, pull out hyphens, spaces, etc. @@ -1141,36 +1172,6 @@ def _setModifier(self, modifier): self._modifier = modifier - modifier = property(_getModifier, _setModifier, doc=''' - Returns or sets the modifier for this instrument. A modifier could - be something like "low-floor" for a TomTom or "rimshot" for a SnareDrum. - - If the modifier is in the object's ._modifierToPercMapPitch dictionary - then changing the modifier also changes the .percMapPitch for the object - - - >>> bd = instrument.BongoDrums() - >>> bd.modifier - 'high' - - >>> bd.percMapPitch - 60 - >>> bd.modifier = 'low' - >>> bd.percMapPitch - 61 - - Variations on modifiers can also be used and they get normalized: - - >>> wb1 = instrument.Woodblock() - >>> wb1.percMapPitch - 76 - >>> wb1.modifier = 'LO' - >>> wb1.percMapPitch - 77 - >>> wb1.modifier # n.b. -- not LO - 'low' - ''') - class Vibraphone(PitchedPercussion): def __init__(self, **keywords): diff --git a/music21/layout.py b/music21/layout.py index e423c2c817..11dcf60f08 100644 --- a/music21/layout.py +++ b/music21/layout.py @@ -442,10 +442,27 @@ def __init__(self, # -------------------------------------------------------------------------- - def _getBarTogether(self) -> t.Literal[True, False, None, 'Mensurstrich']: + @property + def barTogether(self) -> t.Literal[True, False, None, 'Mensurstrich']: + ''' + Get or set the barTogether value, with either Boolean values + or yes or no strings. Or the string 'Mensurstrich' which + indicates barring between staves but not in staves. + + Currently Mensurstrich is not supported by most exporters. + + >>> sg = layout.StaffGroup() + >>> sg.barTogether = 'yes' + >>> sg.barTogether + True + >>> sg.barTogether = 'Mensurstrich' + >>> sg.barTogether + 'Mensurstrich' + ''' return self._barTogether - def _setBarTogether(self, value: t.Literal[True, False, None, 'Mensurstrich', 'yes', 'no']): + @barTogether.setter + def barTogether(self, value: t.Literal[True, False, None, 'Mensurstrich', 'yes', 'no']): if value is None: pass # do nothing for now; could set a default elif value in ['yes', True]: @@ -457,26 +474,20 @@ def _setBarTogether(self, value: t.Literal[True, False, None, 'Mensurstrich', 'y else: raise StaffGroupException(f'the bar together value {value} is not acceptable') - barTogether = property(_getBarTogether, _setBarTogether, doc=''' - Get or set the barTogether value, with either Boolean values - or yes or no strings. Or the string 'Mensurstrich' which - indicates barring between staves but not in staves. - - Currently Mensurstrich is not supported by most exporters. + @property + def symbol(self) -> t.Literal['bracket', 'line', 'brace', 'square']|None: + ''' + Get or set the symbol value, with either Boolean values or yes or no strings. >>> sg = layout.StaffGroup() - >>> sg.barTogether = 'yes' - >>> sg.barTogether - True - >>> sg.barTogether = 'Mensurstrich' - >>> sg.barTogether - 'Mensurstrich' - ''') - - def _getSymbol(self) -> t.Literal['bracket', 'line', 'brace', 'square']|None: + >>> sg.symbol = 'Brace' + >>> sg.symbol + 'brace' + ''' return self._symbol - def _setSymbol(self, value: t.Literal['bracket', 'line', 'brace', 'square']|None): + @symbol.setter + def symbol(self, value: t.Literal['bracket', 'line', 'brace', 'square']|None): if value is None or str(value).lower() == 'none': self._symbol = None elif value.lower() in ['brace', 'line', 'bracket', 'square']: @@ -484,15 +495,6 @@ def _setSymbol(self, value: t.Literal['bracket', 'line', 'brace', 'square']|None else: raise StaffGroupException(f'the symbol value {value} is not acceptable') - symbol = property(_getSymbol, _setSymbol, doc=''' - Get or set the symbol value, with either Boolean values or yes or no strings. - - >>> sg = layout.StaffGroup() - >>> sg.symbol = 'Brace' - >>> sg.symbol - 'brace' - ''') - # --------------------------------------------------------------- # Stream subclasses for layout diff --git a/music21/metadata/__init__.py b/music21/metadata/__init__.py index 7b94721977..e08de8796b 100755 --- a/music21/metadata/__init__.py +++ b/music21/metadata/__init__.py @@ -1456,7 +1456,7 @@ def fileNumber(self) -> str|None: return self._getSingularAttribute('fileNumber') @fileNumber.setter - def fileNumber(self, value: str) -> None: + def fileNumber(self, value: str|int|None) -> None: ''' For type checking only. Does not run. ''' diff --git a/music21/meter/base.py b/music21/meter/base.py index 69d82b2716..835752ba93 100644 --- a/music21/meter/base.py +++ b/music21/meter/base.py @@ -195,14 +195,15 @@ def bestTimeSignature(meas: stream.Stream) -> 'music21.meter.TimeSignature': break # numerator is the count of min parts in the sum multiplier = 1 + numeratorFloat = 0.0 while i > 0: - numerator = multiplier * sumDurQL / minDurQL - if numerator == int(numerator): + numeratorFloat = multiplier * sumDurQL / minDurQL + if numeratorFloat == int(numeratorFloat): break multiplier *= 2 i -= 1 - numerator = int(numerator) + numerator = int(numeratorFloat) floatDenominator *= multiplier denominator = int(floatDenominator) # simplifies to "simplest terms," with 4 in denominator, before testing beat strengths diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index fdc7b0a222..9cc87946e3 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -5164,7 +5164,7 @@ def xmlBarline(self, mxBarline: ET.Element) -> None: mxNumber = mxEndingObj.get('number') # RepeatBracket handles comma-separated values, such as "1,2" try: - rb.number = mxNumber + rb.number = mxNumber if mxNumber is not None else 1 except spanner.SpannerException: rb.number = 1 diff --git a/music21/romanText/clercqTemperley.py b/music21/romanText/clercqTemperley.py index b4917e1d4f..e3af1d3b3e 100644 --- a/music21/romanText/clercqTemperley.py +++ b/music21/romanText/clercqTemperley.py @@ -609,8 +609,7 @@ def toPart( m.number = i + 1 partObj.append(measures) - partObj.insert(0, metadata.Metadata()) - partObj.metadata.title = self.title + partObj.insert(0, metadata.Metadata(title=self.title)) self._partObj = partObj return partObj @@ -672,15 +671,17 @@ def _reprInternal(self) -> str: return f'text={self.text!r}' # -------------------------------------------------------------------------- - def _getParent(self) -> CTSong|None: + @property + def parent(self) -> CTSong|None: + r''' + A reference to the CTSong object housing the CTRule if any. + ''' return common.unwrapWeakref(self._parent) - def _setParent(self, parent: CTSong) -> None: + @parent.setter + def parent(self, parent: CTSong) -> None: self._parent = common.wrapWeakref(parent) - parent = property(_getParent, _setParent, doc=r''' - A reference to the CTSong object housing the CTRule if any. - ''') # -------------------------------------------------------------------------- def expand( @@ -1007,10 +1008,18 @@ def fixupChordAtom(self, atom: str) -> str: return atom # -------------------------------------------------------------------------- - def _setMusicText(self, value: str) -> None: - self._musicText = str(value) + @property + def musicText(self) -> str: + ''' + Gets just the music text of the CTRule, excluding the left hand side and comments - def _getMusicText(self) -> str: + >>> rs = 'In: $BP*3 I IV | I | $BP*3 I IV | I | R |*4 I |*4 % This is a comment' + >>> s = romanText.clercqTemperley.CTRule(rs) + >>> s.text + 'In: $BP*3 I IV | I | $BP*3 I IV | I | R |*4 I |*4 % This is a comment' + >>> s.musicText + '$BP*3 I IV | I | $BP*3 I IV | I | R |*4 I |*4' + ''' if self._musicText: return self._musicText @@ -1025,16 +1034,9 @@ def _getMusicText(self) -> str: self._musicText = text.strip() return self._musicText - musicText = property(_getMusicText, _setMusicText, doc=''' - Gets just the music text of the CTRule, excluding the left hand side and comments - - >>> rs = 'In: $BP*3 I IV | I | $BP*3 I IV | I | R |*4 I |*4 % This is a comment' - >>> s = romanText.clercqTemperley.CTRule(rs) - >>> s.text - 'In: $BP*3 I IV | I | $BP*3 I IV | I | R |*4 I |*4 % This is a comment' - >>> s.musicText - '$BP*3 I IV | I | $BP*3 I IV | I | R |*4 I |*4' - ''') + @musicText.setter + def musicText(self, value: str) -> None: + self._musicText = str(value) @property def comment(self) -> str|None: @@ -1050,7 +1052,18 @@ def comment(self) -> str|None: return self.text[self.text.index('%') + 1:].strip() return None - def _getLHS(self) -> str: + @property + def LHS(self) -> str: + ''' + Get the LHS (Left Hand Side) of the CTRule. + If not specified explicitly but CTtext present, searches + first characters up until ':' for rule and returns string) + + >>> rs = 'In: $BP*3 I IV | R |*4 I |*4 % This is a comment' + >>> s = romanText.clercqTemperley.CTRule(rs) + >>> s.LHS + 'In' + ''' if self._LHS: return self._LHS @@ -1066,20 +1079,10 @@ def _getLHS(self) -> str: else: return '' - def _setLHS(self, value: str) -> None: + @LHS.setter + def LHS(self, value: str) -> None: self._LHS = str(value) - LHS = property(_getLHS, _setLHS, doc=''' - Get the LHS (Left Hand Side) of the CTRule. - If not specified explicitly but CTtext present, searches - first characters up until ':' for rule and returns string) - - >>> rs = 'In: $BP*3 I IV | R |*4 I |*4 % This is a comment' - >>> s = romanText.clercqTemperley.CTRule(rs) - >>> s.LHS - 'In' - ''') - @property def sectionName(self) -> str: ''' diff --git a/music21/romanText/tsvConverter.py b/music21/romanText/tsvConverter.py index 6f3b628755..9c91ac5877 100644 --- a/music21/romanText/tsvConverter.py +++ b/music21/romanText/tsvConverter.py @@ -660,21 +660,22 @@ def prepStream(self) -> stream.Score: if self.dcml_version == 1: # This sort of metadata seems to have been removed altogether from the # v2 files - s.insert(0, metadata.Metadata()) + md = metadata.Metadata() + s.insert(0, md) firstEntry = self.chordList[0] # Any entry will do title = [] if 'op' in firstEntry.extra: - s.metadata.opusNumber = firstEntry.extra['op'] - title.append('Op' + s.metadata.opusNumber) + md.opusNumber = firstEntry.extra['op'] + title.append('Op' + firstEntry.extra['op']) if 'no' in firstEntry.extra: - s.metadata.number = firstEntry.extra['no'] - title.append('No' + s.metadata.number) + md.number = firstEntry.extra['no'] + title.append('No' + firstEntry.extra['no']) if 'mov' in firstEntry.extra: - s.metadata.movementNumber = firstEntry.extra['mov'] - title.append('Mov' + s.metadata.movementNumber) + md.movementNumber = firstEntry.extra['mov'] + title.append('Mov' + firstEntry.extra['mov']) if title: - s.metadata.title = '_'.join(title) + md.title = '_'.join(title) startingKeySig = str(self.chordList[0].global_key) ks = key.Key(startingKeySig) @@ -816,9 +817,10 @@ def _m21ToTsv_v1(self) -> list[list[str]]: thisEntry.timesig = '' else: thisEntry.timesig = ts.ratioString - thisEntry.extra['op'] = self.m21Stream.metadata.opusNumber or '' - thisEntry.extra['no'] = self.m21Stream.metadata.number or '' - thisEntry.extra['mov'] = self.m21Stream.metadata.movementNumber or '' + md = self.m21Stream.metadata + thisEntry.extra['op'] = (md.opusNumber or '') if md is not None else '' + thisEntry.extra['no'] = (md.number or '') if md is not None else '' + thisEntry.extra['mov'] = (md.movementNumber or '') if md is not None else '' thisEntry.length = thisRN.quarterLength thisEntry.global_key = global_key thisEntry.local_key = thisRN.key.tonicPitchNameWithCase diff --git a/music21/sites.py b/music21/sites.py index 9d9598f33c..b09a695cf6 100644 --- a/music21/sites.py +++ b/music21/sites.py @@ -114,7 +114,8 @@ def _reprInternal(self): return f'{self.siteIndex}/{self.globalSiteIndex} to {siteRepr}' - def _getAndUnwrapSite(self): + @property + def site(self): if WEAKREF_ACTIVE: ret = common.unwrapWeakref(self.siteWeakref) else: @@ -125,15 +126,14 @@ def _getAndUnwrapSite(self): return ret - def _setAndWrapSite(self, site): + @site.setter + def site(self, site): if WEAKREF_ACTIVE: self.siteWeakref = common.wrapWeakref(site) else: self.siteWeakref = site self.isDead = False - site = property(_getAndUnwrapSite, _setAndWrapSite) - # called before pickling. def __getstate__(self): currentSite = None diff --git a/music21/spanner.py b/music21/spanner.py index 0ae0bb55f2..681f7e03c0 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -1772,10 +1772,21 @@ def _numberSpanIsContiguous(self) -> bool: return common.contiguousList(self.numberRange) # property to enforce numerical numbers - def _getNumber(self) -> str: + @property + def number(self) -> str: ''' - This must return a string, as we may have single numbers or lists. - For a raw numerical list, look at `.numberRange`. + Get or set the number -- returning a string always, as we may have + single numbers or lists. For a raw numerical list, look at `.numberRange`. + + >>> rb = spanner.RepeatBracket() + >>> rb.number + '' + >>> rb.number = '5-7' + >>> rb.number + '5-7' + >>> rb.numberRange + [5, 6, 7] + >>> rb.number = 1 ''' if len(self.numberRange) == 1: if self.numberRange[0] == 0: @@ -1789,10 +1800,9 @@ def _getNumber(self) -> str: else: # range of values return f'{self.numberRange[0]}-{self.numberRange[-1]}' - def _setNumber(self, value: int|str|Iterable[int]): - ''' - Set the bracket number. There may be a range of values provided. - ''' + @number.setter + def number(self, value: int|str|Iterable[int]): + # There may be a range of values provided. if value == '': # undefined. self.numberRange = [0] @@ -1822,20 +1832,6 @@ def _setNumber(self, value: int|str|Iterable[int]): else: raise SpannerException(f'number for RepeatBracket must be a number, not {value!r}') - number = property(_getNumber, _setNumber, doc=''' - Get or set the number -- returning a string always. - - >>> rb = spanner.RepeatBracket() - >>> rb.number - '' - >>> rb.number = '5-7' - >>> rb.number - '5-7' - >>> rb.numberRange - [5, 6, 7] - >>> rb.number = 1 - ''') - def _reprInternal(self): if self.overrideDisplay is not None: msg = self.overrideDisplay + ' ' @@ -1929,10 +1925,24 @@ def __init__(self, self.placement = placement # can above or below, after musicxml self.transposing = transposing - def _getType(self): + @property + def type(self): + ''' + Get or set Ottava type. This can be set by as complete string + (such as 8va or 15mb) or with a pair specifying size and direction. + + >>> os = spanner.Ottava() + >>> os.type = '8vb' + >>> os.type + '8vb' + >>> os.type = 15, 'down' + >>> os.type + '15mb' + ''' return self._type - def _setType(self, newType): + @type.setter + def type(self, newType): if common.isNum(newType) and newType in (8, 15): if newType == 8: self._type = '8va' @@ -1959,19 +1969,6 @@ def _setType(self, newType): f'cannot create Ottava of type: {newType}') self._type = newType.lower() - type = property(_getType, _setType, doc=''' - Get or set Ottava type. This can be set by as complete string - (such as 8va or 15mb) or with a pair specifying size and direction. - - >>> os = spanner.Ottava() - >>> os.type = '8vb' - >>> os.type - '8vb' - >>> os.type = 15, 'down' - >>> os.type - '15mb' - ''') - def _reprInternal(self): transposing = 'transposing' if not self.transposing: @@ -2155,62 +2152,56 @@ def __init__( if startHeight is not None: self.startHeight = startHeight # use property - def _getEndTick(self): + @property + def endTick(self): + ''' + Get or set the endTick property. + ''' return self._endTick - def _setEndTick(self, value): + @endTick.setter + def endTick(self, value): if value.lower() not in self.validTickTypes: raise SpannerException(f'not a valid value: {value}') self._endTick = value.lower() - endTick = property(_getEndTick, _setEndTick, doc=''' - Get or set the endTick property. - ''') - - def _getStartTick(self): - return self._startTick - - def _setStartTick(self, value): - if value.lower() not in self.validTickTypes: - raise SpannerException(f'not a valid value: {value}') - self._startTick = value.lower() - - startTick = property(_getStartTick, _setStartTick, doc=''' + @property + def startTick(self): + ''' Get or set the startTick property. - ''') - - def _getTick(self): - return self._startTick # just returning start + ''' + return self._startTick - def _setTick(self, value): + @startTick.setter + def startTick(self, value): if value.lower() not in self.validTickTypes: raise SpannerException(f'not a valid value: {value}') self._startTick = value.lower() - self._endTick = value.lower() - tick = property(_getTick, _setTick, doc=''' + @property + def tick(self): + ''' Set the start and end tick to the same value - >>> b = spanner.Line() >>> b.tick = 'arrow' >>> b.startTick 'arrow' >>> b.endTick 'arrow' - ''') - - def _getLineType(self): - return self._lineType + ''' + return self._startTick # just returning start - def _setLineType(self, value): - if value is not None and value.lower() not in self.validLineTypes: + @tick.setter + def tick(self, value): + if value.lower() not in self.validTickTypes: raise SpannerException(f'not a valid value: {value}') - # not sure if we should permit setting as None - if value is not None: - self._lineType = value.lower() + self._startTick = value.lower() + self._endTick = value.lower() - lineType = property(_getLineType, _setLineType, doc=''' + @property + def lineType(self): + ''' Get or set the lineType property. Valid line types are listed in .validLineTypes. >>> b = spanner.Line() @@ -2221,41 +2212,52 @@ def _setLineType(self, value): >>> b.validLineTypes ('solid', 'dashed', 'dotted', 'wavy') - ''') - - def _getEndHeight(self): - return self._endHeight + ''' + return self._lineType - def _setEndHeight(self, value): - if not (common.isNum(value) and value >= 0): + @lineType.setter + def lineType(self, value): + if value is not None and value.lower() not in self.validLineTypes: raise SpannerException(f'not a valid value: {value}') - self._endHeight = value + # not sure if we should permit setting as None + if value is not None: + self._lineType = value.lower() - endHeight = property(_getEndHeight, _setEndHeight, doc=''' + @property + def endHeight(self): + ''' Get or set the endHeight property. >>> b = spanner.Line() >>> b.endHeight = -20 Traceback (most recent call last): music21.spanner.SpannerException: not a valid value: -20 - ''') - - def _getStartHeight(self): - return self._startHeight + ''' + return self._endHeight - def _setStartHeight(self, value): + @endHeight.setter + def endHeight(self, value): if not (common.isNum(value) and value >= 0): raise SpannerException(f'not a valid value: {value}') - self._startHeight = value + self._endHeight = value - startHeight = property(_getStartHeight, _setStartHeight, doc=''' + @property + def startHeight(self): + ''' Get or set the startHeight property. >>> b = spanner.Line() >>> b.startHeight = None Traceback (most recent call last): music21.spanner.SpannerException: not a valid value: None - ''') + ''' + return self._startHeight + + @startHeight.setter + def startHeight(self, value): + if not (common.isNum(value) and value >= 0): + raise SpannerException(f'not a valid value: {value}') + self._startHeight = value class Glissando(Spanner): @@ -2299,18 +2301,19 @@ def __init__(self, if label is not None: self.label = label # use property - def _getLineType(self): + @property + def lineType(self): + ''' + Get or set the lineType property. See Line for valid line types. + ''' return self._lineType - def _setLineType(self, value): + @lineType.setter + def lineType(self, value): if value.lower() not in self.validLineTypes: raise SpannerException(f'not a valid value: {value}') self._lineType = value.lower() - lineType = property(_getLineType, _setLineType, doc=''' - Get or set the lineType property. See Line for valid line types. - ''') - @property def slideType(self): ''' diff --git a/music21/stream/base.py b/music21/stream/base.py index 121bcc0bfd..d296ad5f55 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -5099,42 +5099,9 @@ def measureOffsetMap( orderedOffsetMap = OrderedDict(sorted(offsetMap.items(), key=lambda o: o[0])) return orderedOffsetMap - def _getFinalBarline(self): - # if we have part-like streams, process each part - if self.hasPartLikeStreams(): - post = [] - for p in self.getElementsByClass('Stream'): - post.append(p._getFinalBarline()) - return post # a list of barlines - # core routines for a single Stream - else: - if self.hasMeasures(): - return self.getElementsByClass(Measure).last().rightBarline - elif hasattr(self, 'rightBarline'): - return self.rightBarline - else: - return None - - def _setFinalBarline(self, value): - # if we have part-like streams, process each part - if self.hasPartLikeStreams(): - if not common.isListLike(value): - value = [value] - for i, p in enumerate(self.getElementsByClass('Stream')): - # set final barline w/ mod iteration of value list - bl = value[i % len(value)] - # environLocal.printDebug(['enumerating measures', i, p, 'setting barline', bl]) - p._setFinalBarline(bl) - return - - # core routines for a single Stream - if self.hasMeasures(): - self.getElementsByClass(Measure).last().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=''' + @property + def finalBarline(self): + ''' 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 none exist. @@ -5193,7 +5160,41 @@ def _setFinalBarline(self, value): * Changed in v6.3: does not raise an exception if queried or set on a measure-less stream. Previously raised a StreamException - ''') + ''' + # if we have part-like streams, process each part + if self.hasPartLikeStreams(): + post = [] + for p in self.getElementsByClass('Stream'): + post.append(p.finalBarline) + return post # a list of barlines + # core routines for a single Stream + else: + if self.hasMeasures(): + return self.getElementsByClass(Measure).last().rightBarline + elif hasattr(self, 'rightBarline'): + return self.rightBarline + else: + return None + + @finalBarline.setter + def finalBarline(self, value): + # if we have part-like streams, process each part + if self.hasPartLikeStreams(): + if not common.isListLike(value): + value = [value] + for i, p in enumerate(self.getElementsByClass('Stream')): + # set final barline w/ mod iteration of value list + bl = value[i % len(value)] + # environLocal.printDebug(['enumerating measures', i, p, 'setting barline', bl]) + p.finalBarline = bl + return + + # core routines for a single Stream + if self.hasMeasures(): + self.getElementsByClass(Measure).last().rightBarline = value + elif hasattr(self, 'rightBarline'): + self.rightBarline = value # pylint: disable=attribute-defined-outside-init + # do nothing for other streams @property def voices(self): @@ -8593,10 +8594,47 @@ def duration(self) -> 'music21.duration.Duration': def duration(self, value: 'music21.duration.Duration'): self._setDuration(value) - def _setSeconds(self, value): - pass + @property + def seconds(self): + ''' + Get or set the duration of this Stream in seconds, assuming that + this object contains a :class:`~music21.tempo.MetronomeMark` or + :class:`~music21.tempo.MetricModulation`. + + >>> s = corpus.parse('bwv66.6') # piece without a tempo + >>> sFlat = s.flatten() + >>> t = tempo.MetronomeMark('adagio') + >>> sFlat.insert(0, t) + >>> sFlat.seconds + 38.57142857... + >>> tFast = tempo.MetronomeMark('allegro') + >>> 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. - def _getSeconds(self): + 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. + ''' getTempoFromContext = False # need to find all tempo indications and the number of quarter lengths # under each @@ -8643,45 +8681,9 @@ def _getSeconds(self): return sec - seconds = property(_getSeconds, _setSeconds, doc=''' - Get or set the duration of this Stream in seconds, assuming that - this object contains a :class:`~music21.tempo.MetronomeMark` or - :class:`~music21.tempo.MetricModulation`. - - >>> s = corpus.parse('bwv66.6') # piece without a tempo - >>> sFlat = s.flatten() - >>> t = tempo.MetronomeMark('adagio') - >>> sFlat.insert(0, t) - >>> sFlat.seconds - 38.57142857... - >>> tFast = tempo.MetronomeMark('allegro') - >>> 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. - ''') + @seconds.setter + def seconds(self, value): + pass def metronomeMarkBoundaries(self, srcObj=None): ''' @@ -8863,22 +8865,28 @@ def _getSecondsMap(self, srcObj=None) -> list[SecondsMapEntry]: # -------------------------------------------------------------------------- # Metadata access - def _getMetadata(self) -> metadata.Metadata|None: + @property + def metadata(self) -> metadata.Metadata|None: ''' - >>> a = stream.Stream() - >>> a.metadata = metadata.Metadata() + Get or set the :class:`~music21.metadata.Metadata` object + found at the beginning (offset 0) of this Stream. + + >>> s = stream.Stream() + >>> s.metadata = metadata.Metadata() + >>> s.metadata.composer = 'frank' + >>> s.metadata.composer + 'frank' + + May also return None if nothing is there. ''' mdList = self.getElementsByClass(metadata.Metadata) # only return metadata that has an offset = 0.0 mdList = mdList.getElementsByOffset(0) return mdList.first() - def _setMetadata(self, metadataObj: metadata.Metadata|None) -> None: - ''' - >>> a = stream.Stream() - >>> a.metadata = metadata.Metadata() - ''' - oldMetadata = self._getMetadata() + @metadata.setter + def metadata(self, metadataObj: metadata.Metadata|None) -> None: + oldMetadata = self.metadata if oldMetadata is not None: # environLocal.printDebug(['removing old metadata', oldMetadata]) junk = self.pop(self.index(oldMetadata)) @@ -8886,20 +8894,6 @@ def _setMetadata(self, metadataObj: metadata.Metadata|None) -> None: if metadataObj is not None and isinstance(metadataObj, metadata.Metadata): self.insert(0, metadataObj) - metadata = property(_getMetadata, _setMetadata, - doc=''' - Get or set the :class:`~music21.metadata.Metadata` object - found at the beginning (offset 0) of this Stream. - - >>> s = stream.Stream() - >>> s.metadata = metadata.Metadata() - >>> s.metadata.composer = 'frank' - >>> s.metadata.composer - 'frank' - - May also return None if nothing is there. - ''') - # -------------------------------------------------------------------------- # these methods override the behavior inherited from base.py @@ -13438,7 +13432,15 @@ def bestTimeSignature(self): ''' return meter.bestTimeSignature(self) - def _getLeftBarline(self): + @property + def leftBarline(self): + ''' + Get or set the left barline, or the Barline object + found at offset zero of the Measure. Can be set either with a string + representing barline style or a bar.Barline() object or None. + Note that not all bars have + barline objects here -- regular barlines don't need them. + ''' barList = [] # directly access _elements, as do not want to get any bars # in _endElements @@ -13452,7 +13454,8 @@ def _getLeftBarline(self): else: return barList[0] - def _setLeftBarline(self, barlineObj): + @leftBarline.setter + def leftBarline(self, barlineObj): insert = True if isinstance(barlineObj, str): barlineObj = bar.Barline(barlineObj) @@ -13462,26 +13465,45 @@ def _setLeftBarline(self, barlineObj): else: # assume a Barline object barlineObj.location = 'left' - oldLeftBarline = self._getLeftBarline() + oldLeftBarline = self.leftBarline if oldLeftBarline is not None: - # environLocal.printDebug(['_setLeftBarline()', 'removing left barline']) + # environLocal.printDebug(['leftBarline setter', 'removing left barline']) junk = self.pop(self.index(oldLeftBarline)) if insert: - # environLocal.printDebug(['_setLeftBarline()', + # environLocal.printDebug(['leftBarline setter', # 'inserting new left barline', barlineObj]) self.insert(0, barlineObj) - leftBarline = property(_getLeftBarline, - _setLeftBarline, - doc=''' - Get or set the left barline, or the Barline object - found at offset zero of the Measure. Can be set either with a string - representing barline style or a bar.Barline() object or None. - Note that not all bars have - barline objects here -- regular barlines don't need them. - ''') + @property + def rightBarline(self): + ''' + Get or set the right barline, or the Barline object + found at the offset equal to the bar duration. - def _getRightBarline(self): + >>> b = bar.Barline('final') + >>> m = stream.Measure() + >>> print(m.rightBarline) + None + >>> m.rightBarline = b + >>> m.rightBarline.type + 'final' + + + A string can also be used instead: + + >>> c = converter.parse('tinynotation: 3/8 C8 D E F G A B4.') + >>> c.measure(1).rightBarline = 'light-light' + >>> c.measure(3).rightBarline = 'light-heavy' + >>> #_DOCS_SHOW c.show() + + .. image:: images/stream_barline_demo.* + :width: 211 + + OMIT_FROM_DOCS + + .measure currently isn't the same as the + original measure. + ''' # TODO: Move to Stream or make setting .rightBarline, etc. on Stream raise an exception # look on _endElements barList = [] @@ -13495,7 +13517,8 @@ def _getRightBarline(self): else: return barList[0] - def _setRightBarline(self, barlineObj): + @rightBarline.setter + def rightBarline(self, barlineObj): insert = True if isinstance(barlineObj, str): barlineObj = bar.Barline(barlineObj) @@ -13510,50 +13533,18 @@ def _setRightBarline(self, barlineObj): # environLocal.printDebug(['got barline obj w/ direction', barlineObj.direction]) if barlineObj.direction in ['start', None]: barlineObj.direction = 'end' - oldRightBarline = self._getRightBarline() + oldRightBarline = self.rightBarline if oldRightBarline is not None: - # environLocal.printDebug(['_setRightBarline()', 'removing right barline']) + # environLocal.printDebug(['rightBarline setter', 'removing right barline']) junk = self.pop(self.index(oldRightBarline)) # insert into _endElements if insert: self.storeAtEnd(barlineObj) - # environLocal.printDebug(['post _setRightBarline', barlineObj, + # environLocal.printDebug(['post rightBarline setter', barlineObj, # 'len of elements highest', len(self._endElements)]) - rightBarline = property(_getRightBarline, - _setRightBarline, - doc=''' - Get or set the right barline, or the Barline object - found at the offset equal to the bar duration. - - >>> b = bar.Barline('final') - >>> m = stream.Measure() - >>> print(m.rightBarline) - None - >>> m.rightBarline = b - >>> m.rightBarline.type - 'final' - - - A string can also be used instead: - - >>> c = converter.parse('tinynotation: 3/8 C8 D E F G A B4.') - >>> c.measure(1).rightBarline = 'light-light' - >>> c.measure(3).rightBarline = 'light-heavy' - >>> #_DOCS_SHOW c.show() - - .. image:: images/stream_barline_demo.* - :width: 211 - - OMIT_FROM_DOCS - - .measure currently isn't the same as the - original measure. - - ''') - class Part(Stream): ''' @@ -13587,26 +13578,9 @@ def __init__(self, *args, **keywords): self._partName = None self._partAbbreviation = None - def _getPartName(self): - if self._partName is not None: - return self._partName - elif '_partName' in self._cache: - return self._cache['_partName'] - else: - pn = None - for e in self[instrument.Instrument]: - pn = e.partName - if pn is None: - pn = e.instrumentName - if pn is not None: - break - self._cache['_partName'] = pn - return pn - - def _setPartName(self, newName): - self._partName = newName - - partName = property(_getPartName, _setPartName, doc=''' + @property + def partName(self): + ''' Gets or sets a string representing the name of this part as a whole (not counting instrument changes, etc.). @@ -13639,28 +13613,29 @@ def _setPartName(self, newName): .coreElementsChanged() is called or this Stream's elements are otherwise altered. This is because the value is cached so that O(n) searches through the Stream do not need to be done every time. - ''') - - def _getPartAbbreviation(self): - if self._partAbbreviation is not None: - return self._partAbbreviation - elif '_partAbbreviation' in self._cache: - return self._cache['_partAbbreviation'] + ''' + if self._partName is not None: + return self._partName + elif '_partName' in self._cache: + return self._cache['_partName'] else: pn = None for e in self[instrument.Instrument]: - pn = e.partAbbreviation + pn = e.partName if pn is None: - pn = e.instrumentAbbreviation + pn = e.instrumentName if pn is not None: break - self._cache['_partAbbreviation'] = pn + self._cache['_partName'] = pn return pn - def _setPartAbbreviation(self, newName): - self._partAbbreviation = newName + @partName.setter + def partName(self, newName): + self._partName = newName - partAbbreviation = property(_getPartAbbreviation, _setPartAbbreviation, doc=''' + @property + def partAbbreviation(self): + ''' Gets or sets a string representing the abbreviated name of this part as a whole (not counting instrument changes, etc.). @@ -13693,7 +13668,25 @@ def _setPartAbbreviation(self, newName): .coreElementsChanged() is called or this Stream's elements are otherwise altered. This is because the value is cached so that O(n) searches through the Stream do not need to be done every time. - ''') + ''' + if self._partAbbreviation is not None: + return self._partAbbreviation + elif '_partAbbreviation' in self._cache: + return self._cache['_partAbbreviation'] + else: + pn = None + for e in self[instrument.Instrument]: + pn = e.partAbbreviation + if pn is None: + pn = e.instrumentAbbreviation + if pn is not None: + break + self._cache['_partAbbreviation'] = pn + return pn + + @partAbbreviation.setter + def partAbbreviation(self, newName): + self._partAbbreviation = newName def makeAccidentals( self, diff --git a/music21/style.py b/music21/style.py index 127b7e81cb..d4f6fb1f4b 100644 --- a/music21/style.py +++ b/music21/style.py @@ -176,27 +176,9 @@ def enclosure(self, value: Enclosure|str|None): else: raise TextFormatException(f'Not a supported enclosure: {value!r}') - def _getAbsoluteY(self): - return self._absoluteY - - def _setAbsoluteY(self, value): - if value is None: - self._absoluteY = None - elif value == 'above': # TODO: convert to Enum and keep it - self._absoluteY = 10 - elif value == 'below': - self._absoluteY = -70 - else: - try: - self._absoluteY = common.numToIntOrFloat(value) - except ValueError as ve: - raise TextFormatException( - f'Not a supported absoluteY position: {value!r}' - ) from ve - - absoluteY = property(_getAbsoluteY, - _setAbsoluteY, - doc=''' + @property + def absoluteY(self): + ''' Get or set the vertical position, where 0 is the top line of the staff and units are whatever is defined in `.units`, generally "tenths", meaning @@ -222,7 +204,24 @@ def _setAbsoluteY(self, value): Traceback (most recent call last): music21.style.TextFormatException: Not a supported absoluteY position: 'hello' - ''') + ''' + return self._absoluteY + + @absoluteY.setter + def absoluteY(self, value): + if value is None: + self._absoluteY = None + elif value == 'above': # TODO: convert to Enum and keep it + self._absoluteY = 10 + elif value == 'below': + self._absoluteY = -70 + else: + try: + self._absoluteY = common.numToIntOrFloat(value) + except ValueError as ve: + raise TextFormatException( + f'Not a supported absoluteY position: {value!r}' + ) from ve class NoteStyle(Style): @@ -307,19 +306,9 @@ def __init__(self): self._alignHorizontal = None self._alignVertical = None - def _getAlignVertical(self): - return self._alignVertical - - def _setAlignVertical(self, value): - # TODO: convert to StrEnum - if value in (None, 'top', 'middle', 'bottom', 'baseline'): - self._alignVertical = value - else: - raise TextFormatException(f'Invalid vertical align: {value!r}') - - alignVertical = property(_getAlignVertical, - _setAlignVertical, - doc=''' + @property + def alignVertical(self): + ''' Get or set the vertical align. Valid values are top, middle, bottom, baseline or None. @@ -334,20 +323,20 @@ def _setAlignVertical(self, value): Traceback (most recent call last): music21.style.TextFormatException: Invalid vertical align: 'hello' - ''') - - def _getAlignHorizontal(self): - return self._alignHorizontal + ''' + return self._alignVertical - def _setAlignHorizontal(self, value): - if value in (None, 'left', 'right', 'center'): - self._alignHorizontal = value + @alignVertical.setter + def alignVertical(self, value): + # TODO: convert to StrEnum + if value in (None, 'top', 'middle', 'bottom', 'baseline'): + self._alignVertical = value else: - raise TextFormatException(f'Invalid horizontal align: {value!r}') + raise TextFormatException(f'Invalid vertical align: {value!r}') - alignHorizontal = property(_getAlignHorizontal, - _setAlignHorizontal, - doc=''' + @property + def alignHorizontal(self): + ''' Get or set the horizontal alignment. Valid values are left, right, center, or None. @@ -362,7 +351,15 @@ def _setAlignHorizontal(self, value): Traceback (most recent call last): music21.style.TextFormatException: Invalid horizontal align: 'hello' - ''') + ''' + return self._alignHorizontal + + @alignHorizontal.setter + def alignHorizontal(self, value): + if value in (None, 'left', 'right', 'center'): + self._alignHorizontal = value + else: + raise TextFormatException(f'Invalid horizontal align: {value!r}') @property @@ -423,10 +420,22 @@ def fontStyle(self, value: str|None) -> None: raise TextFormatException(f'Not a supported fontStyle: {value!r}') self._fontStyle = value.lower() - def _getWeight(self): + # TODO: figure out if we want to use fontStyle for all weights. + + @property + def fontWeight(self): + ''' + Get or set the weight, as normal, or bold. + + >>> tst = style.TextStyle() + >>> tst.fontWeight = 'bold' + >>> tst.fontWeight + 'bold' + ''' return self._fontWeight - def _setWeight(self, value): + @fontWeight.setter + def fontWeight(self, value): if value is None: self._fontWeight = None else: @@ -434,23 +443,20 @@ def _setWeight(self, value): raise TextFormatException(f'Not a supported fontWeight: {value}') self._fontWeight = value.lower() - # TODO: figure out if we want to use fontStyle for all weights. - - fontWeight = property(_getWeight, - _setWeight, - doc=''' - Get or set the weight, as normal, or bold. + @property + def fontSize(self): + ''' + Get or set the size. Best, an int or float, but also a css font size. >>> tst = style.TextStyle() - >>> tst.fontWeight = 'bold' - >>> tst.fontWeight - 'bold' - ''') - - def _getSize(self): + >>> tst.fontSize = 20 + >>> tst.fontSize + 20 + ''' return self._fontSize - def _setSize(self, value): + @fontSize.setter + def fontSize(self, value): if value is not None: try: value = common.numToIntOrFloat(value) @@ -459,21 +465,21 @@ def _setSize(self, value): # raise TextFormatException(f'Not a supported size: {value}') self._fontSize = value - fontSize = property(_getSize, - _setSize, - doc=''' - Get or set the size. Best, an int or float, but also a css font size. + @property + def letterSpacing(self): + ''' + Get or set the letter spacing. >>> tst = style.TextStyle() - >>> tst.fontSize = 20 - >>> tst.fontSize - 20 - ''') - - def _getLetterSpacing(self): + >>> tst.letterSpacing = 20 + >>> tst.letterSpacing + 20.0 + >>> tst.letterSpacing = 'normal' + ''' return self._letterSpacing - def _setLetterSpacing(self, value): + @letterSpacing.setter + def letterSpacing(self, value): if value != 'normal' and value is not None: # convert to number try: @@ -485,18 +491,6 @@ def _setLetterSpacing(self, value): self._letterSpacing = value - letterSpacing = property(_getLetterSpacing, - _setLetterSpacing, - doc=''' - Get or set the letter spacing. - - >>> tst = style.TextStyle() - >>> tst.letterSpacing = 20 - >>> tst.letterSpacing - 20.0 - >>> tst.letterSpacing = 'normal' - ''') - @property def fontFamily(self): ''' diff --git a/music21/tempo.py b/music21/tempo.py index 888ee19b72..6eb003b836 100644 --- a/music21/tempo.py +++ b/music21/tempo.py @@ -200,17 +200,23 @@ def __init__(self, text=None, **keywords): def _reprInternal(self): return repr(self.text) - def _getText(self): + @property + def text(self): ''' - Get the text used for this expression. + Get or set the text as a string. Setting is also the primary way that + the stored TextExpression object is created. + + >>> import music21 + >>> tm = music21.tempo.TempoText('adagio') + >>> tm.text + 'adagio' + >>> tm.getTextExpression() + ''' return self._textExpression.content - def _setText(self, value): - ''' - Set the text of this repeat expression. This is also the primary way - that the stored TextExpression object is created. - ''' + @text.setter + def text(self, value): if self._textExpression is None: self._textExpression = expressions.TextExpression(value) if self.hasStyleInformation: @@ -222,17 +228,6 @@ def _setText(self, value): else: self._textExpression.content = value - text = property(_getText, _setText, doc=''' - Get or set the text as a string. - - >>> import music21 - >>> tm = music21.tempo.TempoText('adagio') - >>> tm.text - 'adagio' - >>> tm.getTextExpression() - - ''') - def getMetronomeMark(self): # noinspection PyShadowingNames ''' @@ -486,10 +481,16 @@ def _updateNumberFromText(self): self.numberImplicit = True # ------------------------------------------------------------------------- - def _getReferent(self): + @property + def referent(self): + ''' + Get or set the referent, or the Duration object that is the + reference for the tempo value in BPM. + ''' return self._referent - def _setReferent(self, value): + @referent.setter + def referent(self, value): if value is None: # this may be better not here # if referent is None, set a default quarter note duration self._referent = duration.Duration(type='quarter') @@ -506,11 +507,6 @@ def _setReferent(self, value): else: raise TempoException(f'Cannot get a Duration from the supplied object: {value}') - referent = property(_getReferent, _setReferent, doc=''' - Get or set the referent, or the Duration object that is the - reference for the tempo value in BPM. - ''') - # properties and conversions def _getText(self): if self._tempoText is None: @@ -576,15 +572,9 @@ def _setNumber(self, value: int|float|None, False ''') - def _getNumberSounding(self): - return self._numberSounding # may be None - - def _setNumberSounding(self, value): - if not common.isNum(value) and value is not None: - raise TempoException('cannot set numberSounding to a string') - self._numberSounding = value - - numberSounding = property(_getNumberSounding, _setNumberSounding, doc=''' + @property + def numberSounding(self): + ''' Get and set the numberSounding, or the numerical value of the Metronome that is used for playback independent of display. If numberSounding is None, number is assumed to be numberSounding. @@ -599,7 +589,14 @@ def _setNumberSounding(self, value): >>> mm.numberSounding = 120 >>> mm.numberSounding 120 - ''') + ''' + return self._numberSounding # may be None + + @numberSounding.setter + def numberSounding(self, value): + if not common.isNum(value) and value is not None: + raise TempoException('cannot set numberSounding to a string') + self._numberSounding = value # ------------------------------------------------------------------------- def getQuarterBPM(self, useNumberSounding=True) -> float|None: @@ -949,21 +946,9 @@ def _reprInternal(self): # -------------------------------------------------------------------------- # core properties - def _setOldMetronome(self, value): - if value is None: - pass # allow setting as None - elif not hasattr(value, 'classes') or 'MetronomeMark' not in value.classes: - raise MetricModulationException( - 'oldMetronome property must be set with a MetronomeMark instance') - self._oldMetronome = value - - def _getOldMetronome(self): - if self._oldMetronome is not None: - if self._oldMetronome.number is None: - self.updateByContext() - return self._oldMetronome - - oldMetronome = property(_getOldMetronome, _setOldMetronome, doc=''' + @property + def oldMetronome(self): + ''' Get or set the left :class:`~music21.tempo.MetronomeMark` object for the old, or previous value. @@ -979,9 +964,40 @@ def _getOldMetronome(self): Traceback (most recent call last): music21.tempo.MetricModulationException: oldMetronome property must be set with a MetronomeMark instance - ''') + ''' + if self._oldMetronome is not None: + if self._oldMetronome.number is None: + self.updateByContext() + return self._oldMetronome + + @oldMetronome.setter + def oldMetronome(self, value): + if value is None: + pass # allow setting as None + elif not hasattr(value, 'classes') or 'MetronomeMark' not in value.classes: + raise MetricModulationException( + 'oldMetronome property must be set with a MetronomeMark instance') + self._oldMetronome = value + + @property + def oldReferent(self): + ''' + Get or set the referent of the old MetronomeMark. + + >>> mm1 = tempo.MetronomeMark(number=60, referent=1) + >>> mmod1 = tempo.MetricModulation() + >>> mmod1.oldMetronome = mm1 + >>> mmod1.oldMetronome + + >>> mmod1.oldReferent = 0.25 + >>> mmod1.oldMetronome + + ''' + if self._oldMetronome is not None: + return self._oldMetronome.referent - def _setOldReferent(self, value): + @oldReferent.setter + def oldReferent(self, value): if value is None: raise MetricModulationException('cannot set old referent to None') # try to get and reassign equivalent @@ -1000,25 +1016,30 @@ def _setOldReferent(self, value): self._oldMetronome = MetronomeMark(referent=value) # raise MetricModulationException('cannot set old MetronomeMark from provided value.') - def _getOldReferent(self): - if self._oldMetronome is not None: - return self._oldMetronome.referent - - oldReferent = property(_getOldReferent, _setOldReferent, doc=''' - Get or set the referent of the old MetronomeMark. + @property + def newMetronome(self): + ''' + Get or set the right :class:`~music21.tempo.MetronomeMark` + object for the new, or following value. >>> mm1 = tempo.MetronomeMark(number=60, referent=1) - >>> mmod1 = tempo.MetricModulation() - >>> mmod1.oldMetronome = mm1 - >>> mmod1.oldMetronome + >>> mm1 - >>> mmod1.oldReferent = 0.25 - >>> mmod1.oldMetronome - - - ''') + >>> mmod1 = tempo.MetricModulation() + >>> mmod1.newMetronome = mm1 + >>> mmod1.newMetronome = 'junk' + Traceback (most recent call last): + music21.tempo.MetricModulationException: newMetronome property must be + set with a MetronomeMark instance + ''' + # before returning the referent, see if we can update the number + if self._newMetronome is not None: + if self._newMetronome.number is None: + self.updateByContext() + return self._newMetronome - def _setNewMetronome(self, value): + @newMetronome.setter + def newMetronome(self, value): if value is None: pass # allow setting as None elif not hasattr(value, 'classes') or 'MetronomeMark' not in value.classes: @@ -1026,29 +1047,25 @@ def _setNewMetronome(self, value): 'newMetronome property must be set with a MetronomeMark instance') self._newMetronome = value - def _getNewMetronome(self): - # before returning the referent, see if we can update the number - if self._newMetronome is not None: - if self._newMetronome.number is None: - self.updateByContext() - return self._newMetronome - - newMetronome = property(_getNewMetronome, _setNewMetronome, doc=''' - Get or set the right :class:`~music21.tempo.MetronomeMark` - object for the new, or following value. + @property + def newReferent(self): + ''' + Get or set the referent of the new MetronomeMark. >>> mm1 = tempo.MetronomeMark(number=60, referent=1) - >>> mm1 - >>> mmod1 = tempo.MetricModulation() >>> mmod1.newMetronome = mm1 - >>> mmod1.newMetronome = 'junk' - Traceback (most recent call last): - music21.tempo.MetricModulationException: newMetronome property must be - set with a MetronomeMark instance - ''') + >>> mmod1.newMetronome + + >>> mmod1.newReferent = 0.25 + >>> mmod1.newMetronome + + ''' + if self._newMetronome is not None: + return self._newMetronome.referent - def _setNewReferent(self, value): + @newReferent.setter + def newReferent(self, value): if value is None: raise MetricModulationException('cannot set new referent to None') # if oldMetronome is defined, get new metronome from old @@ -1065,23 +1082,6 @@ def _setNewReferent(self, value): # raise MetricModulationException('cannot set old MetronomeMark from provided value.') self._newMetronome = mm - def _getNewReferent(self): - if self._newMetronome is not None: - return self._newMetronome.referent - - newReferent = property(_getNewReferent, _setNewReferent, doc=''' - Get or set the referent of the new MetronomeMark. - - >>> mm1 = tempo.MetronomeMark(number=60, referent=1) - >>> mmod1 = tempo.MetricModulation() - >>> mmod1.newMetronome = mm1 - >>> mmod1.newMetronome - - >>> mmod1.newReferent = 0.25 - >>> mmod1.newMetronome - - ''') - @property def number(self): ''' diff --git a/music21/variant.py b/music21/variant.py index 9adbfd2a41..17e2184e32 100644 --- a/music21/variant.py +++ b/music21/variant.py @@ -271,19 +271,9 @@ def containedSite(self): ''' return self._stream - def _getReplacementQuarterLength(self): - if self._replacementQuarterLength is None: - return self._stream.duration.quarterLength - else: - return self._replacementQuarterLength - - def _setReplacementQuarterLength(self, value): - self._replacementQuarterLength = value - - replacementQuarterLength = property( - _getReplacementQuarterLength, - _setReplacementQuarterLength, - doc=''' + @property + def replacementQuarterLength(self): + ''' Set or Return the quarterLength in the main stream which this variant object replaces in the variant version of the stream. If replacementQuarterLength is not set, it is assumed to be the same length as the variant. If it is set to 0, @@ -292,23 +282,19 @@ def _setReplacementQuarterLength(self, value): itself. * New in v10.3: renamed from ``replacementDuration``. - ''') - - def _getReplacementDuration(self): - return self.replacementQuarterLength + ''' + if self._replacementQuarterLength is None: + return self._stream.duration.quarterLength + else: + return self._replacementQuarterLength - def _setReplacementDuration(self, value): - warnings.warn( - "'replacementDuration' is deprecated as of v11 and will be removed in v12; " - + 'use the synonym replacementQuarterLength instead.', - exceptions21.Music21DeprecationWarning, - stacklevel=2) - self.replacementQuarterLength = value + @replacementQuarterLength.setter + def replacementQuarterLength(self, value): + self._replacementQuarterLength = value - replacementDuration = property( - _getReplacementDuration, - _setReplacementDuration, - doc=''' + @property + def replacementDuration(self): + ''' Synonym for :attr:`replacementQuarterLength`. .. note:: @@ -317,7 +303,17 @@ def _setReplacementDuration(self, value): avoids referring to an offset/quarterLength value as a "Duration"). Setting it raises a deprecation warning, and it will be removed in v12. Use :attr:`replacementQuarterLength` instead. - ''') + ''' + return self.replacementQuarterLength + + @replacementDuration.setter + def replacementDuration(self, value): + warnings.warn( + "'replacementDuration' is deprecated as of v11 and will be removed in v12; " + + 'use the synonym replacementQuarterLength instead.', + exceptions21.Music21DeprecationWarning, + stacklevel=2) + self.replacementQuarterLength = value @property def lengthType(self): diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py index b6a23659bb..0387ac7717 100644 --- a/music21/voiceLeading.py +++ b/music21/voiceLeading.py @@ -1801,42 +1801,44 @@ def getVerticalityOffset(self, *, leftAlign=True): else: return sorted(self.objects, key=lambda m21Obj: m21Obj.offset)[-1].offset - def _setLyric(self, value): - newList = sorted(self.objects, key=lambda x: x.offset, reverse=True) - newList[0].lyric = value - - def _getLyric(self): - newList = sorted(self.objects, key=lambda x: x.offset, reverse=True) - return newList[0].lyric - - lyric = property(_getLyric, _setLyric, doc=''' + @property + def lyric(self): + ''' Sets each object on the Verticality to have the passed in lyric. >>> h = voiceLeading.Verticality({1: note.Note('C'), 2: harmony.ChordSymbol('C')}) >>> h.lyric = 'Verticality 1' >>> h.getStream().flatten().getElementsByClass(note.Note).first().lyric 'Verticality 1' - ''') + ''' + newList = sorted(self.objects, key=lambda x: x.offset, reverse=True) + return newList[0].lyric + + @lyric.setter + def lyric(self, value): + newList = sorted(self.objects, key=lambda x: x.offset, reverse=True) + newList[0].lyric = value def _reprInternal(self): return f'contentDict={self.contentDict}' - def _setColor(self, color): - self.style.color = color - for obj in self.objects: - obj.style.color = color - - def _getColor(self): - return self.style.color - - color = property(_getColor, _setColor, doc=''' + @property + def color(self): + ''' Sets the color of each element in the Verticality. >>> vs1 = voiceLeading.Verticality({1:note.Note('C'), 2:harmony.ChordSymbol('D')}) >>> vs1.color = 'blue' >>> [(x, x.style.color) for x in vs1.objects] [(, 'blue'), (, 'blue')] - ''') + ''' + return self.style.color + + @color.setter + def color(self, color): + self.style.color = color + for obj in self.objects: + obj.style.color = color class VerticalityNTuplet(base.Music21Object): @@ -2010,18 +2012,9 @@ def noteList(self): return self._noteList[:] - def _getMelodicIntervals(self): - tempListOne = self.noteList[:-1] - tempListTwo = self.noteList[1:] - melodicIntervalList = [] - for n1, n2 in zip(tempListOne, tempListTwo, strict=True): - if n1 and n2: - melodicIntervalList.append(interval.Interval(n1, n2)) - else: - melodicIntervalList.append(None) - return melodicIntervalList - - melodicIntervals = property(_getMelodicIntervals, doc=''' + @property + def melodicIntervals(self): + ''' Calculates the melodic intervals and returns them as a list, with the interval at 0 being the interval between the first and second note. @@ -2031,7 +2024,16 @@ def _getMelodicIntervals(self): [, , ] - ''') + ''' + tempListOne = self.noteList[:-1] + tempListTwo = self.noteList[1:] + melodicIntervalList = [] + for n1, n2 in zip(tempListOne, tempListTwo, strict=True): + if n1 and n2: + melodicIntervalList.append(interval.Interval(n1, n2)) + else: + melodicIntervalList.append(None) + return melodicIntervalList class ThreeNoteLinearSegmentException(exceptions21.Music21Exception): @@ -2095,22 +2097,37 @@ def __init__(self, noteListOrN1=None, n2=None, n3=None, **keywords): else: super().__init__([noteListOrN1, n2, n3], **keywords) - def _getN1(self): + @property + def n1(self): + ''' + Get or set the first note (left-most) in the segment. + ''' return self.noteList[0] - def _setN1(self, value): + @n1.setter + def n1(self, value): self.noteList[0] = self._correctNoteInput(value) - def _getN2(self): + @property + def n2(self): + ''' + Get or set the middle note in the segment. + ''' return self.noteList[1] - def _setN2(self, value): + @n2.setter + def n2(self, value): self.noteList[1] = self._correctNoteInput(value) - def _getN3(self): + @property + def n3(self): + ''' + Get or set the last note (right-most) in the segment. + ''' return self.noteList[2] - def _setN3(self, value): + @n3.setter + def n3(self, value): self.noteList[2] = self._correctNoteInput(value) def _correctNoteInput(self, value): @@ -2130,53 +2147,44 @@ def _correctNoteInput(self, value): ) from e - n1 = property(_getN1, _setN1, doc=''' - Get or set the first note (left-most) in the segment. - ''') - n2 = property(_getN2, _setN2, doc=''' - Get or set the middle note in the segment. - ''') - n3 = property(_getN3, _setN3, doc=''' - Get or set the last note (right-most) in the segment. - ''') - - def _getILeftToRight(self): - if self.n1 and self.n3: - return interval.Interval(self.n1, self.n3) - else: - return None - - def _getILeft(self): - return self.melodicIntervals[0] - - def _getIRight(self): - return self.melodicIntervals[1] - - iLeftToRight = property(_getILeftToRight, doc=''' + @property + def iLeftToRight(self): + ''' Get the interval between the left-most note and the right-most note (read-only property). >>> tnls = voiceLeading.ThreeNoteLinearSegment('C', 'E', 'G') >>> tnls.iLeftToRight - ''') + ''' + if self.n1 and self.n3: + return interval.Interval(self.n1, self.n3) + else: + return None - iLeft = property(_getILeft, doc=''' + @property + def iLeft(self): + ''' Get the interval between the left-most note and the middle note (read-only property). >>> tnls = voiceLeading.ThreeNoteLinearSegment('A', 'B', 'G') >>> tnls.iLeft - ''') - iRight = property(_getIRight, doc=''' + ''' + return self.melodicIntervals[0] + + @property + def iRight(self): + ''' Get the interval between the middle note and the right-most note (read-only property). >>> tnls = voiceLeading.ThreeNoteLinearSegment('A', 'B', 'G') >>> tnls.iRight - ''') + ''' + return self.melodicIntervals[1] def _reprInternal(self): return f'n1={self.n1} n2={self.n2} n3={self.n3}' From 15c08545d98ead8621c75b589109112bad0d7ec1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:43:50 -1000 Subject: [PATCH 065/312] Don't assert a fully-qualified repr in testNonTraditional The CI runner (testSingleCoreAll) imports every module through ModuleGather under its short name, so the class repr is `` there and `` under pytest. Assert only the description. AI-assisted (Claude) --- music21/key.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/music21/key.py b/music21/key.py index 05a2d69a01..f500060992 100644 --- a/music21/key.py +++ b/music21/key.py @@ -1396,7 +1396,7 @@ def testNonTraditional(self): ks = KeySignature() ks.isNonTraditional = True ks.alteredPitches = [pitch.Pitch('E`')] - self.assertEqual(repr(ks), '') + self.assertIn('of pitches: [E`]', repr(ks)) self.assertEqual(ks.accidentalByStep('E'), pitch.Accidental('half-flat')) # a non-traditional key signature is not equal to the C-major signature From 008dd6bf7b9ca2e5c3db3a8df69ccc940e9b4bd1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:48:13 -1000 Subject: [PATCH 066/312] Test runner: import modules by package name, not as top-level copies ModuleGather.getModule loaded each file with load_source() under its bare name ('key' for music21/key.py), which re-executed the file as a separate top-level module. Every unittest the single-core runner ran therefore tested a shadow copy of its module: `mod.KeySignature is not music21.key.KeySignature`, and `isinstance(ks, music21.key.KeySignature)` was False. It also gave those classes a `__module__` of 'key', so `repr()` came out as `` and any test asserting a full repr failed under CI while passing under pytest. Import by fully-qualified name instead, which returns the module music21 already imported. load_source() is now unused and removed. multiprocessTest was unaffected -- it uses getModuleWithoutImp, which walks the real package tree. Restores the exact repr assertion in key.Test.testNonTraditional. AI-assisted (Claude) --- music21/key.py | 2 +- music21/test/commonTest.py | 28 ++-------------------------- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/music21/key.py b/music21/key.py index f500060992..05a2d69a01 100644 --- a/music21/key.py +++ b/music21/key.py @@ -1396,7 +1396,7 @@ def testNonTraditional(self): ks = KeySignature() ks.isNonTraditional = True ks.alteredPitches = [pitch.Pitch('E`')] - self.assertIn('of pitches: [E`]', repr(ks)) + self.assertEqual(repr(ks), '') self.assertEqual(ks.accidentalByStep('E'), pitch.Accidental('half-flat')) # a non-traditional key signature is not equal to the C-major signature diff --git a/music21/test/commonTest.py b/music21/test/commonTest.py index d473a02ab0..64e06d18ca 100644 --- a/music21/test/commonTest.py +++ b/music21/test/commonTest.py @@ -16,9 +16,7 @@ import copy import doctest import importlib -import importlib.util import os -import sys import typing import types import unittest.runner @@ -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(): ''' @@ -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()]) From 5b42eee8bdef16948e0bc315a3f78a1ff602193b Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:51:16 -1000 Subject: [PATCH 067/312] Expand the running-tests skill to cover the whole-suite runners The skill only described pytest, which is right for one module but is not what CI runs. Adds: the two project runners and how their module sets and orders differ from each other and from pytest, why that makes leaked module-level state show up as "passes alone, fails in the suite," the #_DOCS_HIDE save/restore pattern for a doctest that must mutate module state, the mainTest/-m forms for a single module or test, the Test/TestSlow/TestExternal conventions from documentation/source/developerReference/testing.ipynb, and pylint alongside ruff and mypy. AI-assisted (Claude) --- .agents/skills/running-tests/SKILL.md | 173 ++++++++++++++++++-------- 1 file changed, 122 insertions(+), 51 deletions(-) diff --git a/.agents/skills/running-tests/SKILL.md b/.agents/skills/running-tests/SKILL.md index 685143ea27..0644763556 100644 --- a/.agents/skills/running-tests/SKILL.md +++ b/.agents/skills/running-tests/SKILL.md @@ -1,79 +1,150 @@ --- name: running-tests description: >- - How to correctly run music21's tests and doctests with pytest. Use this - whenever you need to run, verify, or judge the pass/fail of tests or doctests - in this repo — confirming a change works, checking that doctests still pass, - running a single module's tests, or sanity-checking before a PR or push. - Especially consult this before concluding that a doctest "fails": the repo's - pytest plugin normalizes object addresses and injects the doctest namespace, - and running doctests any other way (e.g. raw doctest.testmod) produces FALSE - failures. + How to correctly run music21's tests and doctests -- with pytest for a module, + and with the project's own runners for the whole suite. Use this whenever you + need to run, verify, or judge the pass/fail of tests or doctests in this repo: + confirming a change works, checking that doctests still pass, running a single + module's tests, or sanity-checking before a PR or push. Especially consult it + before concluding that a doctest "fails" (the repo's pytest plugin normalizes + object addresses and injects the doctest namespace, so raw doctest.testmod + produces FALSE failures), and before concluding that a green pytest run means + CI will be green. --- # Running music21 tests and doctests -The reliable way to run tests/doctests for a music21 module is plain pytest with -explicit file paths: +Every function, method and class needs documentation and at least one passing +test. See `documentation/source/developerReference/testing.ipynb` for the +project's own account of testing; this skill covers how to run what is there. + +## One module: pytest with explicit file paths ```bash uv run pytest music21/abcFormat/__init__.py music21/abcFormat/translate.py ``` -`pyproject.toml` `[tool.pytest.ini_options]` already wires up everything that -makes this work: +`pyproject.toml` `[tool.pytest.ini_options]` already wires up everything: ``` addopts = ['--doctest-modules', '-p', 'music21.test.pytest_plugin'] doctest_optionflags = ['NORMALIZE_WHITESPACE', 'ELLIPSIS'] ``` -So you do not pass `--doctest-modules` yourself — doctests in every `.py` are +So do not pass `--doctest-modules` yourself -- doctests in every `.py` are collected automatically, and `music21/test/pytest_plugin.py` is loaded. +Pass explicit file paths so the in-module `Test(unittest.TestCase)` classes run +too. `python_files = ['test_*.py', '*_test.py', 'tests.py']`, so for a module +that keeps its `Test` class inside `__init__.py` / `translate.py` (e.g. +`abcFormat`), pointing pytest at the directory collects ONLY doctests and +silently skips the unittest classes: + +- `uv run pytest music21/abcFormat/` -> ~43 items (doctests only) +- `uv run pytest music21/abcFormat/__init__.py music21/abcFormat/translate.py` + -> ~82 items (doctests **and** `Test` methods) + +Modules whose tests live in a `tests.py` are already picked up by the directory +form; the gotcha is specifically a `Test` class in a non-`tests.py` file. + +A module also runs its own tests directly, via the `mainTest` call at the foot +of each file: + +```bash +uv run python -m music21.note # whole module +uv run python music21/note.py testHello # one test method +``` + ## Why the plugin matters (and why not to use raw doctest) -The plugin does two things that make doctests pass the way the project intends: +`music21/test/pytest_plugin.py` does three things that make doctests behave as +the project intends: - It runs `stripAddresses(example.want, '0x...')` (from - `music21.test.testRunner`) on every doctest, so docstrings that hardcode a - repr like `` are - normalized and DO pass. The literal hex address in the docstring is expected - and fine. + `music21.test.testRunner`) on every doctest, so a docstring that hardcodes + `` is normalized and DOES + pass. The literal hex address in the docstring is expected and fine. - It injects the `music21.__all__` names plus `music21` itself into the doctest - namespace, so examples can reference `meter`, `key`, `corpus`, etc. without - importing them. - -This is why you must judge doctest pass/fail **through pytest**. Do NOT use -`doctest.testmod(...)` or `doctest.DocTestSuite(...)` directly to decide whether -doctests pass: that path applies neither the address normalizer nor the -namespace injection, so it manufactures false failures on any docstring that -prints a `<... object at 0x...>` repr or relies on the injected names. If you -ever see only an object-address example "failing," that is the tell that you -ran doctests the wrong way — rerun through pytest. - -## Pass explicit file paths, not a bare directory - -Use explicit file paths so the in-module `Test(unittest.TestCase)` classes run -too, not just doctests. `python_files = ['test_*.py', '*_test.py', 'tests.py']`, -so for a module that keeps its `Test` class inside `__init__.py` / -`translate.py` (e.g. `abcFormat`), pointing pytest at the directory -(`uv run pytest music21/abcFormat/`) collects ONLY its doctests and silently -skips the unittest classes. - -Concretely, for `abcFormat`: -- `uv run pytest music21/abcFormat/` → ~43 items (doctests only) -- `uv run pytest music21/abcFormat/__init__.py music21/abcFormat/translate.py` - → ~82 items (doctests **and** `Test` methods) + namespace, so examples reference `meter`, `key`, `corpus` without importing. +- It keeps only `Test` classes, dropping `TestSlow` and `TestExternal`. + +Judge doctest pass/fail **through pytest**. Do NOT use `doctest.testmod(...)` or +`doctest.DocTestSuite(...)` directly: that path applies neither the address +normalizer nor the namespace injection, so it manufactures false failures on any +docstring printing a `<... object at 0x...>` repr or relying on injected names. +An object-address example failing on its own is the tell that doctests were run +the wrong way -- rerun through pytest. + +## Whole suite: the project's runners, not pytest + +pytest is for a module. For the whole suite use the runners, and note that a +green pytest run does not mean CI will be green -- they gather and order modules +differently. -Both apply the address normalizer; only the explicit-paths form also runs the -unittest classes. When a module's tests live in a `tests.py` file (much of -music21's house style), the directory form already picks those up — the gotcha -is specifically modules that keep `Test` inside a non-`tests.py` file. +```bash +# everyday full run, on n-1 cores +uv run python music21/test/multiprocessTest.py + +# exactly what GitHub Actions runs (~1 minute); use before pushing to a PR +uv run python -c 'from music21.test.testSingleCoreAll import ciMain as ci; ci()' +``` + +The two runners see slightly different sets of modules -- `multiprocessTest` +walks the package tree for modules reachable from `import music21`, while +`testSingleCoreAll` gathers module files from disk. Run both before a release. + +Module **order** differs between the two runners and again from pytest. +`multiprocessTest` goes in reverse-alphabetical order (with the known-slow +modules hoisted to the front on machines with more than 4 cores); +`testSingleCoreAll` re-sorts through `common.misc.sortModules`, by file mtime, +most recently modified first, falling back to reverse-alphabetical when mtimes +tie as they do on a fresh clone; pytest walks alphabetically. So a test that +depends on module order can pass in one runner and fail in another -- and +editing a file locally sorts it to the front of the CI runner, changing the +order again. + +That makes leaked module-level state the usual cause of "passes alone, fails in +the suite." A doctest in `duration.py` once set +`humdrum.spineParser.flavors['JRP'] = True` and never restored it; `flavors` is +a module-level dict, so every later humdrum test in the same process parsed in +the wrong flavor. Under pytest's alphabetical order `duration` runs before +`humdrum` and the failure appeared; under CI's order it did not. + +**So: a doctest that mutates module-level state must restore it**, using +`#_DOCS_HIDE` so the bookkeeping stays out of the published docs: + +``` +>>> saved_JRP_flavor = humdrum.spineParser.flavors['JRP'] #_DOCS_HIDE +>>> humdrum.spineParser.flavors['JRP'] = True +... +>>> humdrum.spineParser.flavors['JRP'] = saved_JRP_flavor #_DOCS_HIDE +``` -## Whole suite and the other gates +When a test fails only in a full run, re-run it alone. If it then passes, +suspect state left behind by an earlier module rather than the test itself. + +## What tests may do + +- Exactly one `Test(unittest.TestCase)` class per module, methods named + `test...`. It must produce no output, open no windows, play nothing, and not + require packages outside the music21 ecosystem. +- Anything that produces external output goes in `TestExternal`; anything slow + goes in `TestSlow`. Both are excluded from the normal run. +- Tests should be fast: a major new module may add a few seconds, a small + addition should add milliseconds. +- Regression cases for a bug fix belong in `Test`, never in a doctest. See the + `writing-docs` skill. + +## The other gates before a PR or a push + +```bash +uv run ruff check music21 +uv run mypy music21 +uv run pylint -j4 music21 --rcfile=.pylintrc # catches what the first two miss +``` -- Full suite: `python music21/test/multiprocessTest.py` (or - `music21/test/testSingleCoreAll.py` on a single-core machine). -- Before a PR or a push to an open PR, also run the lint and type gates: - `uv run ruff check music21` and `uv run mypy music21`. +Coverage is expected to rise with each contribution +(https://coveralls.io/github/cuthbertLab/music21). CI measures it on a single +pinned Python -- the **middle** supported version, see +`coverageM21.getCoverage`. `# pragma: no cover` exists for genuinely +untriggerable code and is otherwise discouraged. From 934cc1a35d06cff6611cce6687d6ba4c237e94bd Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:55:12 -1000 Subject: [PATCH 068/312] Revert the test-runner change out of this PR; import key inside the test The runner fix does not belong in a KeySignature PR. Reverts commonTest.py to master and instead follows the convention used elsewhere: import the module inside the test method, so the test uses music21.key.KeySignature and its repr is fully qualified under both runners. AI-assisted (Claude) --- music21/key.py | 8 +++++--- music21/test/commonTest.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/music21/key.py b/music21/key.py index 05a2d69a01..b23054f513 100644 --- a/music21/key.py +++ b/music21/key.py @@ -1390,10 +1390,12 @@ def testNonTraditional(self): ''' AI-assisted (Claude). ''' - ks = KeySignature(3) + from music21 import key + + ks = key.KeySignature(3) self.assertFalse(ks.isNonTraditional) - ks = KeySignature() + ks = key.KeySignature() ks.isNonTraditional = True ks.alteredPitches = [pitch.Pitch('E`')] self.assertEqual(repr(ks), '') @@ -1401,7 +1403,7 @@ def testNonTraditional(self): # a non-traditional key signature is not equal to the C-major signature # it shares a `sharps` count with. - self.assertNotEqual(ks, KeySignature()) + self.assertNotEqual(ks, key.KeySignature()) def testSharpsNoneDeprecated(self): ''' diff --git a/music21/test/commonTest.py b/music21/test/commonTest.py index 64e06d18ca..d473a02ab0 100644 --- a/music21/test/commonTest.py +++ b/music21/test/commonTest.py @@ -16,7 +16,9 @@ import copy import doctest import importlib +import importlib.util import os +import sys import typing import types import unittest.runner @@ -59,6 +61,28 @@ 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(): ''' @@ -393,11 +417,11 @@ def getModule(self, fp, restoreEnvironmentDefaults=False): if skip: return None - name = self._getNamePeriod(fp, addM21=True) + name = self._getNamePeriod(fp, addM21=False) try: with warnings.catch_warnings(): - mod = importlib.import_module(name) + mod = load_source(name, fp) except Exception as excp: # pylint: disable=broad-exception-caught environLocal.warn(['failed import:', name, '\t', fp, '\n', '\tEXCEPTION:', str(excp).strip()]) From bdf5a89aa3c32ca2c21c0311e67a6b39e4738338 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 17:52:33 -1000 Subject: [PATCH 069/312] Test runner: import modules by package name, not as top-level copies ModuleGather.getModule loaded each file with load_source() under its bare name ('key' for music21/key.py), which re-executed the file as a separate top-level module. Every unittest the single-core runner ran therefore tested a shadow copy of its module: `mod.KeySignature is not music21.key.KeySignature`, and `isinstance(ks, music21.key.KeySignature)` was False. It also gave those classes a `__module__` of 'key', so a test asserting a full repr saw `` under CI and `` under pytest -- which is why tests import names from `music21.x` rather than relying on their own module globals. Import by fully-qualified name instead, which returns the module music21 already imported. load_source() is now unused and removed. multiprocessTest was unaffected -- it uses getModuleWithoutImp, which walks the real package tree. DO NOT MERGE AS IS -- reported coverage drops from 93.33% to 77.20%. testSingleCoreAll calls coverageM21.getCoverage() at module import time, after its own `from music21 import ...` lines have already run, so music21's module-level code (class bodies, defs, constants) executes before cov.start(). Re-executing every file through load_source() is what currently gets those lines counted. A real fix has to start coverage before music21 is imported (measured on the branch: 93.33% at 15c08545d, 77.20% with this commit). Then drop the `from music21. import ...` lines inside Test methods that exist only to work around the shadow modules. AI-assisted (Claude) --- music21/test/commonTest.py | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/music21/test/commonTest.py b/music21/test/commonTest.py index d473a02ab0..64e06d18ca 100644 --- a/music21/test/commonTest.py +++ b/music21/test/commonTest.py @@ -16,9 +16,7 @@ import copy import doctest import importlib -import importlib.util import os -import sys import typing import types import unittest.runner @@ -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(): ''' @@ -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()]) From 728ec247fc891870aa6dd9986c617c0fc754a5e4 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 18:30:14 -1000 Subject: [PATCH 070/312] Shorten the running-tests skill description It loads into every session; 90 words down to 34. AI-assisted (Claude) --- .agents/skills/running-tests/SKILL.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.agents/skills/running-tests/SKILL.md b/.agents/skills/running-tests/SKILL.md index 0644763556..a93741c4f5 100644 --- a/.agents/skills/running-tests/SKILL.md +++ b/.agents/skills/running-tests/SKILL.md @@ -1,15 +1,9 @@ --- name: running-tests description: >- - How to correctly run music21's tests and doctests -- with pytest for a module, - and with the project's own runners for the whole suite. Use this whenever you - need to run, verify, or judge the pass/fail of tests or doctests in this repo: - confirming a change works, checking that doctests still pass, running a single - module's tests, or sanity-checking before a PR or push. Especially consult it - before concluding that a doctest "fails" (the repo's pytest plugin normalizes - object addresses and injects the doctest namespace, so raw doctest.testmod - produces FALSE failures), and before concluding that a green pytest run means - CI will be green. + How to run music21's tests and doctests and judge pass/fail: pytest for one + module, the project's own runners for the whole suite. Read it before running + tests, and before calling any doctest failure real. --- # Running music21 tests and doctests From d53792f41276c833f4ec12dc9e578d98673d7dea Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 27 Aug 2026 18:32:44 -1000 Subject: [PATCH 071/312] Add a prose section to the writing-docs skill Length was covered; voice was not. Adds rules for examples drawn from real music, leading with the verb, varying sentence length, cutting throat-clearing, and treating the house's dry humor as style rather than slippage. Shortens the skill description from 55 words to 34 -- it loads every session. AI-assisted (Claude) --- .agents/skills/writing-docs/SKILL.md | 31 +++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 345b2b150c..bf6776ad56 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -1,11 +1,9 @@ --- name: writing-docs description: >- - House style for docstrings, code comments, and `Changed in`/`New in` version - markers in music21. Use whenever you write or edit a docstring or comment, add - a version marker, or decide where a bug fix's test belongs. Covers the length - target, the rule against narrating bugs you just fixed, and why regression - cases go in unittests rather than doctests. + House style for music21 prose: docstrings, comments, and `Changed in`/`New in` + markers. Read before writing any of them. Sets the length target, the voice, + and where a bug fix's test belongs. --- # Writing docs and comments @@ -16,6 +14,29 @@ Aim for about 40% of the length an LLM writes by default. Cut qualifiers, restatements, and the sentence that explains the sentence before it. A comment earning its place says something the code cannot. +## Prose + +Assume the reader is skimming and looking for an excuse to stop. Give them +something to catch on. + +**Examples are music.** `key.KeySignature(-3)` is E-flat major, so say E-flat +major. Reach into the corpus rather than inventing `foo`: `bwv66.6` and +`luca/gloria` are in hundreds of docstrings because a reader can hear them. + +**Lead with the thing itself.** "Returns the pitches sounding at an offset," not +"This method can be used to obtain the pitches that are sounding." + +**Vary sentence length.** Three medium declaratives in a row is where the eye +slides off the page. A short one lands. + +**Cut throat-clearing.** "It is important to note that," "Note that in general," +"This allows the user to." Start at the verb. + +**Personality is house style, not a lapse.** `Cannot determine sharps for +quarter-tone keys! silly!` and "it'd wig me out if I ever didn't see them in +reverse alphabetical order" are both shipping music21. Dry, specific, short. A +joke needing a second sentence to land is one sentence too long. + ## Say what is, not what was or what not to do Describe current behavior. Do not narrate the bug you just fixed, the old From 98490dbc60ec264214db4e8f51d6fe8114645ec6 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 09:34:08 -1000 Subject: [PATCH 072/312] change lack of Ending number to warning --- music21/musicxml/xmlToM21.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 9cc87946e3..7433a493df 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -6,7 +6,7 @@ # Christopher Ariza # Jacob Tyler Walls # -# Copyright: Copyright © 2009-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -1484,8 +1484,8 @@ def __init__(self, self.staffReferenceList: list[StaffReferenceType] = [] self.lastTimeSignature: meter.TimeSignature|None = None - self.lastMeasureWasShort = False - self.lastMeasureOffset = 0.0 + self.lastMeasureWasShort: bool = False + self.lastMeasureOffset: OffsetQL = 0.0 # a dict of clefs per staff number -- needed for converting rests w/ steps self.lastClefs: dict[int, clef.Clef|None] = {} @@ -2282,7 +2282,7 @@ def adjustTimeAttributesFromMeasure(self, m: stream.Measure): mOffsetShift = mHighestTime else: mOffsetShift = mHighestTime # lastTimeSignatureQuarterLength - if self.lastMeasureWasShort is True: + if self.lastMeasureWasShort: if m.barDurationProportion() < 1.0: m.padAsAnacrusis() # probably a pickup after a repeat or phrase boundary # or something @@ -5161,10 +5161,16 @@ def xmlBarline(self, mxBarline: ET.Element) -> None: rb.addSpannedElements(m) if mxEndingObj.get('type') == 'start': - mxNumber = mxEndingObj.get('number') + mxNumberStr = mxEndingObj.get('number') + if mxNumberStr is None: + warnings.warn(f'number is required on object, {mxEndingObj}', + MusicXMLWarning, + stacklevel=2) + mxNumberStr = '1' + # RepeatBracket handles comma-separated values, such as "1,2" try: - rb.number = mxNumber if mxNumber is not None else 1 + rb.number = mxNumberStr except spanner.SpannerException: rb.number = 1 From 36d2298484f5e1240e2d487716a6feb54a25cd43 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 09:51:23 -1000 Subject: [PATCH 073/312] Just some small misc edits/improvements to docs --- .agents/skills/writing-docs/SKILL.md | 13 +++++++++ AGENTS.md | 43 ++++++++++++++++++++++------ music21/pitch.py | 14 ++++++--- music21/stream/core.py | 4 +-- 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 345b2b150c..2bf010307b 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -84,3 +84,16 @@ unittest. Naming the guarded bug **is** appropriate in a unittest; that is what the test is for. The rule against narrating old bugs applies to docstrings and to comments in shipping code, not to tests. + +## Writing and Comment style +- When writing comments in code, assume a strong code reader — anything inferable from the code is noise (docs that paraphrase names of functions or variable names esp.); focus on high level issues and gotchas that might bite again if not documented. +- Say how to use code, not prior bugs or how code used to work or what was removed. That's for commit messages. Don't document where code is called from except for "keep in sync" lines across Py/TS. +- Don't hijack a docstring for your addition. Original purpose line stays primary + one short line for the new bit. Prefer not documenting a small feature over making it seem like the primary reason for the code. +- Examples of usage are usually better than long descriptions. +- Avoid jargon not already found in the codebase; use plain descriptive English. +- Rare paths should get little weight: in both code and docs. Use try/except over if/else when the except clause is rare. In docs, state the 90% path first and point exceptional cases to code that handle it. +- When wording is dictated to the agent to substitute for original wording, use it. Do not add parentheticals. Only fix obvious typos. +- No weapon-metaphors or overly militaristic language. Avoid "blast radius", "rearm", "landmine", + "detonate" in issues/PR/code. Trigger or fire events is so commonly used that they're okay. + + diff --git a/AGENTS.md b/AGENTS.md index 3ba5ecca1e..a23c16018a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,12 @@ `f"the value is {d["key"]}"`. This is pinned in `pyproject.toml` via `[tool.ruff.format] nested-string-quote-style = "alternating"`. +# Established contributors + +- "established contributors" are people with at least 3 PRs merged or a history of contributing to issues and the list that goes back at least 1 year. There are exceptions to general rules for them below. +- "core dev" means someone officially part of the project team or with 20+ PRs merged. Michael Cuthbert, Jacob Walls, Joseph VanderStel are non-exhaustive examples of people in that group who are still often contributing in 2026-- they and their agents can make exceptions to these rules + + # Testing - pytest works but to run the whole suite run music21/test/multiprocessTest.py (or testSingleCoreAll.py @@ -27,12 +33,12 @@ - Run `uv run ruff check music21` before making PRs or pushes to open PRs. - Run `uv run mypy music21` before making PRs or pushes to open PRs. - **Regression cases go in the module's `Test(unittest.TestCase)` class — never in a - docstring.** This is absolute, and it covers the tempting one-liner showing that some + docstring.** Docstrings should not include one-liners showing that some bad input now raises. A doctest sits in the most-read documentation the project has, so an example built from input no one would ever write teaches nothing and puts an obscure bug on a billboard. The test: would a first-time reader of this object want this example? If no, it is a unittest. See the `writing-docs` skill. -- Never commit `forceSource=True` to a test or doctest (it re-parses from source every +- Never commit `forceSource=True` to a test or doctest (it reparses from source every run and slows the suite for everyone). The ONLY exception is the one test that exercises `forceSource` itself. If you hit a stale-parse problem while developing: - If it is local-only (e.g. you just changed a parser and a cached pickle is stale), @@ -62,19 +68,40 @@ branch such as `m21_9`. When a CI check fails but passes locally, "is my branch behind `master`?" should be one of the first things to check: fetch and merge `master`, then the newer types/code on `master` will reproduce the failure locally. -- All PRs and Issues need to be declared AI-assisted. -- 10 or more lines of code written by an agent needs to be declared as AI-assisted in the docstring. +- All PRs and Issues that use AI to be declared AI-assisted. Just write "AI-assisted (Claude)" with short name of Agent replacing "Claude". No robot emoji under any circumstance. +- 20 or more lines of code written by an agent needs to be declared as AI-assisted in the docstring. Humans can remove and should remove this note when they do a review. -- If no code was written by a user, any PR must declare "entirely AI written" unless the user - is a longtime contributor to music21. - If it does not pass the tests it will be closed (or should be closed - by the agent or author). Failure to do so may result in the user being banned from the project. +- If no code was written by a user and no language was provided for the issue and no reference + to specific code to change was given, any PR must declare "(Entirely AI written)" unless the user + is by a core dev. Failure to do so may result in new users + being banned from the project. +- If an entirely AI written issue does not pass the tests it will be closed (or should be closed + by the agent or author). - Agents must follow the [Code of Conduct](CODE_OF_CONDUCT.md). Agents that do not will be banned as well at their users. Not even the slightest bit of disrespect from an AI agent will be tolerated. - Mark changes in public interface with `* Changed in v[X]: One-line explanation.` Or new features with "New" instead of "Changed". - Changes to parsing formats (esp. musicxml) need to update the patch version of the version file. - Music21 uses even minor version numbers for alpha/beta and odd minor numbers for releases. - If the current version is MAJOR.0.... then mark `Changed in vMAJOR:` if it is `MAJOR.[even]` use the next odd number, like if it's 10.2 now use "Changed in 10.3". If current version is odd that's likely a mistake or you caught it just before a new release. Use the following odd number instead. +- Any PR not from an established contributor touching more than about 20-30 lines should have an issue that has been opened and had enough + time for people to discuss/review it before moving forward. Don't open the PR unless you've seen + thumbs up or "sounds good" etc. from an established contributor already + - PRs that fix typos, clear bugs in one or two places etc. are exempt. +- Issues must state clearly at the top in 50 words or fewer what the problem is, or what the gain is, etc. it should + not be filled with jargon. More details can go below. +- If the language of the issue was not prompted by the user ("say something like Adds color support to Lilypond output of lyrics") then the summary should end with "(Entirely AI written)". +- PRs should reference the existing issue by number and summarize that issue in 30 words or fewer. If the + approach used to solve the issue is substantially different from the main approach discussed in the issue + this should be addressed. +- If a PR or issue was closed by a core dev (and not reopened by them), agents must refuse + to reopen the PR or issue or to create another issue/PR for the same topic. Leave it to the humans to reopen + after addressing the problem. (A blind close or close with "not accepted" etc. generally means that the issue/PR + has too many problems to easily solve and has become a burden for the maintainer). +- Do not include a "Tests run" section unless the testing procedure was unusual (like it affects part of the system without standard tests, like the testing system itself.) + +# Writing style + +- See the writing-docs skill, which also includes required language for PRs and Issues. # Worktrees diff --git a/music21/pitch.py b/music21/pitch.py index 8a66edb211..217fd3fb8a 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -88,6 +88,8 @@ MICROTONE_OPEN = '(' MICROTONE_CLOSE = ')' + +# do not change this -- consider it fixed. accidentalNameToModifier = { 'natural': '', 'sharp': '#', @@ -98,9 +100,13 @@ 'double-flat': '--', 'triple-flat': '---', 'quadruple-flat': '----', - 'half-sharp': '~', + 'half-sharp': '~', # Might remove soon; use 'half-sharp' + # Might remove soon; use 'one-and-a-half-sharp' + # or 'three-quarter-sharp' 'one-and-a-half-sharp': '#~', - 'half-flat': '`', + 'half-flat': '`', # might remove soon; use 'half-flat' + # Might remove soon; use 'one-and-a-half-flat' + # or 'three-quarter-flat' 'one-and-a-half-flat': '-`', } @@ -1072,7 +1078,6 @@ def __ge__(self, other: object) -> bool: >>> b = pitch.Accidental('flat') >>> a >= b True - ''' return self.__gt__(other) or self.__eq__(other) @@ -1227,7 +1232,8 @@ def set(self, name: str|int|float, *, allowNonStandardValue: bool = False) -> No if name in ('natural', 'n', 0): self._name = 'natural' self._alter = 0.0 - elif name in ('sharp', accidentalNameToModifier['sharp'], 'is', 1): + elif name in ('sharp', '#', 'is', 1): + # accidentalNameToModifier['sharp'] will always be #! self._name = 'sharp' self._alter = 1.0 elif name in ('double-sharp', accidentalNameToModifier['double-sharp'], diff --git a/music21/stream/core.py b/music21/stream/core.py index e0fe04e3ec..fac17154a3 100644 --- a/music21/stream/core.py +++ b/music21/stream/core.py @@ -104,9 +104,9 @@ def coreInsert( if not ignoreSort: # # if sorted and our insertion is > the highest time, then # # are still inserted - # if self.isSorted is True and self.highestTime <= offset: + # if self.isSorted and self.highestTime <= offset: # storeSorted = True - if self.isSorted is True: + if self.isSorted: ht = self.highestTime # type: ignore if ht < offset: storeSorted = True From 766c1a9c98131b905d134760b7fd02261d37ac58 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 10:24:21 -1000 Subject: [PATCH 074/312] More agent skills --- .agents/skills/bump-version/SKILL.md | 41 ++++++++++++++++--------- .agents/skills/running-tests/SKILL.md | 23 ++++++++++++++ .agents/skills/writing-docs/SKILL.md | 44 +++++++++++++++++++++++++++ AGENTS.md | 28 +++++++++++++++++ music21/scale/intervalNetwork.py | 8 ++++- music21/scale/test_scale_main.py | 6 ++-- 6 files changed, 132 insertions(+), 18 deletions(-) diff --git a/.agents/skills/bump-version/SKILL.md b/.agents/skills/bump-version/SKILL.md index 31c0320d0b..40a08b891d 100644 --- a/.agents/skills/bump-version/SKILL.md +++ b/.agents/skills/bump-version/SKILL.md @@ -35,27 +35,37 @@ uv run pytest --doctest-modules music21/base.py music21/_version.py ## When to bump -- **Parsing-format / parser changes (most common reason).** Any change to how a - format is read or written — musicxml, abc, MIDI, noteworthy/NWC, humdrum, - etc. — should bump the version, even a refactor, because the version is baked - into the **pickled-stream cache** (cached parsed files). Bumping invalidates - stale pickles so users don't get results from the old parser. This is the rule - in AGENTS.md ("Changes to parsing formats … need to update the patch version"). -- **New feature** → bump (see digit rules below); mark it in docstrings with - `* New in vX: …`. -- **Bug fix** → bump the patch; if it changes public behavior, mark - `* Changed in vX: …`. -- **Pure internal refactor with no behavioral/pickle impact** → usually no bump. +One thing depends on the number changing: the **pickled-stream cache**. The +version is part of the cache key, so a bump invalidates stale pickles +everywhere. If nobody is holding a wrong cached parse, don't bump — the commit +is the record. + +- **Parser or parsing-format change → always bump**, even a pure refactor: + someone is holding a pickle parsed by the old code. musicxml, abc, MIDI, + noteworthy/NWC, humdrum, and the rest. +- **Anything else → no bump**: a new feature, a fix that makes code do what it + already claimed, an internal refactor, a test-only or docs-only change. + +"It's a bug fix" is not by itself a reason. A musicxml importer that now reads a +tag it used to drop bumps, because cached parses are wrong. A scale analysis +method that changed its return format does not. + +A `New in` / `Changed in` marker is **not** a reason to bump. Those markers name +a major version, at most a minor one — never a patch, never a `bN` beta — and +moving those digits is a release decision only a human makes. Write the marker +for the version the change will land in and leave `_version.py` alone. ## Which part to change music21 follows semver-ish rules (see the `_version.py` docstring for the full rationale): -- **MAJOR (X)** — breaks old features. Rare. +- **MAJOR (X)** — breaks old features. Rare. A human decides this. - **MINOR (Y)** — new features. **Even Y = alpha/beta, odd Y = release.** `X.0` (e.g. `11.0`) are development releases that can still change until `X.1`. -- **PATCH (Z)** — bug fixes and parsing/pickle-invalidating changes. + A human decides this too; an agent changes only the patch or the beta suffix. +- **PATCH (Z)** — parsing/pickle-invalidating changes and other fixes that + meet the bar above. - **beta suffix (`bN`)** — successive pre-release builds of the same `MAJOR.MINOR.PATCH`. To cut another beta without otherwise changing the number, increment it: `11.0.0b1` → `11.0.0b2`. (This is what a @@ -63,8 +73,9 @@ rationale): ## `Changed in` / `New in` docstring markers -When a bump accompanies a public-interface change, annotate the affected -method/class docstring: +Annotate a changed public interface in the affected method/class docstring. +This is independent of bumping — a marker is documentation, not a version +change: - `* Changed in vX: one-line explanation.` - `* New in vX: one-line explanation.` diff --git a/.agents/skills/running-tests/SKILL.md b/.agents/skills/running-tests/SKILL.md index 685143ea27..136555437c 100644 --- a/.agents/skills/running-tests/SKILL.md +++ b/.agents/skills/running-tests/SKILL.md @@ -71,6 +71,29 @@ unittest classes. When a module's tests live in a `tests.py` file (much of music21's house style), the directory form already picks those up — the gotcha is specifically modules that keep `Test` inside a non-`tests.py` file. +## Speed budget + +Aim for about 3 seconds for a module's tests, and never add more than 15 — that is time +every contributor waits on every full run. `corpus.parse()` is nearly always the culprit: +build a small stream by hand instead, or parse `bwv66.6`, which is short and still full +of interesting cases (pickup measures, and so on). + +## Tests that open windows + +Nothing in `Test` or in a doctest may open a window, play audio, or launch another +program. Those go in a sibling class, run only when named explicitly: + +```python +class TestExternal(unittest.TestCase): + ... + +if __name__ == '__main__': + import music21 + music21.mainTest(Test, TestExternal) +``` + +Because they have those side effects, skip `TestExternal` when running a file directly. + ## Whole suite and the other gates - Full suite: `python music21/test/multiprocessTest.py` (or diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 2bf010307b..3bd5cda8d1 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -57,6 +57,50 @@ doctest. It goes in the commit message. See the `bump-version` skill for which digit to change and the odd/even convention. +## Doctest mechanics + +In doctests, no need to run `from music21 import *` that happens automatically (In Jupyter notebooks for the user's guide, the first line should begin `>>> from music21 import *`, so readers remember they need it) + +Examples always qualify by module: `note.Note('C4')`, never a bare `Note`. + +`OMIT_FROM_DOCS`, alone on a line, hides everything after it: cases worth checking that +no reader wants to meet. + +`#_DOCS_HIDE` at the end of a line runs it without showing it; `#_DOCS_SHOW` at the +start of a line shows it without running it. Together they let a doctest look +nondeterministic and still have a fixed answer: + +``` +>>> import random +>>> randomNumber = 12 #_DOCS_HIDE +>>> #_DOCS_SHOW randomNumber = random.randint(0, 127) +>>> p = pitch.Pitch() +>>> p.ps = randomNumber +>>> p + +``` + +Link with ``:class:`~music21.note.Note` `` and ``:meth:`~music21.note.Note.addLyric` ``. + +## Examples + +Give steps that have meaningful intermediate output descriptive names instead of chaining — so readers can understand what the intermediate values are: + +``` +>>> bachScore = corpus.parse('bwv66.6') +>>> excerpt = bachScore.measures(4, 6) +>>> chordReduction = excerpt.chordify() +``` + +Pick examples musicians care about: semitones to frequency, not Celsius to Fahrenheit; +scramble "Chaminade", not "puppy". If you cannot think of a reason a musician would call the method, it may not belong in music21. + +Describe what a parameter is and does in English if it is not obvious; type alone is not +documentation. + +No dull repetition in docs. A bit of humor is welcome in docs; the docs are written +for humans who will close the window if they are dull. If seven methods do essentially the same thing, give extensive docs the first time and then later methods can refer back to the first method. Don't repeat the same docs over and over. + ## Doctests are not regression tests Doctests are documentation that happens to be verified. Every example must earn diff --git a/AGENTS.md b/AGENTS.md index a23c16018a..37adebbe3e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,26 @@ quote for nested strings, e.g. `f'the value is {d["key"]}'`, not `f"the value is {d["key"]}"`. This is pinned in `pyproject.toml` via `[tool.ruff.format] nested-string-quote-style = "alternating"`. +- Never `eval` or `global`, or a trick that does the same thing. +- Imports: standard library first, then music21 modules, one per line, alphabetical. +- New modules open with the `# Name: / # Purpose: / # Authors: / # Copyright: / # License:` + banner (copy a neighboring module's), then the module docstring, then imports. Update the Copyright date end to current year when changing the module. +- No `print()`. Use `environLocal = environment.Environment('moduleName')` and + `environLocal.printDebug(...)`, or `environLocal.warn(...)` when the user should hear + about it every time. `test/toggleDebug.py` switches debug output on and off. +- New exceptions subclass `exceptions21.Music21Exception`. +- Return named things — a small class, a `namedtuple`, a dict — never a positional tuple of more than 2 (maybe 3) elements + whose elements each mean something different and unrelated (x, y, z is okay for instance). Nobody should write `returned[3][0][7]`. +- Don't reuse a variable name once the type of what it holds changes + (`onsetCount` (int) then `extractedOnsets` (list[int]), not `onsets` twice). +- Don't add a property that only gets or sets a private attribute; expose the attribute. +- Making a method `_private` is not a substitute for documenting and testing it +- A private method should not normally be mentioned in public documentation. +- A method past ~30 lines, or deeply nested, should probably be several methods. +- `i`, `j`, `junk`, `counter` are exempt from unused-variable warnings; otherwise prefix with `unused_`. +- Don't restyle code you aren't otherwise changing. +- Text handling must survive non-ASCII input (ß, é, 中国). Comments should mostly remain ASCII; docs may use UTF-8 freely. +- Never commit anything under copyright without a free (not GNU) license — code, scores, or someone else's encodings. # Established contributors @@ -30,6 +50,10 @@ - pytest works but to run the whole suite run music21/test/multiprocessTest.py (or testSingleCoreAll.py if on a single core machine.) +- Every function, method, and class needs documentation and at least one passing test. +- Keep tests fast: aim for about 3 seconds per module, never more than 15. See the + `running-tests` skill for the speed budget and for `TestExternal`. +- When making major changes, check the User's Guide to to make sure that docs still work there too. - Run `uv run ruff check music21` before making PRs or pushes to open PRs. - Run `uv run mypy music21` before making PRs or pushes to open PRs. - **Regression cases go in the module's `Test(unittest.TestCase)` class — never in a @@ -81,6 +105,10 @@ Not even the slightest bit of disrespect from an AI agent will be tolerated. - Mark changes in public interface with `* Changed in v[X]: One-line explanation.` Or new features with "New" instead of "Changed". - Changes to parsing formats (esp. musicxml) need to update the patch version of the version file. +- That is the only reason an agent bumps: a parser change leaves stale pickles in + everyone's cache. A plain bug fix or a new feature elsewhere does not bump — the commit + is the record, and a `New in`/`Changed in` marker is documentation, not a bump. Major + and minor version changes are a human's call. See the `bump-version` skill. - Music21 uses even minor version numbers for alpha/beta and odd minor numbers for releases. - If the current version is MAJOR.0.... then mark `Changed in vMAJOR:` if it is `MAJOR.[even]` use the next odd number, like if it's 10.2 now use "Changed in 10.3". If current version is odd that's likely a mistake or you caught it just before a new release. Use the following odd number instead. - Any PR not from an established contributor touching more than about 20-30 lines should have an issue that has been opened and had enough diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index af6bf35c52..6d25c6fad0 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -2688,7 +2688,13 @@ def getPitchFromNodeDegree( # TODO: BUG: Does not work with bidirectional scales. # TODO: possibly cache results - for unused_counter in range(10): + + # A non-deterministic realization can skip the target node entirely, so try + # again; a deterministic one realizes the same way every time, so once is enough. + # 40 tries on Weighed Hexatonic or other scales where each node exists + # = 1 fail in 1.1 trillion tries, but still only about 25ms in case the node actually + # does not exist in the scale. + for unused_counter in range(1 if self.deterministic else 40): realizedPitch, realizedNode = self.realize( pitchReference=pitchReference, nodeId=nodeListForNames[0], diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index b0b4cf5735..587056715c 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -585,8 +585,10 @@ def testWeightedHexatonicBluesA(self): self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') self.assertEqual(str(sc.nextPitch('c4', Direction.ASCENDING)), 'E-4') - # degree 4 is always the blues note in this model - self.assertEqual(str(sc.pitchFromDegree(4)), 'F#4') + # degree 4 is always the blue note in this model, even though only half of + # the realizations pass through it + for dummy in range(20): + self.assertEqual(str(sc.pitchFromDegree(4)), 'F#4') # This never worked consistently and was not an important enough part of the project tp # continue to debug. From 7e7a2a640bd82cfd2e31b3c67306144bc4215a19 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 10:41:51 -1000 Subject: [PATCH 075/312] Merge master into writing-docs-prose; drop rules master now covers Master's new "Examples" section already says pick examples musicians care about and that dull docs get closed, so this branch's versions of those go. What survives is the four rules master does not state: lead with the verb, vary sentence length, cut throat-clearing, and keep a joke to one sentence. AI-assisted (Claude) --- .agents/skills/writing-docs/SKILL.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 33b6afe57a..41d4e664af 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -16,13 +16,6 @@ earning its place says something the code cannot. ## Prose -Assume the reader is skimming and looking for an excuse to stop. Give them -something to catch on. - -**Examples are music.** `key.KeySignature(-3)` is E-flat major, so say E-flat -major. Reach into the corpus rather than inventing `foo`: `bwv66.6` and -`luca/gloria` are in hundreds of docstrings because a reader can hear them. - **Lead with the thing itself.** "Returns the pitches sounding at an offset," not "This method can be used to obtain the pitches that are sounding." @@ -32,10 +25,10 @@ slides off the page. A short one lands. **Cut throat-clearing.** "It is important to note that," "Note that in general," "This allows the user to." Start at the verb. -**Personality is house style, not a lapse.** `Cannot determine sharps for -quarter-tone keys! silly!` and "it'd wig me out if I ever didn't see them in -reverse alphabetical order" are both shipping music21. Dry, specific, short. A -joke needing a second sentence to land is one sentence too long. +**Humor is house style, not a lapse.** `Cannot determine sharps for quarter-tone +keys! silly!` and "it'd wig me out if I ever didn't see them in reverse +alphabetical order" are both shipping music21. A joke needing a second sentence +to land is one sentence too long. ## Say what is, not what was or what not to do From 2466fb1759f5ffcdc6165f7d4798ec5511f0a799 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 10:46:42 -1000 Subject: [PATCH 076/312] Fold the prose rules into the existing sections No separate Prose heading: lead-with-the-verb joins Examples, sentence length, throat-clearing and joke length join Writing and Comment style. AI-assisted (Claude) --- .agents/skills/writing-docs/SKILL.md | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 41d4e664af..0e393cfcc8 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -14,22 +14,6 @@ Aim for about 40% of the length an LLM writes by default. Cut qualifiers, restatements, and the sentence that explains the sentence before it. A comment earning its place says something the code cannot. -## Prose - -**Lead with the thing itself.** "Returns the pitches sounding at an offset," not -"This method can be used to obtain the pitches that are sounding." - -**Vary sentence length.** Three medium declaratives in a row is where the eye -slides off the page. A short one lands. - -**Cut throat-clearing.** "It is important to note that," "Note that in general," -"This allows the user to." Start at the verb. - -**Humor is house style, not a lapse.** `Cannot determine sharps for quarter-tone -keys! silly!` and "it'd wig me out if I ever didn't see them in reverse -alphabetical order" are both shipping music21. A joke needing a second sentence -to land is one sentence too long. - ## Say what is, not what was or what not to do Describe current behavior. Do not narrate the bug you just fixed, the old @@ -112,6 +96,9 @@ scramble "Chaminade", not "puppy". If you cannot think of a reason a musician wo Describe what a parameter is and does in English if it is not obvious; type alone is not documentation. +Lead with the thing itself: "Returns the pitches sounding at an offset," not "This method +can be used to obtain the pitches that are sounding." + No dull repetition in docs. A bit of humor is welcome in docs; the docs are written for humans who will close the window if they are dull. If seven methods do essentially the same thing, give extensive docs the first time and then later methods can refer back to the first method. Don't repeat the same docs over and over. @@ -153,5 +140,7 @@ comments in shipping code, not to tests. - When wording is dictated to the agent to substitute for original wording, use it. Do not add parentheticals. Only fix obvious typos. - No weapon-metaphors or overly militaristic language. Avoid "blast radius", "rearm", "landmine", "detonate" in issues/PR/code. Trigger or fire events is so commonly used that they're okay. +- Vary sentence length; three medium declaratives in a row is where the eye slides off the page. Cut throat-clearing — "It is important to note that", "Note that in general" — and start at the verb. +- A joke needing a second sentence to land is one sentence too long. `Cannot determine sharps for quarter-tone keys! silly!` is the register. From 1afa86e8cd89da6dfd6347e7672c26c0f7144331 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 10:55:02 -1000 Subject: [PATCH 077/312] Start coverage before music21 is imported Splitting the runner fix from its own consequence: importing modules by package name stopped re-executing them, and reported coverage fell from 93.33% to 77.20%. The re-execution was propping up the number. testSingleCoreAll called coverageM21.getCoverage() at import time, several lines below its own `from music21 import ...` statements, so every class body, def and module constant had already run before cov.start(). Measured directly: with coverage started first, importing music21 alone covers 88/430 statements in key.py and 119/710 in tempo.py; started afterwards, zero. That is the missing 16%. Nothing inside the package can start coverage early, since importing it imports music21. So coverage now wraps the process: `coverage run -m music21.test.testSingleCoreAll ci` on the pinned Python, plain python elsewhere. The omit and exclude lists move to .coveragerc, which the in-process Coverage() was already reading for everything else, and coverageM21.py goes away with the plumbing it existed to hold. Local full run: 93.150% on 3.12, against coveralls' 93.331% on 3.13. Also drops the `from music21 import key` in key.Test.testNonTraditional, which existed only to dodge the shadow modules. AI-assisted (Claude) --- .coveragerc | 8 +++- .github/workflows/maincheck.yml | 11 ++++- music21/key.py | 8 ++-- music21/test/coverageM21.py | 70 ------------------------------- music21/test/testSingleCoreAll.py | 17 ++------ 5 files changed, 24 insertions(+), 90 deletions(-) delete mode 100644 music21/test/coverageM21.py diff --git a/.coveragerc b/.coveragerc index a134e125e3..e5a0bef7f6 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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__.: diff --git a/.github/workflows/maincheck.yml b/.github/workflows/maincheck.yml index d046ccaec2..0537f2c6f1 100644 --- a/.github/workflows/maincheck.yml +++ b/.github/workflows/maincheck.yml @@ -35,10 +35,19 @@ 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 != '3.13' }} run: uv run python -c 'from music21.test.testSingleCoreAll import ciMain as ci; ci()' + # Coverage has to wrap the whole process: anything imported from inside the + # music21 package imports music21 first, and by then every class body and def + # has already run and would read as uncovered. Run on a MIDDLE supported + # Python, so failures on the newest and oldest stay quick to see. + # When changing the version, change the two conditions below it as well. + - name: Run Main Test script with coverage + if: ${{ matrix.python-version == '3.13' }} + 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 + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_SERVICE_NAME: github run: uv run coveralls diff --git a/music21/key.py b/music21/key.py index b23054f513..05a2d69a01 100644 --- a/music21/key.py +++ b/music21/key.py @@ -1390,12 +1390,10 @@ 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), '') @@ -1403,7 +1401,7 @@ def testNonTraditional(self): # 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): ''' diff --git a/music21/test/coverageM21.py b/music21/test/coverageM21.py deleted file mode 100644 index 51f7d10fd3..0000000000 --- a/music21/test/coverageM21.py +++ /dev/null @@ -1,70 +0,0 @@ -# ------------------------------------------------------------------------------ -# Name: coverageM21.py -# Purpose: Starts Coverage w/ default arguments -# -# Authors: Christopher Ariza -# Michael Scott Asato Cuthbert -# -# Copyright: Copyright © 2014-15 Michael Scott Asato Cuthbert -# License: BSD, see license.txt -# ------------------------------------------------------------------------------ -from __future__ import annotations - -import sys - -omit_modules = [ - 'dist/dist.py', - 'music21/test/*', - 'music21/configure.py', - 'music21/figuredBass/examples.py', - 'music21/alpha/*', -] - -# THESE ARE NOT RELEVANT FOR coveralls.io -- edit .coveragerc to change that -exclude_lines = [ - r'\s*import music21\s*', - r'\s*music21.mainTest\(\)\s*', - r'.*#\s*pragma:\s*no cover.*', - r'class TestExternal.*', - r'class TestSlow.*', - r'\s*if TYPE_CHECKING:\s*', - r'\s*if t.TYPE_CHECKING:\s*', -] - - -def getCoverage(overrideVersion=False): - # MEMORY / NOTE FOR UPDATING PYTHON: - # Run this on a MIDDLE supported Python version so that we can - # check timing of newest vs oldest, AND so that - # we can quickly see failures on newest and oldest. - # (The odds of a failure on the middle version are low if - # the newest and oldest are passing.) - # - # Note the .minor == 13 -- that makes it only run on 3.13 - # - # When changing the version, be sure also to change - # .github/workflows/maincheck.yml's line: - # if: ${{ matrix.python-version == '3.13' }} - if overrideVersion or sys.version_info.minor == 13: - try: - # noinspection PyPackageRequirements - import coverage # type: ignore - cov = coverage.Coverage(omit=omit_modules) # , debug='trace') - for e in exclude_lines: - cov.exclude(e, which='exclude') - cov.start() - import music21 # pylint: disable=unused-import # noqa: F401 - except ImportError: - cov = None - else: - cov = None - return cov - -def startCoverage(cov): - if cov is not None: - cov.start() - -def stopCoverage(cov): - if cov is not None: - cov.stop() - cov.save() diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py index 6fd06d6dda..72c99d92f4 100644 --- a/music21/test/testSingleCoreAll.py +++ b/music21/test/testSingleCoreAll.py @@ -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, @@ -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): @@ -136,17 +128,16 @@ 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: + # 'ci' runs what GitHub Actions runs; other arguments are test group names. + if sys.argv[1:2] == ['ci']: + ciMain() + elif len(sys.argv) >= 2: unused_returnCode = main(sys.argv[1:]) else: unused_returnCode = main() From 19cee5c618f21e28728f0f260a012199eaaaf72a Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 11:36:06 -1000 Subject: [PATCH 078/312] Flatten the __main__ dispatch in testSingleCoreAll sys.argv[1:2] == ['ci'] was a slice used only to dodge IndexError on the no-argument run. Test the length first and the rest reads straight through. Drop unused_returnCode with it; nothing consumed the value. AI-assisted (Claude) --- music21/test/testSingleCoreAll.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/music21/test/testSingleCoreAll.py b/music21/test/testSingleCoreAll.py index 72c99d92f4..547416de2a 100644 --- a/music21/test/testSingleCoreAll.py +++ b/music21/test/testSingleCoreAll.py @@ -135,10 +135,10 @@ def ciMain(): # ------------------------------------------------------------------------------ if __name__ == '__main__': # 'ci' runs what GitHub Actions runs; other arguments are test group names. - if sys.argv[1:2] == ['ci']: + if len(sys.argv) < 2: + main() + elif sys.argv[1] == 'ci': ciMain() - elif len(sys.argv) >= 2: - unused_returnCode = main(sys.argv[1:]) else: - unused_returnCode = main() + main(sys.argv[1:]) From f597000662d38cd02c63a48a6ff63f6117954974 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 11:37:52 -1000 Subject: [PATCH 079/312] Name the coverage Python version once PY_VERSION_WITH_COVERAGE at workflow level; the three step conditions read it from the env context. AI-assisted (Claude) --- .github/workflows/maincheck.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/maincheck.yml b/.github/workflows/maincheck.yml index 0537f2c6f1..45ece16361 100644 --- a/.github/workflows/maincheck.yml +++ b/.github/workflows/maincheck.yml @@ -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 @@ -35,18 +40,13 @@ 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 != '3.13' }} + if: ${{ matrix.python-version != env.PY_VERSION_WITH_COVERAGE }} run: uv run python -c 'from music21.test.testSingleCoreAll import ciMain as ci; ci()' - # Coverage has to wrap the whole process: anything imported from inside the - # music21 package imports music21 first, and by then every class body and def - # has already run and would read as uncovered. Run on a MIDDLE supported - # Python, so failures on the newest and oldest stay quick to see. - # When changing the version, change the two conditions below it as well. - name: Run Main Test script with coverage - if: ${{ matrix.python-version == '3.13' }} + 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' }} + if: ${{ matrix.python-version == env.PY_VERSION_WITH_COVERAGE }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_SERVICE_NAME: github From e786c52ba5f71f2a3ac5c1508571d0b43c96a34b Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 12:20:29 -1000 Subject: [PATCH 080/312] pylint is optional; drop the coverageM21 pointer AI-assisted (Claude) --- .agents/skills/running-tests/SKILL.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.agents/skills/running-tests/SKILL.md b/.agents/skills/running-tests/SKILL.md index 116e4be7a0..92601b045a 100644 --- a/.agents/skills/running-tests/SKILL.md +++ b/.agents/skills/running-tests/SKILL.md @@ -155,11 +155,11 @@ Regression cases for a bug fix belong in `Test`, never in a doctest. See the ```bash uv run ruff check music21 uv run mypy music21 -uv run pylint -j4 music21 --rcfile=.pylintrc # catches what the first two miss +uv run pylint -j4 music21 --rcfile=.pylintrc # optional: run only if major refactoring since it was last run. ``` Coverage is expected to rise with each contribution -(https://coveralls.io/github/cuthbertLab/music21). CI measures it on a single -pinned Python -- the **middle** supported version, see -`coverageM21.getCoverage`. `# pragma: no cover` exists for genuinely -untriggerable code and is otherwise discouraged. +(https://coveralls.io/github/cuthbertLab/music21). CI measures it on one pinned +Python, a middle supported version, so failures on the newest and oldest return +first. `# pragma: no cover` exists for genuinely untriggerable code and is +otherwise discouraged. From ee8808d5663a31bbf4c7ba6fd0e3b2ba62307bb4 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 12:24:29 -1000 Subject: [PATCH 081/312] Point AGENTS.md at the workflow variable for the coverage Python coverageM21.py was removed in #2014; the pin is now PY_VERSION_WITH_COVERAGE. AI-assisted (Claude) --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 37adebbe3e..c61555b625 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,7 +82,7 @@ - `music21` supports at least the last two released versions of Python and up to whatever Python version Google Colab runs (unless it gets EOL). Policy can change as features are added. - The coverage CI run is intentionally pinned to the **middle** supported - Python version. See `coverageM21.getCoverage`. + Python version. See `PY_VERSION_WITH_COVERAGE` in `.github/workflows/maincheck.yml`. # PRs and Issues From c6ae99a5e385a2088a758bc0bc2c997a76c6e646 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 12:38:07 -1000 Subject: [PATCH 082/312] Drop the self-imports that existed only to dodge the shadow modules ModuleGather.getModule used to load each file as a separate top-level module, so a test's own module globals held a different copy of its classes than music21.spanner did. Tests worked around it by re-importing their own module -- `from music21 import spanner` inside spanner.Test -- which restored identity but also made the module read as though it were a foreign one. Now that the runner imports by fully-qualified name (#2014), the workaround is dead weight. Removes 90 self-imports across 21 modules and rewrites their references to the bare module-level names. Also drops three comments that only explained the workaround (bar.py's "avoid not same class error" plus its pylint disable, parallel.py's "we need the full path to the modules", iterator.py's noinspection), and re-aligns three continuation lines the shortened call names left over-indented. Not touched: clef.clefFromString's `from music21 import clef as myself`, which is a real dynamic lookup over the module's own namespace, not a test artifact. Verified with multiprocessTest (5299 tests) and testSingleCoreAll (5022), plus ruff, mypy, pylint, and a pycodestyle E12 pass held to its prior count. AI-assisted (Claude) --- music21/abcFormat/__init__.py | 9 +- music21/bar.py | 2 - music21/common/parallel.py | 3 - music21/configure.py | 17 ++- music21/duration.py | 6 +- music21/dynamics.py | 5 +- music21/freezeThaw.py | 21 ++-- music21/harmony.py | 44 ++++---- music21/instrument.py | 96 ++++++++--------- music21/layout.py | 3 +- music21/note.py | 6 +- music21/roman.py | 150 ++++++++++++--------------- music21/romanText/clercqTemperley.py | 3 +- music21/romanText/translate.py | 8 +- music21/sieve.py | 12 +-- music21/spanner.py | 106 +++++++------------ music21/stream/iterator.py | 4 +- music21/tablature.py | 3 +- music21/tempo.py | 62 +++++------ music21/variant.py | 15 +-- music21/volume.py | 20 ++-- 21 files changed, 245 insertions(+), 350 deletions(-) diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py index 4c15bb7349..7319a00509 100644 --- a/music21/abcFormat/__init__.py +++ b/music21/abcFormat/__init__.py @@ -3935,8 +3935,7 @@ def testBow(self): def testAcc(self): from music21.abcFormat import testFiles - from music21 import abcFormat - ah = abcFormat.ABCHandler() + ah = ABCHandler() ah.process(testFiles.accTest) # noinspection SpellCheckingInspection tokensCorrect = ''' @@ -4035,11 +4034,11 @@ def testAcc(self): j = 0 k = 0 for token in tokens: - if isinstance(token, abcFormat.ABCAccent): + if isinstance(token, ABCAccent): i += 1 - elif isinstance(token, abcFormat.ABCStraccent): + elif isinstance(token, ABCStraccent): j += 1 - elif isinstance(token, abcFormat.ABCTenuto): + elif isinstance(token, ABCTenuto): k += 1 self.assertEqual(i, 2) self.assertEqual(j, 2) diff --git a/music21/bar.py b/music21/bar.py index 05b1c35f01..57f02a449a 100644 --- a/music21/bar.py +++ b/music21/bar.py @@ -408,8 +408,6 @@ def testSortOrder(self): def testFreezeThaw(self): from music21 import converter from music21 import stream - # pylint: disable=redefined-outer-name - from music21.bar import Barline # avoid not same class error b = Barline() self.assertNotIn('StyleMixin', b.classes) diff --git a/music21/common/parallel.py b/music21/common/parallel.py index d10c509339..438d2cf5c4 100644 --- a/music21/common/parallel.py +++ b/music21/common/parallel.py @@ -272,11 +272,8 @@ def _countUnpacked(i: int, filename: str) -> bool: class Test(unittest.TestCase): - # pylint: disable=redefined-outer-name def x_figure_out_segfault_testMultiprocess(self) -> None: files = ['bach/bwv66.6', 'schoenberg/opus19', 'AcaciaReel'] - # for importing into testSingleCoreAll we need the full path to the modules - from music21.common.parallel import _countN, _countUnpacked output = runParallel(files, _countN) self.assertEqual(output, [165, 50, 131]) runParallel(files, diff --git a/music21/configure.py b/music21/configure.py index e5e3ad3c1b..024af6d3c4 100644 --- a/music21/configure.py +++ b/music21/configure.py @@ -1545,9 +1545,8 @@ def testConfigurationAssistant(self): class Test(unittest.TestCase): def testYesOrNo(self): - from music21 import configure - d = configure.YesOrNo(default=True, tryAgain=False, - promptHeader='Are you ready to continue?') + d = YesOrNo(default=True, tryAgain=False, + promptHeader='Are you ready to continue?') d.askUser('n') self.assertEqual(str(d.getResult()), 'False') d.askUser('y') @@ -1557,8 +1556,8 @@ def testYesOrNo(self): d.askUser('blah') # gets default self.assertEqual(str(d.getResult()), '') - d = configure.YesOrNo(default=None, tryAgain=False, - promptHeader='Are you ready to continue?') + d = YesOrNo(default=None, tryAgain=False, + promptHeader='Are you ready to continue?') d.askUser('n') self.assertEqual(str(d.getResult()), 'False') d.askUser('y') @@ -1569,13 +1568,11 @@ def testYesOrNo(self): self.assertEqual(str(d.getResult()), '') def testSelectFromList(self): - from music21 import configure - d = configure.SelectFromList(default=1) + d = SelectFromList(default=1) self.assertEqual(d._default, 1) def testSelectMusicXMLReaders(self): - from music21 import configure - d = configure.SelectMusicXMLReader() + d = SelectMusicXMLReader() # force request to user by returning no valid results def getValidResults(force=None): @@ -1585,7 +1582,7 @@ def getValidResults(force=None): d.askUser(force='n', skipIntro=True) # reject option to open in a browser post = d.getResult() # returns a bad condition b/c there are no options and user entered 'n' - self.assertIsInstance(post, configure.BadConditions) + self.assertIsInstance(post, BadConditions) def testMuseScoreNameRe(self): ''' diff --git a/music21/duration.py b/music21/duration.py index 5a7b27c6f9..a0af8d0460 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -3915,16 +3915,14 @@ def testAugmentOrDiminish(self): "DurationTuple(type='16th', dots=0, quarterLength=0.25)") def testUnlinkedTypeA(self): - from music21 import duration - - du = duration.Duration() + du = Duration() du.linked = False du.quarterLength = 5.0 du.type = 'quarter' self.assertEqual(du.quarterLength, 5.0) self.assertEqual(du.type, 'quarter') - d = duration.Duration() + d = Duration() self.assertTrue(d.linked) # note set d.linked = False d.type = 'quarter' diff --git a/music21/dynamics.py b/music21/dynamics.py index ecd20297a6..146ea79783 100644 --- a/music21/dynamics.py +++ b/music21/dynamics.py @@ -430,13 +430,12 @@ def testBasic(self): def testCorpusDynamicsWedge(self): from music21 import corpus - from music21 import dynamics a = corpus.parse('opus41no1/movement2') # has dynamics! - b = a.parts[0].flatten().getElementsByClass(dynamics.Dynamic) + b = a.parts[0].flatten().getElementsByClass(Dynamic) self.assertEqual(len(b), 35) - b = a.parts[0].flatten().getElementsByClass(dynamics.DynamicWedge) + b = a.parts[0].flatten().getElementsByClass(DynamicWedge) self.assertEqual(len(b), 2) def testMusicxmlOutput(self): diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py index 86cf877fee..3176ec6fc3 100644 --- a/music21/freezeThaw.py +++ b/music21/freezeThaw.py @@ -1047,7 +1047,6 @@ def testFreezeThawCorpusFileWithSpanners(self): self.assertEqual(len(s.parts[0].measure(7).notes), 6) def x_testSimplePickle(self): - from music21 import freezeThaw from music21 import corpus c = corpus.parse('bwv66.6').parts[0].measure(0).notes @@ -1061,7 +1060,7 @@ def x_testSimplePickle(self): n1 = c[0] n2 = c[1] - sf = freezeThaw.StreamFreezer(c, fastButUnsafe=True) + sf = StreamFreezer(c, fastButUnsafe=True) sf.setupSerializationScaffold() for dummy in n1.sites.siteDict: pass @@ -1088,17 +1087,16 @@ def x_testSimplePickle(self): # s.show('t') def x_testFreezeThawPickle(self): - from music21 import freezeThaw from music21 import corpus c = corpus.parse('luca/gloria') # c.show('t') - sf = freezeThaw.StreamFreezer(c, fastButUnsafe=True) + sf = StreamFreezer(c, fastButUnsafe=True) d = sf.writeStr() # print(d) - st = freezeThaw.StreamThawer() + st = StreamThawer() st.openStr(d) s = st.stream @@ -1107,7 +1105,6 @@ def x_testFreezeThawPickle(self): pass def testFreezeThawSimpleVariant(self): - from music21 import freezeThaw from music21 import stream from music21 import note @@ -1126,15 +1123,14 @@ def testFreezeThawSimpleVariant(self): s.insert(0, v) - sf = freezeThaw.StreamFreezer(s) + sf = StreamFreezer(s) d = sf.writeStr() - st = freezeThaw.StreamThawer() + st = StreamThawer() st.openStr(d) s = st.stream def testFreezeThawVariant(self): - from music21 import freezeThaw from music21 import corpus from music21 import stream from music21 import note @@ -1156,14 +1152,14 @@ def testFreezeThawVariant(self): # test Variant is in stream unused_v1 = c.parts.first().getElementsByClass(variant.Variant).first() - sf = freezeThaw.StreamFreezer(c, fastButUnsafe=True) + sf = StreamFreezer(c, fastButUnsafe=True) # sf.v = v d = sf.writeStr() # print(d) # print('thawing.') - st = freezeThaw.StreamThawer() + st = StreamThawer() st.openStr(d) s = st.stream # s.show('lily.pdf') @@ -1176,7 +1172,6 @@ def testFreezeThawVariant(self): def testSerializationScaffoldA(self): from music21 import note from music21 import stream - from music21 import freezeThaw n1 = note.Note() @@ -1186,7 +1181,7 @@ def testSerializationScaffoldA(self): s1.append(n1) s2.append(n1) - sf = freezeThaw.StreamFreezer(s2, fastButUnsafe=False) + sf = StreamFreezer(s2, fastButUnsafe=False) sf.setupSerializationScaffold() # test safety diff --git a/music21/harmony.py b/music21/harmony.py index 3acc12e99a..ec9dabb64c 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -2634,8 +2634,7 @@ def realizeChordSymbolDurations(piece): class Test(unittest.TestCase): def testChordAttributes(self): - from music21 import harmony - cs = harmony.ChordSymbol('Cm') + cs = ChordSymbol('Cm') self.assertEqual(str(cs), '') self.assertEqual( str(cs.pitches), @@ -2644,15 +2643,13 @@ def testChordAttributes(self): self.assertTrue(cs.isConsonant()) def testBasic(self): - from music21 import harmony - h = harmony.Harmony() - hd = harmony.ChordStepModification('add', 4) + h = Harmony() + hd = ChordStepModification('add', 4) h.addChordStepModification(hd) self.assertEqual(len(h.chordStepModifications), 1) def testChordKindSetting(self): - from music21 import harmony - cs = harmony.ChordSymbol() + cs = ChordSymbol() cs.root('E-') cs.bass('B-', allow_add=True) cs.inversion(2, transposeOnSet=False) @@ -2717,38 +2714,37 @@ def testClassSortOrderHarmony(self): self.assertIs(n.getContextByClass('ChordSymbol'), cs) def testNoChord(self): - from music21 import harmony - nc = harmony.NoChord() + nc = NoChord() self.assertEqual('none', nc.chordKind) self.assertEqual('N.C.', nc.chordKindStr) self.assertEqual('N.C.', nc.figure) - nc = harmony.NoChord('NC') + nc = NoChord('NC') self.assertEqual('none', nc.chordKind) self.assertEqual('NC', nc.chordKindStr) self.assertEqual('NC', nc.figure) - nc = harmony.NoChord('None') + nc = NoChord('None') self.assertEqual('none', nc.chordKind) self.assertEqual('None', nc.chordKindStr) self.assertEqual('None', nc.figure) - nc = harmony.NoChord(kind='none') + nc = NoChord(kind='none') self.assertEqual('none', nc.chordKind) self.assertEqual('N.C.', nc.chordKindStr) self.assertEqual('N.C.', nc.figure) - nc = harmony.NoChord(kindStr='No Chord') + nc = NoChord(kindStr='No Chord') self.assertEqual('none', nc.chordKind) self.assertEqual('No Chord', nc.chordKindStr) self.assertEqual('No Chord', nc.figure) - nc = harmony.NoChord('NC', kindStr='No Chord') + nc = NoChord('NC', kindStr='No Chord') self.assertEqual('none', nc.chordKind) self.assertEqual('No Chord', nc.chordKindStr) self.assertEqual('NC', nc.figure) - nc = harmony.NoChord(root='C', bass='E', kind='none') + nc = NoChord(root='C', bass='E', kind='none') self.assertEqual('N.C.', nc.chordKindStr) self.assertEqual('N.C.', nc.figure) @@ -2761,9 +2757,8 @@ def testNoChord(self): self.assertEqual(0, len(nc.pitches)) def testInvalidRoots(self): - from music21 import harmony with self.assertRaises(ValueError) as context: - harmony.ChordSymbol('H-7') + ChordSymbol('H-7') self.assertEqual( str(context.exception), @@ -2772,7 +2767,7 @@ def testInvalidRoots(self): with self.assertRaises(ValueError) as context: # noinspection SpellCheckingInspection - harmony.ChordSymbol('Garg7') + ChordSymbol('Garg7') self.assertEqual( str(context.exception), @@ -2782,9 +2777,8 @@ def testInvalidRoots(self): ) def testInvalidSymbol(self): - from music21 import harmony c = chord.Chord(('A#', 'C', 'E')) - cs = harmony.chordSymbolFromChord(c) + cs = chordSymbolFromChord(c) self.assertEqual(cs.figure, 'Chord Symbol Cannot Be Identified') def testRegexEdgeCases(self): @@ -3257,15 +3251,14 @@ def testUpdatePitchesFalse(self): class TestExternal(unittest.TestCase): def testReadInXML(self): - from music21 import harmony from music21 import corpus from music21 import stream testFile = corpus.parse('leadSheet/fosterBrownHair.xml') # testFile.show('text') - testFile = harmony.realizeChordSymbolDurations(testFile) + testFile = realizeChordSymbolDurations(testFile) # testFile.show() - chordSymbols = testFile.flatten().getElementsByClass(harmony.ChordSymbol) + chordSymbols = testFile.flatten().getElementsByClass(ChordSymbol) s = stream.Stream() for cS in chordSymbols: @@ -3277,7 +3270,6 @@ def testReadInXML(self): # self.assertEqual(len(csChords), 40) def testChordRealization(self): - from music21 import harmony from music21 import corpus from music21 import note from music21 import stream @@ -3288,8 +3280,8 @@ def testChordRealization(self): # tests, and adjust 57 accordingly testFile = corpus.parse('demos/ComprehensiveChordSymbolsTestFile.xml') - testFile = harmony.realizeChordSymbolDurations(testFile) - chords = testFile.flatten().getElementsByClass(harmony.ChordSymbol) + testFile = realizeChordSymbolDurations(testFile) + chords = testFile.flatten().getElementsByClass(ChordSymbol) # testFile.show() s = stream.Stream() # i = 0 diff --git a/music21/instrument.py b/music21/instrument.py index 0d819a0a90..16772e6d23 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -2600,82 +2600,79 @@ def testMusicXMLExport(self): # s3.show() def testPartitionByInstrumentA(self): - from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() - p1.append(instrument.Piano()) + p1.append(Piano()) p2 = stream.Part() - p2.append(instrument.Piccolo()) + p2.append(Piccolo()) s.insert(0, p1) s.insert(0, p2) - post = instrument.partitionByInstrument(s) + post = partitionByInstrument(s) self.assertEqual(len(post), 2) - self.assertEqual(len(post.flatten().getElementsByClass(instrument.Instrument)), 2) + self.assertEqual(len(post.flatten().getElementsByClass(Instrument)), 2) # post.show('t') # one Stream with multiple instruments s = stream.Stream() - s.insert(0, instrument.PanFlute()) - s.insert(20, instrument.ReedOrgan()) + s.insert(0, PanFlute()) + s.insert(20, ReedOrgan()) - post = instrument.partitionByInstrument(s) + post = partitionByInstrument(s) self.assertEqual(len(post), 2) - self.assertEqual(len(post[instrument.Instrument]), 2) + self.assertEqual(len(post[Instrument]), 2) # post.show('t') def testPartitionByInstrumentB(self): - from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note(), 6) p2 = stream.Part() - p2.append(instrument.Piccolo()) + p2.append(Piccolo()) p2.repeatAppend(note.Note(), 12) s.insert(0, p1) s.insert(0, p2) - post = instrument.partitionByInstrument(s) + post = partitionByInstrument(s) self.assertEqual(len(post), 2) - self.assertEqual(len(post[instrument.Instrument]), 2) + self.assertEqual(len(post[Instrument]), 2) self.assertEqual(len(post.parts[0].notes), 6) self.assertEqual(len(post.parts[1].notes), 12) def testPartitionByInstrumentC(self): - from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note('a'), 6) # will go in next available offset - p1.append(instrument.AcousticGuitar()) + p1.append(AcousticGuitar()) p1.repeatAppend(note.Note('b'), 3) p2 = stream.Part() - p2.append(instrument.Piccolo()) + p2.append(Piccolo()) p2.repeatAppend(note.Note('c'), 2) - p2.append(instrument.Flute()) + p2.append(Flute()) p2.repeatAppend(note.Note('d'), 4) s.insert(0, p1) s.insert(0, p2) - post = instrument.partitionByInstrument(s) + post = partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments - self.assertEqual(len(post[instrument.Instrument]), 4) + self.assertEqual(len(post[Instrument]), 4) self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 6) self.assertEqual(post.parts[1].getInstrument().instrumentName, 'Acoustic Guitar') @@ -2689,34 +2686,33 @@ def testPartitionByInstrumentC(self): # post.show('t') def testPartitionByInstrumentD(self): - from music21 import instrument from music21 import stream # basic case of instruments in Parts s = stream.Score() p1 = stream.Part() - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note('a'), 6) # will go in next available offset - p1.append(instrument.AcousticGuitar()) + p1.append(AcousticGuitar()) p1.repeatAppend(note.Note('b'), 3) - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note('e'), 5) p2 = stream.Part() - p2.append(instrument.Piccolo()) + p2.append(Piccolo()) p2.repeatAppend(note.Note('c'), 2) - p2.append(instrument.Flute()) + p2.append(Flute()) p2.repeatAppend(note.Note('d'), 4) - p2.append(instrument.Piano()) + p2.append(Piano()) p2.repeatAppend(note.Note('f'), 1) s.insert(0, p1) s.insert(0, p2) - post = instrument.partitionByInstrument(s) + post = partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments - self.assertEqual(len(post[instrument.Instrument]), 4) + self.assertEqual(len(post[Instrument]), 4) # piano spans are joined together self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') self.assertEqual(len(post.parts[0].notes), 12) @@ -2728,32 +2724,31 @@ def testPartitionByInstrumentD(self): # post.show('t') def testPartitionByInstrumentE(self): - from music21 import instrument from music21 import stream # basic case of instruments in Parts # s = stream.Score() p1 = stream.Part() - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note('a'), 6) # will go in next available offset - p1.append(instrument.AcousticGuitar()) + p1.append(AcousticGuitar()) p1.repeatAppend(note.Note('b'), 3) - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note('e'), 5) - p1.append(instrument.Piccolo()) + p1.append(Piccolo()) p1.repeatAppend(note.Note('c'), 2) - p1.append(instrument.Flute()) + p1.append(Flute()) p1.repeatAppend(note.Note('d'), 4) - p1.append(instrument.Piano()) + p1.append(Piano()) p1.repeatAppend(note.Note('f'), 1) s = p1 - post = instrument.partitionByInstrument(s) + post = partitionByInstrument(s) self.assertEqual(len(post), 4) # 4 instruments - self.assertEqual(len(post[instrument.Instrument]), 4) + self.assertEqual(len(post[Instrument]), 4) # piano spans are joined together self.assertEqual(post.parts[0].getInstrument().instrumentName, 'Piano') @@ -2767,16 +2762,15 @@ def testPartitionByInstrumentE(self): [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 9.0, 10.0, 11.0, 12.0, 13.0, 20.0]) def testPartitionByInstrumentF(self): - from music21 import instrument from music21 import stream s1 = stream.Stream() - s1.append(instrument.AcousticGuitar()) + s1.append(AcousticGuitar()) s1.append(note.Note()) - s1.append(instrument.Tuba()) + s1.append(Tuba()) s1.append(note.Note()) - post = instrument.partitionByInstrument(s1) + post = partitionByInstrument(s1) self.assertEqual(len(post), 2) # 4 instruments # def testPartitionByInstrumentDocTest(self): @@ -2803,14 +2797,12 @@ def testPartitionByInstrumentF(self): # p.makeRests(fillGaps=True, inPlace=True) def testLanguageChoice(self): - from music21 import instrument - # fromString testString = 'Klarinette' # German name # Works when language not specified - self.assertEqual(instrument.fromString(testString).instrumentName, + self.assertEqual(fromString(testString).instrumentName, testString) workingExamples = ['german', # Works with correct language for the term @@ -2818,7 +2810,7 @@ def testLanguageChoice(self): ] for langStr in workingExamples: - instrName = instrument.fromString(testString, language=langStr).instrumentName + instrName = fromString(testString, language=langStr).instrumentName self.assertEqual(instrName, testString) failingExamples = ['french', # Error when the language doesn't match the term @@ -2827,20 +2819,20 @@ def testLanguageChoice(self): for langStr in failingExamples: self.assertRaises(InstrumentException, - instrument.fromString, + fromString, testString, language=langStr) # getAllNamesForInstrument - inst = instrument.Flute() + inst = Flute() # Working example - self.assertEqual(instrument.getAllNamesForInstrument(inst, - language=SearchLanguage.ABBREVIATION), + self.assertEqual(getAllNamesForInstrument(inst, + language=SearchLanguage.ABBREVIATION), {'abbreviation': ['fl']}) # Error for unsupported language self.assertRaises(InstrumentException, - instrument.getAllNamesForInstrument, + getAllNamesForInstrument, inst, language='finnish') diff --git a/music21/layout.py b/music21/layout.py index 11dcf60f08..580c8b2329 100644 --- a/music21/layout.py +++ b/music21/layout.py @@ -1660,9 +1660,8 @@ def testGetStaffLayoutFromStaff(self): We have had problems with attributes disappearing. ''' from music21 import corpus - from music21 import layout lt = corpus.parse('demos/layoutTest.xml') - ls = layout.divideByPages(lt, fastMeasures=True) + ls = divideByPages(lt, fastMeasures=True) hiddenStaff = ls.pages[0].systems[3].staves[1] self.assertTrue(repr(hiddenStaff).endswith('Staff 11: p.1, sys.4, st.2>'), diff --git a/music21/note.py b/music21/note.py index aaed8c01d5..1f1fd4f6a1 100644 --- a/music21/note.py +++ b/music21/note.py @@ -2077,14 +2077,12 @@ def testSingle(self): ''' Need to test direct note creation w/o stream ''' - from music21 import note - a = note.Note('D-3') + a = Note('D-3') a.quarterLength = 2.25 if self.show: a.show() def testBasic(self): - from music21 import note from music21 import stream a = stream.Stream() @@ -2093,7 +2091,7 @@ def testBasic(self): ('d-3', 2.5), ('c#6', 3.25), ('a--5', 0.5), ('f#2', 1.75), ('g-3', (4 / 3)), ('d#6', (2 / 3)) ]: - b = note.Note() + b = Note() b.quarterLength = qLen b.name = pitchName # Pylint going crazy here diff --git a/music21/roman.py b/music21/roman.py index 146f99a062..51041220ca 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -4332,9 +4332,8 @@ def testYieldRemoveA(self): c.remove(e) def testScaleDegreesA(self): - from music21 import roman k = key.Key('f#') # 3-sharps minor - rn = roman.RomanNumeral('V', k) + rn = RomanNumeral('V', k) self.assertEqual(str(rn.key), 'f# minor') self.assertEqual( str(rn.pitches), @@ -4348,8 +4347,7 @@ def testScaleDegreesA(self): ) def testNeapolitanAndHalfDiminished(self): - from music21 import roman - alteredChordHalfDim3rdInv = roman.RomanNumeral( + alteredChordHalfDim3rdInv = RomanNumeral( 'bii/o42', scale.MajorScale('F')) self.assertEqual( [str(p) for p in alteredChordHalfDim3rdInv.pitches], @@ -4361,162 +4359,157 @@ def testNeapolitanAndHalfDiminished(self): self.assertEqual(cn, 'half-diminished seventh chord') def testOmittedFifth(self): - from music21 import roman c = chord.Chord('A3 E-4 G-4') k = key.Key('b-') - rnDim7 = roman.romanNumeralFromChord(c, k) + rnDim7 = romanNumeralFromChord(c, k) self.assertEqual(rnDim7.figure, 'viio7') def testAllFormsOfVII(self): - from music21 import roman - def p(c): return ' '.join([x.nameWithOctave for x in c.pitches]) k = key.Key('c') - rn = roman.RomanNumeral('viio', k) + rn = RomanNumeral('viio', k) self.assertEqual(p(rn), 'B4 D5 F5') - rn = roman.RomanNumeral('viio6', k) + rn = RomanNumeral('viio6', k) self.assertEqual(p(rn), 'D4 F4 B4') - rn = roman.RomanNumeral('viio64', k) + rn = RomanNumeral('viio64', k) self.assertEqual(p(rn), 'F4 B4 D5') - rn = roman.RomanNumeral('vii', k) + rn = RomanNumeral('vii', k) self.assertEqual(p(rn), 'B4 D5 F#5') - rn = roman.RomanNumeral('vii6', k) + rn = RomanNumeral('vii6', k) self.assertEqual(p(rn), 'D4 F#4 B4') - rn = roman.RomanNumeral('vii64', k) + rn = RomanNumeral('vii64', k) self.assertEqual(p(rn), 'F#4 B4 D5') - rn = roman.RomanNumeral('viio7', k) + rn = RomanNumeral('viio7', k) self.assertEqual(p(rn), 'B4 D5 F5 A-5') - rn = roman.RomanNumeral('viio65', k) + rn = RomanNumeral('viio65', k) self.assertEqual(p(rn), 'D4 F4 A-4 B4') - rn = roman.RomanNumeral('viio43', k) + rn = RomanNumeral('viio43', k) self.assertEqual(p(rn), 'F4 A-4 B4 D5') - rn = roman.RomanNumeral('viio42', k) + rn = RomanNumeral('viio42', k) self.assertEqual(p(rn), 'A-4 B4 D5 F5') - rn = roman.RomanNumeral('vii/o7', k) + rn = RomanNumeral('vii/o7', k) self.assertEqual(p(rn), 'B4 D5 F5 A5') # noinspection SpellCheckingInspection - rn = roman.RomanNumeral('viiø65', k) + rn = RomanNumeral('viiø65', k) self.assertEqual(p(rn), 'D4 F4 A4 B4') # noinspection SpellCheckingInspection - rn = roman.RomanNumeral('viiø43', k) + rn = RomanNumeral('viiø43', k) self.assertEqual(p(rn), 'F4 A4 B4 D5') - rn = roman.RomanNumeral('vii/o42', k) + rn = RomanNumeral('vii/o42', k) self.assertEqual(p(rn), 'A4 B4 D5 F5') - rn = roman.RomanNumeral('VII', k) + rn = RomanNumeral('VII', k) self.assertEqual(p(rn), 'B-4 D5 F5') - rn = roman.RomanNumeral('VII6', k) + rn = RomanNumeral('VII6', k) self.assertEqual(p(rn), 'D4 F4 B-4') - rn = roman.RomanNumeral('VII64', k) + rn = RomanNumeral('VII64', k) self.assertEqual(p(rn), 'F4 B-4 D5') - rn = roman.RomanNumeral('bVII', k) + rn = RomanNumeral('bVII', k) self.assertEqual(p(rn), 'B--4 D-5 F-5') - rn = roman.RomanNumeral('bVII6', k) + rn = RomanNumeral('bVII6', k) self.assertEqual(p(rn), 'D-4 F-4 B--4') - rn = roman.RomanNumeral('bVII64', k) + rn = RomanNumeral('bVII64', k) self.assertEqual(p(rn), 'F-4 B--4 D-5') - rn = roman.RomanNumeral('bvii', k) + rn = RomanNumeral('bvii', k) self.assertEqual(p(rn), 'B-4 D-5 F5') - rn = roman.RomanNumeral('bvii6', k) + rn = RomanNumeral('bvii6', k) self.assertEqual(p(rn), 'D-4 F4 B-4') - rn = roman.RomanNumeral('bvii64', k) + rn = RomanNumeral('bvii64', k) self.assertEqual(p(rn), 'F4 B-4 D-5') - rn = roman.RomanNumeral('bviio', k) + rn = RomanNumeral('bviio', k) self.assertEqual(p(rn), 'B-4 D-5 F-5') - rn = roman.RomanNumeral('bviio6', k) + rn = RomanNumeral('bviio6', k) self.assertEqual(p(rn), 'D-4 F-4 B-4') - rn = roman.RomanNumeral('bviio64', k) + rn = RomanNumeral('bviio64', k) self.assertEqual(p(rn), 'F-4 B-4 D-5') - rn = roman.RomanNumeral('#VII', k) + rn = RomanNumeral('#VII', k) self.assertEqual(p(rn), 'B4 D#5 F#5') - rn = roman.RomanNumeral('#vii', k) + rn = RomanNumeral('#vii', k) self.assertEqual(p(rn), 'B#4 D#5 F##5') - rn = roman.RomanNumeral('VII+', k) + rn = RomanNumeral('VII+', k) self.assertEqual(p(rn), 'B-4 D5 F#5') def testAllFormsOfVI(self): - from music21 import roman - def p(c): return ' '.join([x.nameWithOctave for x in c.pitches]) k = key.Key('c') - rn = roman.RomanNumeral('vio', k) + rn = RomanNumeral('vio', k) self.assertEqual(p(rn), 'A4 C5 E-5') - rn = roman.RomanNumeral('vio6', k) + rn = RomanNumeral('vio6', k) self.assertEqual(p(rn), 'C4 E-4 A4') - rn = roman.RomanNumeral('vio64', k) + rn = RomanNumeral('vio64', k) self.assertEqual(p(rn), 'E-4 A4 C5') - rn = roman.RomanNumeral('vi', k) + rn = RomanNumeral('vi', k) self.assertEqual(p(rn), 'A4 C5 E5') - rn = roman.RomanNumeral('vi6', k) + rn = RomanNumeral('vi6', k) self.assertEqual(p(rn), 'C4 E4 A4') - rn = roman.RomanNumeral('vi64', k) + rn = RomanNumeral('vi64', k) self.assertEqual(p(rn), 'E4 A4 C5') - rn = roman.RomanNumeral('vio7', k) + rn = RomanNumeral('vio7', k) self.assertEqual(p(rn), 'A4 C5 E-5 G-5') - rn = roman.RomanNumeral('vio65', k) + rn = RomanNumeral('vio65', k) self.assertEqual(p(rn), 'C4 E-4 G-4 A4') - rn = roman.RomanNumeral('vio43', k) + rn = RomanNumeral('vio43', k) self.assertEqual(p(rn), 'E-4 G-4 A4 C5') - rn = roman.RomanNumeral('vio42', k) + rn = RomanNumeral('vio42', k) self.assertEqual(p(rn), 'G-4 A4 C5 E-5') - rn = roman.RomanNumeral('viø7', k) + rn = RomanNumeral('viø7', k) self.assertEqual(p(rn), 'A4 C5 E-5 G5') - rn = roman.RomanNumeral('vi/o65', k) + rn = RomanNumeral('vi/o65', k) self.assertEqual(p(rn), 'C4 E-4 G4 A4') - rn = roman.RomanNumeral('vi/o43', k) + rn = RomanNumeral('vi/o43', k) self.assertEqual(p(rn), 'E-4 G4 A4 C5') - rn = roman.RomanNumeral('viø42', k) + rn = RomanNumeral('viø42', k) self.assertEqual(p(rn), 'G4 A4 C5 E-5') - rn = roman.RomanNumeral('VI', k) + rn = RomanNumeral('VI', k) self.assertEqual(p(rn), 'A-4 C5 E-5') - rn = roman.RomanNumeral('VI6', k) + rn = RomanNumeral('VI6', k) self.assertEqual(p(rn), 'C4 E-4 A-4') - rn = roman.RomanNumeral('VI64', k) + rn = RomanNumeral('VI64', k) self.assertEqual(p(rn), 'E-4 A-4 C5') - rn = roman.RomanNumeral('bVI', k) + rn = RomanNumeral('bVI', k) self.assertEqual(p(rn), 'A--4 C-5 E--5') - rn = roman.RomanNumeral('bVI6', k) + rn = RomanNumeral('bVI6', k) self.assertEqual(p(rn), 'C-4 E--4 A--4') - rn = roman.RomanNumeral('bVI64', k) + rn = RomanNumeral('bVI64', k) self.assertEqual(p(rn), 'E--4 A--4 C-5') - rn = roman.RomanNumeral('bvi', k) + rn = RomanNumeral('bvi', k) self.assertEqual(p(rn), 'A-4 C-5 E-5') - rn = roman.RomanNumeral('bvi6', k) + rn = RomanNumeral('bvi6', k) self.assertEqual(p(rn), 'C-4 E-4 A-4') - rn = roman.RomanNumeral('bvi64', k) + rn = RomanNumeral('bvi64', k) self.assertEqual(p(rn), 'E-4 A-4 C-5') - rn = roman.RomanNumeral('bvio', k) + rn = RomanNumeral('bvio', k) self.assertEqual(p(rn), 'A-4 C-5 E--5') - rn = roman.RomanNumeral('bvio6', k) + rn = RomanNumeral('bvio6', k) self.assertEqual(p(rn), 'C-4 E--4 A-4') - rn = roman.RomanNumeral('bvio64', k) + rn = RomanNumeral('bvio64', k) self.assertEqual(p(rn), 'E--4 A-4 C-5') - rn = roman.RomanNumeral('#VI', k) + rn = RomanNumeral('#VI', k) self.assertEqual(p(rn), 'A4 C#5 E5') - rn = roman.RomanNumeral('#vi', k) + rn = RomanNumeral('#vi', k) self.assertEqual(p(rn), 'A#4 C#5 E#5') - rn = roman.RomanNumeral('VI+', k) + rn = RomanNumeral('VI+', k) self.assertEqual(p(rn), 'A-4 C5 E5') def testRomanNumeralFromChordRaised67(self): @@ -4529,8 +4522,6 @@ def testRomanNumeralFromChordRaised67(self): This test was AI-assisted (Claude). ''' - from music21 import roman - k = key.Key('c') for pitchNames, expectedFigure, expectedRN in [ (('A-4', 'C5', 'E-5'), 'bVI', 'VI'), @@ -4545,16 +4536,16 @@ def testRomanNumeralFromChordRaised67(self): ]: with self.subTest(pitches=pitchNames): c = chord.Chord(pitchNames) - rn = roman.romanNumeralFromChord(c, k) + rn = romanNumeralFromChord(c, k) self.assertEqual(rn.figure, expectedFigure) self.assertEqual(rn.romanNumeral, expectedRN) # the figure must round-trip to the same pitch names under # the convention that romanNumeralFromChord itself uses. - roundTrip = roman.RomanNumeral( + roundTrip = RomanNumeral( rn.figure, k, - sixthMinor=roman.Minor67Default.CAUTIONARY, - seventhMinor=roman.Minor67Default.CAUTIONARY, + sixthMinor=Minor67Default.CAUTIONARY, + seventhMinor=Minor67Default.CAUTIONARY, ) self.assertEqual( [p_.name for p_ in roundTrip.pitches], @@ -4562,8 +4553,6 @@ def testRomanNumeralFromChordRaised67(self): ) def testAugmented(self): - from music21 import roman - def p(c): return ' '.join([x.nameWithOctave for x in c.pitches]) @@ -4573,7 +4562,7 @@ def test_numeral(country, figure_list, result, key_in='a'): for kStr in (key_in, key_in.upper()): key_obj = key.Key(kStr) rn_str = country + with_plus + figure - rn = roman.RomanNumeral(rn_str, key_obj) + rn = RomanNumeral(rn_str, key_obj) self.assertEqual(p(rn), result) @@ -4640,14 +4629,13 @@ def testSetFigureAgain(self): self.assertEqual(sharp_four.pitches, pitches_before) def testZeroForDiminished(self): - from music21 import roman - rn = roman.RomanNumeral('vii07', 'c') + rn = RomanNumeral('vii07', 'c') self.assertEqual([p.name for p in rn.pitches], ['B', 'D', 'F', 'A-']) - rn = roman.RomanNumeral('vii/07', 'c') + rn = RomanNumeral('vii/07', 'c') self.assertEqual([p.name for p in rn.pitches], ['B', 'D', 'F', 'A']) # However, when there is a '10' somewhere in the figure, don't replace # the 0 (this occurs in DCML corpora) - rn = roman.RomanNumeral('V7[add10]', 'c') + rn = RomanNumeral('V7[add10]', 'c') self.assertEqual([p.name for p in rn.pitches], ['G', 'B-', 'B', 'D', 'F']) def testIII7(self): diff --git a/music21/romanText/clercqTemperley.py b/music21/romanText/clercqTemperley.py index e3af1d3b3e..1fa06a92df 100644 --- a/music21/romanText/clercqTemperley.py +++ b/music21/romanText/clercqTemperley.py @@ -1141,8 +1141,7 @@ class TestExternal(unittest.TestCase): show = True def testB(self) -> None: - from music21.romanText import clercqTemperley - s = clercqTemperley.CTSong(BlitzkriegBopCT) + s = CTSong(BlitzkriegBopCT) partObj = s.toPart() if self.show: partObj.show() diff --git a/music21/romanText/translate.py b/music21/romanText/translate.py index e029534819..0add263098 100644 --- a/music21/romanText/translate.py +++ b/music21/romanText/translate.py @@ -1575,7 +1575,6 @@ def testNoChord(self): def testUnprocessed(self): from music21 import converter - from music21.romanText import translate src = '''Note: Hello m1 G: IV || b3 d: III b4 NC varM1 I @@ -1583,7 +1582,7 @@ def testUnprocessed(self): ''' s = converter.parse(src, format='romantext') p = s.parts[0] - unprocessedElements = p[translate.RomanTextUnprocessedMetadata] + unprocessedElements = p[RomanTextUnprocessedMetadata] self.assertEqual(len(unprocessedElements), 3) note1, var1, note2 = unprocessedElements self.assertEqual(note1.tag, 'Note') @@ -1594,19 +1593,18 @@ def testUnprocessed(self): self.assertIn(' I', var1.data) def testUnprocessedWithAnacrusis(self): - from music21.romanText import translate src = ''' Time Signature: 4/4 m0 b4 f: i Note: Internal Note field after anacrusis. m1 V ''' - s = translate.romanTextToStreamScore(src) + s = romanTextToStreamScore(src) p = s.parts[0] self.assertEqual(len(p), 3) self.assertIsInstance(p[0], stream.Measure) self.assertEqual(p[0].paddingLeft, 3.0) - self.assertIsInstance(p[1], translate.RomanTextUnprocessedMetadata) + self.assertIsInstance(p[1], RomanTextUnprocessedMetadata) self.assertEqual(p[1].offset, 0.0) self.assertEqual(p[1].data, 'Internal Note field after anacrusis.') self.assertIsInstance(p[2], stream.Measure) diff --git a/music21/sieve.py b/music21/sieve.py index 8a35fa31e7..b999fa5fc0 100644 --- a/music21/sieve.py +++ b/music21/sieve.py @@ -2097,22 +2097,18 @@ def testSieve(self): '{-{13@3|13@5|13@7|13@9}&11@2}|{-{11@4|11@8}&13@9}|{13@0|13@1|13@6}') def testPitchSieveA(self): - from music21 import sieve - - s1 = sieve.PitchSieve('3@0|7@0', 'c2', 'c6') + s1 = PitchSieve('3@0|7@0', 'c2', 'c6') self.assertEqual(self.pitchOut(s1()), '[C2, E-2, F#2, G2, A2, C3, D3, E-3, F#3, A3, C4, E-4, ' 'E4, F#4, A4, B4, C5, E-5, F#5, A5, C6]') - s1 = sieve.PitchSieve('3@0|7@0', 'c2', 'c6', eld=2) + s1 = PitchSieve('3@0|7@0', 'c2', 'c6', eld=2) self.assertEqual(self.pitchOut(s1()), '[C2, D2, F#2, C3, E3, F#3, C4, F#4, C5, F#5, G#5, C6]') def testPitchSieveB(self): - from music21 import sieve - # microtonal elds - s1 = sieve.PitchSieve('1@0', 'c2', 'c6', eld=0.5) + s1 = PitchSieve('1@0', 'c2', 'c6', eld=0.5) self.assertEqual(self.pitchOut(s1()), '[C2, C~2, C#2, C#~2, D2, D~2, E-2, E`2, E2, E~2, F2, F~2, F#2, ' 'F#~2, G2, G~2, G#2, G#~2, A2, A~2, B-2, B`2, B2, B~2, C3, C~3, C#3, ' @@ -2122,7 +2118,7 @@ def testPitchSieveB(self): 'B`4, B4, B~4, C5, C~5, C#5, C#~5, D5, D~5, E-5, E`5, E5, E~5, F5, F~5, ' 'F#5, F#~5, G5, G~5, G#5, G#~5, A5, A~5, B-5, B`5, B5, B~5, C6]') - s1 = sieve.PitchSieve('3@0', 'c2', 'c6', eld=0.5) + s1 = PitchSieve('3@0', 'c2', 'c6', eld=0.5) self.assertEqual(self.pitchOut(s1()), '[C2, C#~2, E-2, E~2, F#2, G~2, A2, B`2, C3, C#~3, E-3, E~3, F#3, G~3, ' 'A3, B`3, C4, C#~4, E-4, E~4, F#4, G~4, A4, B`4, C5, C#~5, E-5, E~5, F#5, ' diff --git a/music21/spanner.py b/music21/spanner.py index 681f7e03c0..6a04bd498e 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -2404,10 +2404,9 @@ def testBasic(self): def testSpannerAnchorRepr(self): from music21 import stream - from music21 import spanner # SpannerAnchor with no activeSite - sa1 = spanner.SpannerAnchor() + sa1 = SpannerAnchor() self.assertEqual(repr(sa1), '') # SpannerAnchor with activeSite, but no duration @@ -2420,19 +2419,17 @@ def testSpannerAnchorRepr(self): self.assertEqual(repr(sa1), '') def testSpannerRepr(self): - from music21 import spanner - su1 = spanner.Slur() + su1 = Slur() self.assertEqual(repr(su1), '') def testSpannerFill(self): from music21 import stream from music21 import note - from music21 import spanner theNotes = [note.Note('A'), note.Note('B'), note.Note('C'), note.Note('D')] m = stream.Measure(theNotes) # Spanner with no fillElementTypes - sp = spanner.Spanner(theNotes[0], theNotes[3]) + sp = Spanner(theNotes[0], theNotes[3]) sp.fill(m) # should not have done anything noFillElements = [theNotes[0], theNotes[3]] @@ -2441,7 +2438,7 @@ def testSpannerFill(self): self.assertIs(el, noFillElements[i]) # Ottava with filledStatus == True - ott1 = spanner.Ottava(noFillElements) + ott1 = Ottava(noFillElements) ott1.filledStatus = True # pretend it has already been filled ott1.fill(m) # should not have done anything @@ -2459,20 +2456,20 @@ def testSpannerFill(self): self.assertIs(el, theNotes[i]) # Ottava with no elements - ott2 = spanner.Ottava() + ott2 = Ottava() ott2.fill(m) self.assertEqual(len(ott2), 0) # Ottava with only element not in searchStream expectedElements = [note.Note('E')] - ott3 = spanner.Ottava(expectedElements) + ott3 = Ottava(expectedElements) ott3.fill(m) self.assertEqual(len(ott3), 1) self.assertIs(ott3.getFirst(), expectedElements[0]) # Ottava with start element not in searchStream, end element is expectedElements = [note.Note('F'), m.notes[0]] - ott4 = spanner.Ottava(expectedElements) + ott4 = Ottava(expectedElements) ott4.fill(m) self.assertEqual(len(ott4), 2) for i, el in enumerate(ott4.getSpannedElements()): @@ -2480,40 +2477,38 @@ def testSpannerFill(self): # Ottava with endElement not in searchStream, startElement is expectedElements = [m.notes[0], note.Note('G')] - ott5 = spanner.Ottava(expectedElements) + ott5 = Ottava(expectedElements) ott5.fill(m) self.assertEqual(len(ott5), 2) for i, el in enumerate(ott5.getSpannedElements()): self.assertIs(el, expectedElements[i]) def testSpannerBundle(self): - from music21 import spanner from music21 import stream - su1 = spanner.Slur() + su1 = Slur() su1.idLocal = 1 - su2 = spanner.Slur() + su2 = Slur() su2.idLocal = 2 - sb = spanner.SpannerBundle() + sb = SpannerBundle() sb.append(su1) sb.append(su2) self.assertEqual(len(sb), 2) self.assertEqual(sb[0], su1) self.assertEqual(sb[1], su2) - su3 = spanner.Slur() - su4 = spanner.Slur() + su3 = Slur() + su4 = Slur() s = stream.Stream() s.append(su3) s.append(su4) - sb2 = spanner.SpannerBundle(list(s)) + sb2 = SpannerBundle(list(s)) self.assertEqual(len(sb2), 2) self.assertEqual(sb2[0], su3) self.assertEqual(sb2[1], su4) def testDeepcopySpanner(self): - from music21 import spanner from music21 import note # how slurs might be defined @@ -2535,7 +2530,7 @@ def testDeepcopySpanner(self): self.assertEqual(n1.getSpannerSites(), [su1, su2]) self.assertEqual(n3.getSpannerSites(), [su1, su2]) - sb1 = spanner.SpannerBundle([su1, su2]) + sb1 = SpannerBundle([su1, su2]) sb2 = copy.deepcopy(sb1) self.assertEqual(sb1[0].getSpannedElements(), [n1, n3]) self.assertEqual(sb2[0].getSpannedElements(), [n1, n3]) @@ -2544,7 +2539,6 @@ def testDeepcopySpanner(self): def testReplaceSpannedElement(self): from music21 import note - from music21 import spanner n1 = note.Note() n2 = note.Note() @@ -2552,7 +2546,7 @@ def testReplaceSpannedElement(self): n4 = note.Note() n5 = note.Note() - su1 = spanner.Slur() + su1 = Slur() su1.addSpannedElements([n1, n3]) self.assertEqual(su1.getSpannedElements(), [n1, n3]) @@ -2570,10 +2564,10 @@ def testReplaceSpannedElement(self): self.assertEqual(n2.getSpannerSites(), []) self.assertEqual(n1.getSpannerSites(), [su1]) - su2 = spanner.Slur() + su2 = Slur() su2.addSpannedElements([n3, n4]) - su3 = spanner.Slur() + su3 = Slur() su3.addSpannedElements([n4, n5]) # n1a = note.Note() @@ -2582,7 +2576,7 @@ def testReplaceSpannedElement(self): n4a = note.Note() # n5a = note.Note() - sb1 = spanner.SpannerBundle([su1, su2, su3]) + sb1 = SpannerBundle([su1, su2, su3]) self.assertEqual(len(sb1), 3) self.assertEqual(list(sb1), [su1, su2, su3]) @@ -2603,18 +2597,16 @@ def testReplaceSpannedElement(self): self.assertEqual(sb1[2].getSpannedElements(), [n4a, n5]) def testRepeatBracketA(self): - from music21 import spanner from music21 import stream m1 = stream.Measure() - rb1 = spanner.RepeatBracket(m1) + rb1 = RepeatBracket(m1) # if added again; it is not really added, it simply is ignored rb1.addSpannedElements(m1) self.assertEqual(len(rb1), 1) def testRepeatBracketB(self): from music21 import note - from music21 import spanner from music21 import stream from music21 import bar @@ -2630,19 +2622,19 @@ def testRepeatBracketB(self): m3.repeatAppend(note.Note('g#4'), 4) m3.rightBarline = bar.Repeat(direction='end') p.append(m3) - p.append(spanner.RepeatBracket(m3, number=1)) + p.append(RepeatBracket(m3, number=1)) m4 = stream.Measure() m4.repeatAppend(note.Note('a4'), 4) m4.rightBarline = bar.Repeat(direction='end') p.append(m4) - p.append(spanner.RepeatBracket(m4, number=2)) + p.append(RepeatBracket(m4, number=2)) m5 = stream.Measure() m5.repeatAppend(note.Note('b4'), 4) m5.rightBarline = bar.Repeat(direction='end') p.append(m5) - p.append(spanner.RepeatBracket(m5, number=3)) + p.append(RepeatBracket(m5, number=3)) m6 = stream.Measure() m6.repeatAppend(note.Note('c#5'), 4) @@ -2654,7 +2646,6 @@ def testRepeatBracketB(self): # noinspection DuplicatedCode def testRepeatBracketC(self): from music21 import note - from music21 import spanner from music21 import stream from music21 import bar @@ -2671,7 +2662,7 @@ def testRepeatBracketC(self): m3.repeatAppend(note.Note('g#4'), 4) m3.rightBarline = bar.Repeat(direction='end') p.append(m3) - rb1 = spanner.RepeatBracket(number=1) + rb1 = RepeatBracket(number=1) rb1.addSpannedElements(m2, m3) self.assertEqual(len(rb1), 2) p.insert(0, rb1) @@ -2680,7 +2671,7 @@ def testRepeatBracketC(self): m4.repeatAppend(note.Note('a4'), 4) m4.rightBarline = bar.Repeat(direction='end') p.append(m4) - p.append(spanner.RepeatBracket(m4, number=2)) + p.append(RepeatBracket(m4, number=2)) m5 = stream.Measure() m5.repeatAppend(note.Note('b4'), 4) @@ -2699,7 +2690,6 @@ def testRepeatBracketC(self): # noinspection DuplicatedCode def testRepeatBracketD(self): from music21 import note - from music21 import spanner from music21 import stream from music21 import bar @@ -2716,7 +2706,7 @@ def testRepeatBracketD(self): m3.repeatAppend(note.Note('g#4'), 4) m3.rightBarline = bar.Repeat(direction='end') p.append(m3) - rb1 = spanner.RepeatBracket(number=1) + rb1 = RepeatBracket(number=1) rb1.addSpannedElements(m2, m3) self.assertEqual(len(rb1), 2) p.insert(0, rb1) @@ -2730,7 +2720,7 @@ def testRepeatBracketD(self): m5.rightBarline = bar.Repeat(direction='end') p.append(m5) - rb2 = spanner.RepeatBracket(number=2) + rb2 = RepeatBracket(number=2) rb2.addSpannedElements(m4, m5) self.assertEqual(len(rb2), 2) p.insert(0, rb2) @@ -2748,7 +2738,7 @@ def testRepeatBracketD(self): m8.rightBarline = bar.Repeat(direction='end') p.append(m8) - rb3 = spanner.RepeatBracket(number=3) + rb3 = RepeatBracket(number=3) rb3.addSpannedElements(m6, m8) self.assertEqual(len(rb3), 2) p.insert(0, rb3) @@ -2770,7 +2760,7 @@ def testRepeatBracketD(self): m12.rightBarline = bar.Repeat(direction='end') p.append(m12) - rb4 = spanner.RepeatBracket(number=4) + rb4 = RepeatBracket(number=4) rb4.addSpannedElements(m9, m10, m11, m12) self.assertEqual(len(rb4), 4) p.insert(0, rb4) @@ -2799,7 +2789,6 @@ def testRepeatBracketD(self): def testRepeatBracketE(self): from music21 import note - from music21 import spanner from music21 import stream from music21 import bar @@ -2815,19 +2804,19 @@ def testRepeatBracketE(self): m3.repeatAppend(note.Note('g#4'), 1) m3.rightBarline = bar.Repeat(direction='end') p.append(m3) - p.append(spanner.RepeatBracket(m3, number=1)) + p.append(RepeatBracket(m3, number=1)) m4 = stream.Measure(number=4) m4.repeatAppend(note.Note('a4'), 1) m4.rightBarline = bar.Repeat(direction='end') p.append(m4) - p.append(spanner.RepeatBracket(m4, number=2)) + p.append(RepeatBracket(m4, number=2)) m5 = stream.Measure(number=5) m5.repeatAppend(note.Note('b4'), 1) m5.rightBarline = bar.Repeat(direction='end') p.append(m5) - p.append(spanner.RepeatBracket(m5, number=3)) + p.append(RepeatBracket(m5, number=3)) m6 = stream.Measure(number=6) m6.repeatAppend(note.Note('c#5'), 1) @@ -2868,7 +2857,6 @@ def testOttavaShiftA(self): from music21 import stream from music21 import note from music21 import chord - from music21.spanner import Ottava # need to do it this way for classSet s = stream.Stream() s.repeatAppend(chord.Chord(['c-3', 'g4']), 12) # s.repeatAppend(note.Note(), 12) @@ -2920,10 +2908,9 @@ def testOttavaShiftB(self): ''' from music21 import stream from music21 import note - from music21 import spanner s = stream.Stream() n = note.Note('c4') - sp = spanner.Ottava(n) + sp = Ottava(n) s.append(n) s.append(sp) # s.show() @@ -2972,15 +2959,14 @@ def testCrescendoA(self): def testLineA(self): from music21 import stream from music21 import note - from music21 import spanner s = stream.Stream() s.repeatAppend(note.Note(), 12) n1 = s.notes[0] n2 = s.notes[len(s.notes) // 2] n3 = s.notes[-1] - sp1 = spanner.Line(n1, n2, startTick='up', lineType='dotted') - sp2 = spanner.Line(n2, n3, startTick='down', lineType='dashed', + sp1 = Line(n1, n2, startTick='up', lineType='dotted') + sp2 = Line(n2, n3, startTick='down', lineType='dashed', endHeight=40) s.append(sp1) s.append(sp2) @@ -2992,7 +2978,6 @@ def testLineA(self): def testLineB(self): from music21 import stream from music21 import note - from music21 import spanner s = stream.Stream() s.repeatAppend(note.Note(), 12) @@ -3002,8 +2987,8 @@ def testLineB(self): n3 = s.notes[0] n4 = s.notes[2] - sp1 = spanner.Line(n1, n2, startTick='up', endTick='down', lineType='solid') - sp2 = spanner.Line(n3, n4, startTick='arrow', endTick='none', lineType='solid') + sp1 = Line(n1, n2, startTick='up', endTick='down', lineType='solid') + sp2 = Line(n3, n4, startTick='arrow', endTick='none', lineType='solid') s.append(sp1) s.append(sp2) @@ -3019,7 +3004,6 @@ def testLineB(self): def testGlissandoA(self): from music21 import stream from music21 import note - from music21 import spanner s = stream.Stream() s.repeatAppend(note.Note(), 3) @@ -3030,8 +3014,8 @@ def testGlissandoA(self): n1 = s.notes[0] n2 = s.notes[len(s.notes) // 2] n3 = s.notes[-1] - sp1 = spanner.Glissando(n1, n2) - sp2 = spanner.Glissando(n2, n3) + sp1 = Glissando(n1, n2) + sp2 = Glissando(n2, n3) sp2.lineType = 'dashed' s.append(sp1) s.append(sp2) @@ -3045,7 +3029,6 @@ def testGlissandoA(self): def testGlissandoB(self): from music21 import stream from music21 import note - from music21 import spanner s = stream.Stream() s.repeatAppend(note.Note(), 12) @@ -3055,7 +3038,7 @@ def testGlissandoB(self): # note: this does not support glissandi between non-adjacent notes n1 = s.notes[0] n2 = s.notes[1] - sp1 = spanner.Glissando(n1, n2) + sp1 = Glissando(n1, n2) sp1.lineType = 'solid' sp1.label = 'gliss.' s.append(sp1) @@ -3086,7 +3069,6 @@ def testGlissandoB(self): def testOneElementSpanners(self): from music21 import note - from music21.spanner import Spanner n1 = note.Note() sp = Spanner() @@ -3099,7 +3081,6 @@ def testOneElementSpanners(self): def testRemoveSpanners(self): from music21 import stream from music21 import note - from music21.spanner import Spanner, Slur p = stream.Part() m1 = stream.Measure() @@ -3123,7 +3104,6 @@ def testFreezeSpanners(self): from music21 import stream from music21 import note from music21 import converter - from music21.spanner import Slur p = stream.Part() m1 = stream.Measure() @@ -3143,7 +3123,6 @@ def testFreezeSpanners(self): def testDeepcopyJustSpannerAndNotes(self): from music21 import note from music21 import clef - from music21.spanner import Spanner n1 = note.Note('g') n2 = note.Note('f#') @@ -3162,7 +3141,6 @@ def testDeepcopySpannerInStreamNotNotes(self): from music21 import note from music21 import clef from music21 import stream - from music21.spanner import Spanner n1 = note.Note('g') n2 = note.Note('f#') @@ -3185,7 +3163,6 @@ def testDeepcopyNotesInStreamNotSpanner(self): from music21 import note from music21 import clef from music21 import stream - from music21.spanner import Spanner n1 = note.Note('g') n2 = note.Note('f#') @@ -3209,7 +3186,6 @@ def testDeepcopyNotesInStreamNotSpanner(self): def testDeepcopyNotesAndSpannerInStream(self): from music21 import note from music21 import stream - from music21.spanner import Spanner n1 = note.Note('G4') n2 = note.Note('F#4') @@ -3234,7 +3210,6 @@ def testDeepcopyNotesAndSpannerInStream(self): def testDeepcopyStreamWithSpanners(self): from music21 import note from music21 import stream - from music21.spanner import Slur n1 = note.Note() su1 = Slur((n1,)) @@ -3258,7 +3233,6 @@ def testDeepcopyStreamWithSpanners(self): def testGetSpannedElementIds(self): from music21 import note - from music21.spanner import Spanner n1 = note.Note('g') n2 = note.Note('f#') diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py index 95118c114a..3686214b23 100644 --- a/music21/stream/iterator.py +++ b/music21/stream/iterator.py @@ -2132,8 +2132,6 @@ def testCurrentHierarchyOffsetReset(self): def testAddingFiltersMidRecursiveIteration(self): from music21 import stream - # noinspection PyUnresolvedReferences - from music21.stream.iterator import RecursiveIterator as ImportedRecursiveIterator m = stream.Measure() r = note.Rest() n = note.Note() @@ -2149,7 +2147,7 @@ def testAddingFiltersMidRecursiveIteration(self): self.assertIs(p0, p) child = sIter.childRecursiveIterator - self.assertIsInstance(child, ImportedRecursiveIterator) + self.assertIsInstance(child, RecursiveIterator) diff --git a/music21/tablature.py b/music21/tablature.py index cf529a6d82..2638e87711 100644 --- a/music21/tablature.py +++ b/music21/tablature.py @@ -363,8 +363,7 @@ def testStupidFretNote(self): self.assertEqual(FretNote().string, None) def testFretNoteWeirdRepr(self): - from music21 import tablature - weirdFretNote = tablature.FretNote(6, 133) + weirdFretNote = FretNote(6, 133) expectedRepr = '' diff --git a/music21/tempo.py b/music21/tempo.py index 6eb003b836..9cf0bb0bd5 100644 --- a/music21/tempo.py +++ b/music21/tempo.py @@ -1370,8 +1370,7 @@ def testUnicode(self): self.assertEqual(mm.number, 144) def testTempoTextStyle(self): - from music21 import tempo - tm = tempo.TempoText('adagio') + tm = TempoText('adagio') self.assertEqual(tm.style.absoluteY, 45) self.assertEqual(tm.style.fontStyle, 'bold') tm.style.absoluteY = 33 @@ -1435,8 +1434,7 @@ def testTempoTextStyle(self): self.assertIs(tm.style, te4.style) # check for linked styles def testMetronomeMarkA(self): - from music21 import tempo - mm = tempo.MetronomeMark() + mm = MetronomeMark() mm.number = 56 # should implicitly set text self.assertEqual(mm.text, 'adagio') self.assertTrue(mm.textImplicit) @@ -1447,7 +1445,7 @@ def testMetronomeMarkA(self): self.assertEqual(mm.referent.quarterLength, 1.0) # setting the text first - mm = tempo.MetronomeMark() + mm = MetronomeMark() mm.text = 'presto' mm.referent = duration.Duration(3.0) self.assertEqual(mm.text, 'presto') @@ -1480,13 +1478,12 @@ def testMetronomeMarkB(self): self.assertFalse(mm.textImplicit) def testMetronomeModulationA(self): - from music21 import tempo # need to create a mm without a speed # want to say that an eighth is becoming the speed of a sixteenth - mm1 = tempo.MetronomeMark(referent=0.5, number=120) - mm2 = tempo.MetronomeMark(referent='16th') + mm1 = MetronomeMark(referent=0.5, number=120) + mm2 = MetronomeMark(referent='16th') - mmod1 = tempo.MetricModulation() + mmod1 = MetricModulation() mmod1.oldMetronome = mm1 mmod1.newMetronome = mm2 @@ -1497,8 +1494,8 @@ def testMetronomeModulationA(self): + '>') # we can get the same result by using setEqualityByReferent() - mm1 = tempo.MetronomeMark(referent=0.5, number=120) - mmod1 = tempo.MetricModulation() + mm1 = MetronomeMark(referent=0.5, number=120) + mmod1 = MetricModulation() mmod1.oldMetronome = mm1 # will automatically set right mm, as presently is None mmod1.setOtherByReferent(referent='16th') @@ -1512,7 +1509,6 @@ def testMetronomeModulationA(self): self.assertEqual(mmod1.newMetronome.getQuarterBPM(), 30.0) def testGetPreviousMetronomeMarkA(self): - from music21 import tempo from music21 import stream # test getting basic metronome marks @@ -1520,9 +1516,9 @@ def testGetPreviousMetronomeMarkA(self): m1 = stream.Measure() m1.repeatAppend(note.Note(quarterLength=1), 4) m2 = copy.deepcopy(m1) - mm1 = tempo.MetronomeMark(number=56, referent=0.25) + mm1 = MetronomeMark(number=56, referent=0.25) m1.insert(0, mm1) - mm2 = tempo.MetronomeMark(number=150, referent=0.5) + mm2 = MetronomeMark(number=150, referent=0.5) m2.insert(0, mm2) p.append([m1, m2]) self.assertEqual(str(mm2.getPreviousMetronomeMark()), @@ -1530,7 +1526,6 @@ def testGetPreviousMetronomeMarkA(self): # p.show() def testGetPreviousMetronomeMarkB(self): - from music21 import tempo from music21 import stream # test using a tempo text, will return a default metronome mark if possible @@ -1538,9 +1533,9 @@ def testGetPreviousMetronomeMarkB(self): m1 = stream.Measure() m1.repeatAppend(note.Note(quarterLength=1), 4) m2 = copy.deepcopy(m1) - mm1 = tempo.TempoText('slow') + mm1 = TempoText('slow') m1.insert(0, mm1) - mm2 = tempo.MetronomeMark(number=150, referent=0.5) + mm2 = MetronomeMark(number=150, referent=0.5) m2.insert(0, mm2) p.append([m1, m2]) self.assertEqual(str(mm2.getPreviousMetronomeMark()), @@ -1548,7 +1543,6 @@ def testGetPreviousMetronomeMarkB(self): # p.show() def testGetPreviousMetronomeMarkC(self): - from music21 import tempo from music21 import stream # test using a metric modulation @@ -1558,15 +1552,15 @@ def testGetPreviousMetronomeMarkC(self): m2 = copy.deepcopy(m1) m3 = copy.deepcopy(m2) - mm1 = tempo.MetronomeMark('slow') + mm1 = MetronomeMark('slow') m1.insert(0, mm1) - mm2 = tempo.MetricModulation() - mm2.oldMetronome = tempo.MetronomeMark(referent=1, number=52) + mm2 = MetricModulation() + mm2.oldMetronome = MetronomeMark(referent=1, number=52) mm2.setOtherByReferent(referent='16th') m2.insert(0, mm2) - mm3 = tempo.MetronomeMark(number=150, referent=0.5) + mm3 = MetronomeMark(number=150, referent=0.5) m3.insert(0, mm3) p.append([m1, m2, m3]) @@ -1580,17 +1574,16 @@ def testSetReferentA(self): Test setting referents directly via context searches. ''' from music21 import stream - from music21 import tempo p = stream.Part() m1 = stream.Measure() m1.repeatAppend(note.Note(quarterLength=1), 4) m2 = copy.deepcopy(m1) m3 = copy.deepcopy(m2) - mm1 = tempo.MetronomeMark(number=92) + mm1 = MetronomeMark(number=92) m1.insert(0, mm1) - mm2 = tempo.MetricModulation() + mm2 = MetricModulation() m2.insert(0, mm2) p.append([m1, m2, m3]) @@ -1604,15 +1597,14 @@ def testSetReferentA(self): # p.show() def testSetReferentB(self): - from music21 import tempo from music21 import stream s = stream.Stream() - mm1 = tempo.MetronomeMark(number=60) + mm1 = MetronomeMark(number=60) s.append(mm1) s.repeatAppend(note.Note(quarterLength=1), 2) s.repeatAppend(note.Note(quarterLength=0.5), 4) - mmod1 = tempo.MetricModulation() + mmod1 = MetricModulation() mmod1.oldReferent = 0.5 # can use Duration objects mmod1.newReferent = 'quarter' # can use Duration objects s.append(mmod1) @@ -1626,7 +1618,7 @@ def testSetReferentB(self): s.append(note.Note()) s.repeatAppend(note.Note(quarterLength=1.5), 2) - mmod2 = tempo.MetricModulation() + mmod2 = MetricModulation() mmod2.oldReferent = 1.5 mmod2.newReferent = 'quarter' # can use Duration objects s.append(mmod2) @@ -1640,15 +1632,14 @@ def testSetReferentB(self): # s.show() def testSetReferentC(self): - from music21 import tempo from music21 import stream s = stream.Stream() - mm1 = tempo.MetronomeMark(number=60) + mm1 = MetronomeMark(number=60) s.append(mm1) s.repeatAppend(note.Note(quarterLength=1), 2) s.repeatAppend(note.Note(quarterLength=0.5), 4) - mmod1 = tempo.MetricModulation() + mmod1 = MetricModulation() s.append(mmod1) mmod1.oldReferent = 0.5 # can use Duration objects mmod1.newReferent = 'quarter' # can use Duration objects @@ -1661,7 +1652,7 @@ def testSetReferentC(self): s.append(note.Note()) s.repeatAppend(note.Note(quarterLength=1.5), 2) - mmod2 = tempo.MetricModulation() + mmod2 = MetricModulation() s.append(mmod2) mmod2.oldReferent = 1.5 mmod2.newReferent = 'quarter' # can use Duration objects @@ -1674,15 +1665,14 @@ def testSetReferentC(self): # s.show() def testSetReferentD(self): - from music21 import tempo from music21 import stream s = stream.Stream() - mm1 = tempo.MetronomeMark(number=60) + mm1 = MetronomeMark(number=60) s.append(mm1) s.repeatAppend(note.Note(quarterLength=1), 2) s.repeatAppend(note.Note(quarterLength=0.5), 4) - mmod1 = tempo.MetricModulation() + mmod1 = MetricModulation() s.append(mmod1) # even with we have no assigned metronome, update context will create mmod1.updateByContext() diff --git a/music21/variant.py b/music21/variant.py index 17e2184e32..f365d25af8 100644 --- a/music21/variant.py +++ b/music21/variant.py @@ -2530,8 +2530,7 @@ def makeVariantBlocks(s): ''' Unknown and undocumented. Used only in lily/translate -- for musicdiff. ''' - from music21 import variant - variantsToBeDone = s.getElementsByClass(variant.Variant) + variantsToBeDone = s.getElementsByClass(Variant) for v in variantsToBeDone: startOffset = s.elementOffset(v) @@ -2541,7 +2540,7 @@ def makeVariantBlocks(s): includeEndBoundary=False, mustFinishInSpan=False, mustBeginInSpan=True, - classList=[variant.Variant]) + classList=[Variant]) for cV in conflictingVariants: oldReplacementDuration = cV.replacementQuarterLength if s.elementOffset(cV) == startOffset: @@ -2633,14 +2632,12 @@ def testVariantClassA(self): self.assertTrue(v1.getElementsByClass(stream.Measure)) def testDeepCopyVariantA(self): - from music21 import variant - s = stream.Stream() s.repeatAppend(note.Note('G4'), 8) vn1 = note.Note('F#4') vn2 = note.Note('A-4') - v1 = variant.Variant() + v1 = Variant() v1.insert(0, vn1) v1.insert(0, vn2) v1Copy = copy.deepcopy(v1) @@ -2660,7 +2657,7 @@ def testDeepCopyVariantA(self): # test functionality on a deepcopy sCopy = copy.deepcopy(s) - self.assertEqual(len(sCopy.getElementsByClass(variant.Variant)), 1) + self.assertEqual(len(sCopy.getElementsByClass(Variant)), 1) self.assertEqual(self.pitchOut(sCopy.pitches), '[G4, G4, G4, G4, G4, G4, G4, G4]') sCopy.activateVariants(inPlace=True) @@ -2668,13 +2665,11 @@ def testDeepCopyVariantA(self): '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') def testDeepCopyVariantB(self): - from music21 import variant - s = stream.Stream() s.repeatAppend(note.Note('G4'), 8) vn1 = note.Note('F#4') vn2 = note.Note('A-4') - v1 = variant.Variant() + v1 = Variant() v1.insert(0, vn1) v1.insert(0, vn2) s.insert(5, v1) diff --git a/music21/volume.py b/music21/volume.py index 1b798c7efa..6b08dcd516 100644 --- a/music21/volume.py +++ b/music21/volume.py @@ -508,10 +508,9 @@ class Test(unittest.TestCase): def testBasic(self): import gc - from music21 import volume n1 = note.Note('G#4') - v = volume.Volume(client=n1) + v = Volume(client=n1) self.assertEqual(v.client, n1) del n1 gc.collect() @@ -521,7 +520,6 @@ def testBasic(self): def testGetContextSearchA(self): from music21 import stream - from music21 import volume s = stream.Stream() d1 = dynamics.Dynamic('mf') @@ -530,7 +528,7 @@ def testGetContextSearchA(self): s.insert(2, d2) n1 = note.Note('g') - v1 = volume.Volume(client=n1) + v1 = Volume(client=n1) s.insert(4, n1) # can get dynamics from volume object @@ -556,10 +554,9 @@ def testGetContextSearchB(self): def testDeepCopyA(self): import copy - from music21 import volume n1 = note.Note() - v1 = volume.Volume() + v1 = Volume() v1.velocity = 111 v1.client = n1 @@ -572,9 +569,7 @@ def testDeepCopyA(self): def testGetRealizedA(self): - from music21 import volume - - v1 = volume.Volume(velocity=64) + v1 = Volume(velocity=64) self.assertEqual(v1.getRealizedStr(), '0.5') d1 = dynamics.Dynamic('p') @@ -589,7 +584,7 @@ def testGetRealizedA(self): # if vel is at max, can scale down with a dynamic - v1 = volume.Volume(velocity=127) + v1 = Volume(velocity=127) d1 = dynamics.Dynamic('fff') self.assertEqual(v1.getRealizedStr(useDynamicContext=d1), '1.0') @@ -620,7 +615,6 @@ def testGetRealizedB(self): def testRealizeVolumeA(self): from music21 import stream - from music21 import volume s = stream.Stream() s.repeatAppend(note.Note('g3'), 16) @@ -637,7 +631,7 @@ def testRealizeVolumeA(self): self.assertEqual(match, ['0.71'] * 16) # calling realize will set all to new cached values - volume.realizeVolume(s) + realizeVolume(s) match = [n.volume.cachedRealizedStr for n in s.notes] self.assertEqual(match, ['0.35', '0.35', '0.5', '0.5', '0.64', '0.64', '0.99', '0.99', @@ -666,7 +660,7 @@ def testRealizeVolumeA(self): self.assertEqual(match, [None] * 16) # can set velocity with realized values - volume.realizeVolume(s, setAbsoluteVelocity=True) + realizeVolume(s, setAbsoluteVelocity=True) match = [n.volume.velocity for n in s.notes] self.assertEqual(match, [45, 45, 63, 63, 81, 81, 126, 126, 99, 99, 127, 127, 27, 27, 99, 99]) From e57c658fdd9b32b288a5e1f7123ccf1306a79ace Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 12:50:15 -1000 Subject: [PATCH 083/312] Remove the last self-references: clef's own module, two doctests clefFromString imported its own module to enumerate the clef classes by name; globals() is the same namespace without the import. The doctests in configure.Dialog._rawQueryPrepareHeader and tempo.TempoText imported music21 (or configure) before using it, which the pytest plugin already supplies -- it injects music21 and everything in its __all__ into the doctest namespace. base.py keeps its four `>>> import music21` lines, which are there to show the fully-qualified path rather than to make the example run. TempoText's docstring was a bare example with no prose; says now what the class is for. AI-assisted (Claude) --- music21/clef.py | 8 +++----- music21/configure.py | 1 - music21/tempo.py | 9 +++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/music21/clef.py b/music21/clef.py index 3a85fc7b5b..b01d0977c2 100644 --- a/music21/clef.py +++ b/music21/clef.py @@ -824,14 +824,12 @@ def clefFromString(clefString, octaveShift=0) -> Clef: else: lineNum = False elif len(xnStr) > 2: - from music21 import clef as myself xnLower = xnStr.lower() - for x in dir(myself): - if 'Clef' not in x: + for className, objType in globals().items(): + if 'Clef' not in className: continue - if xnLower != x.lower() and xnLower + 'clef' != x.lower(): + if className.lower() not in (xnLower, xnLower + 'clef'): continue - objType = getattr(myself, x) if isinstance(objType, type): return objType() diff --git a/music21/configure.py b/music21/configure.py index 024af6d3c4..772cee6ab7 100644 --- a/music21/configure.py +++ b/music21/configure.py @@ -364,7 +364,6 @@ def _rawQueryPrepareHeader(self, msg=''): ''' Prepare the header, given a string. - >>> from music21 import configure >>> d = configure.Dialog() >>> d._rawQueryPrepareHeader('test') 'test' diff --git a/music21/tempo.py b/music21/tempo.py index 9cf0bb0bd5..bba4a40a8e 100644 --- a/music21/tempo.py +++ b/music21/tempo.py @@ -181,8 +181,10 @@ def getPreviousMetronomeMark(self): # ------------------------------------------------------------------------------ class TempoText(TempoIndication): ''' - >>> import music21 - >>> tm = music21.tempo.TempoText('adagio') + TempoText is a TempoIndication that uses words (not metronome numbers) + to indicate tempo. + + >>> tm = tempo.TempoText('adagio') >>> tm >>> print(tm.text) @@ -206,8 +208,7 @@ def text(self): Get or set the text as a string. Setting is also the primary way that the stored TextExpression object is created. - >>> import music21 - >>> tm = music21.tempo.TempoText('adagio') + >>> tm = tempo.TempoText('adagio') >>> tm.text 'adagio' >>> tm.getTextExpression() From 303bcebd925ccd66ad1120c2f26be451a9852fce Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 12:52:18 -1000 Subject: [PATCH 084/312] Match clef classes by inheritance, not by name substring The name scan `if 'Clef' not in className` also matched ClefException, so clefFromString('clefexception') built and returned an exception object. Filter on issubclass(Clef) instead; every clef class in the module ends in 'Clef', so the clefs found are unchanged. AI-assisted (Claude) --- music21/clef.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/music21/clef.py b/music21/clef.py index b01d0977c2..4debb4b543 100644 --- a/music21/clef.py +++ b/music21/clef.py @@ -825,13 +825,11 @@ def clefFromString(clefString, octaveShift=0) -> Clef: lineNum = False elif len(xnStr) > 2: xnLower = xnStr.lower() - for className, objType in globals().items(): - if 'Clef' not in className: + for className, classObj in globals().items(): + if not isinstance(classObj, type) or not issubclass(classObj, Clef): continue - if className.lower() not in (xnLower, xnLower + 'clef'): - continue - if isinstance(objType, type): - return objType() + if className.lower() in (xnLower, xnLower + 'clef'): + return classObj() raise ClefException('Could not find clef ' + xnStr) else: From e673c59cda92c3bf095b35317a77aa81b34e253c Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 12:59:24 -1000 Subject: [PATCH 085/312] Bump clef.py's copyright; say when a sweep should not bump one clef.py is the only file in this branch whose change stands on its own -- the other twenty are a mechanical removal of dead imports, and bumping twenty copyright banners would bury the diff under them. AI-assisted (Claude) --- AGENTS.md | 2 ++ music21/clef.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index c61555b625..6b9738c6f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,6 +23,8 @@ - Imports: standard library first, then music21 modules, one per line, alphabetical. - New modules open with the `# Name: / # Purpose: / # Authors: / # Copyright: / # License:` banner (copy a neighboring module's), then the module docstring, then imports. Update the Copyright date end to current year when changing the module. + When a file is edited only incidentally, as one of many touched by a sweep, leave its + Copyright alone; bump only the files the change is really about. - No `print()`. Use `environLocal = environment.Environment('moduleName')` and `environLocal.printDebug(...)`, or `environLocal.warn(...)` when the user should hear about it every time. `test/toggleDebug.py` switches debug output on and off. diff --git a/music21/clef.py b/music21/clef.py index 4debb4b543..7ff0c6c80c 100644 --- a/music21/clef.py +++ b/music21/clef.py @@ -6,7 +6,7 @@ # Christopher Ariza # Michael Bodenbach # -# Copyright: Copyright © 2009-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' From 46500f5dca40f244f517018ca7ff54c8a8947aaf Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:11:20 -1000 Subject: [PATCH 086/312] Drop the blank lines sitting just inside docstring delimiters 314 docstrings ended with a blank line before their closing quotes, a few began with one after the opening quotes. Whitespace only: a doctest's expected output is already terminated by the end of the docstring, so the blank line was doing nothing. Covers docstrings proper plus the doc strings held in _DOC_ATTR dicts. AI-assisted (Claude) --- music21/abcFormat/__init__.py | 2 -- music21/alpha/analysis/aligner.py | 6 ------ music21/alpha/analysis/hasher.py | 1 - music21/analysis/correlate.py | 2 -- music21/analysis/discrete.py | 6 ------ music21/analysis/harmonicFunction.py | 1 - music21/analysis/metrical.py | 1 - music21/analysis/reduceChordsOld.py | 1 - music21/analysis/reduction.py | 1 - music21/analysis/windowed.py | 2 -- music21/articulations.py | 2 -- music21/audioSearch/scoreFollower.py | 3 --- music21/base.py | 3 --- music21/beam.py | 2 -- music21/braille/basic.py | 2 -- music21/braille/noteGrouping.py | 1 - music21/braille/segment.py | 1 - music21/braille/text.py | 1 - music21/braille/translate.py | 1 - music21/capella/fromCapellaXML.py | 1 - music21/chord/__init__.py | 13 ------------- music21/clef.py | 2 -- music21/common/classTools.py | 1 - music21/common/decorators.py | 1 - music21/common/formats.py | 1 - music21/common/numberTools.py | 1 - music21/configure.py | 1 - music21/converter/subConverters.py | 1 - music21/corpus/__init__.py | 2 -- music21/corpus/corpora.py | 4 ---- music21/corpus/manager.py | 1 - music21/corpus/virtual.py | 1 - music21/duration.py | 2 -- music21/dynamics.py | 1 - music21/editorial.py | 1 - music21/environment.py | 1 - music21/expressions.py | 6 ------ music21/features/base.py | 3 --- music21/features/jSymbolic.py | 25 ------------------------- music21/features/native.py | 3 --- music21/features/outputFormats.py | 2 -- music21/figuredBass/notation.py | 1 - music21/figuredBass/possibility.py | 1 - music21/figuredBass/realizer.py | 1 - music21/freezeThaw.py | 2 -- music21/graph/__init__.py | 1 - music21/graph/axis.py | 3 --- music21/graph/findPlot.py | 3 --- music21/graph/plot.py | 8 -------- music21/graph/primitives.py | 3 --- music21/harmony.py | 5 ----- music21/instrument.py | 4 ---- music21/interval.py | 3 --- music21/key.py | 1 - music21/lily/lilyObjects.py | 4 ---- music21/lily/translate.py | 13 ------------- music21/metadata/__init__.py | 4 ---- music21/metadata/bundles.py | 2 -- music21/metadata/caching.py | 1 - music21/metadata/primitives.py | 1 - music21/meter/base.py | 2 -- music21/meter/core.py | 5 ----- music21/midi/percussion.py | 1 - music21/midi/translate.py | 2 -- music21/musedata/__init__.py | 8 -------- music21/musedata/translate.py | 1 - music21/musicxml/m21ToXml.py | 11 ----------- music21/musicxml/xmlObjects.py | 3 --- music21/musicxml/xmlToM21.py | 2 -- music21/note.py | 1 - music21/noteworthy/binaryTranslate.py | 1 - music21/noteworthy/translate.py | 7 ------- music21/omr/correctors.py | 7 ------- music21/omr/evaluators.py | 2 -- music21/pitch.py | 5 ----- music21/repeat.py | 5 ----- music21/roman.py | 5 ----- music21/romanText/clercqTemperley.py | 2 -- music21/romanText/rtObjects.py | 2 -- music21/romanText/tsvConverter.py | 2 -- music21/romanText/writeRoman.py | 2 -- music21/scale/intervalNetwork.py | 4 ---- music21/search/base.py | 1 - music21/search/lyrics.py | 1 - music21/search/segment.py | 1 - music21/serial.py | 1 - music21/sieve.py | 4 ---- music21/sites.py | 3 --- music21/spanner.py | 2 -- music21/stream/base.py | 15 --------------- music21/stream/core.py | 1 - music21/stream/filters.py | 4 ---- music21/stream/iterator.py | 1 - music21/stream/streamStatus.py | 1 - music21/stream/tools.py | 1 - music21/style.py | 1 - music21/tempo.py | 3 --- music21/text.py | 1 - music21/tree/core.py | 1 - music21/tree/fromStream.py | 1 - music21/tree/spans.py | 2 -- music21/tree/trees.py | 4 ---- music21/variant.py | 7 ------- music21/voiceLeading.py | 3 --- music21/volume.py | 1 - 105 files changed, 314 deletions(-) diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py index 7319a00509..4adcab5256 100644 --- a/music21/abcFormat/__init__.py +++ b/music21/abcFormat/__init__.py @@ -2785,7 +2785,6 @@ def __add__(self, other: ABCHandler) -> ABCHandler: True >>> ah3.tokens[3] == ah2.tokens[0] True - ''' ah = self.__class__() # will get the same class type ah.tokens = self.tokens + other.tokens @@ -3114,7 +3113,6 @@ def _buildMeasureBoundaryIndices( [[0, 9], [10, 16], [16, 23], [23, 29], [29, 36], [36, 42], [42, 49], [49, 56], [56, 61], [62, 64], [64, 70], [70, 77], [77, 84], [84, 90], [90, 96], [96, 103], [103, 110], [110, 115]] - ''' # collect start and end pairs of split pairs = [] diff --git a/music21/alpha/analysis/aligner.py b/music21/alpha/analysis/aligner.py index 4df85e39f2..d7604ded32 100644 --- a/music21/alpha/analysis/aligner.py +++ b/music21/alpha/analysis/aligner.py @@ -176,7 +176,6 @@ def makeHashedStreams(self): >>> sa2.hashedSourceStream [NoteHashWithReference(Pitch=69, Duration=1.0, Offset=0.0), NoteHashWithReference(Pitch=67, Duration=1.0, Offset=1.0)] - ''' if not self.preHashed: self.hashedTargetStream = self.hasher.hashStream(self.targetStream) @@ -230,7 +229,6 @@ def setupDistanceMatrix(self): Traceback (most recent call last): music21.alpha.analysis.aligner.AlignerException: Cannot perform alignment with empty source stream. - ''' if not self.hashedTargetStream: self.makeHashedStreams() @@ -306,7 +304,6 @@ def populateDistanceMatrix(self): [2, 2, 2, 4], [4, 4, 3, 3], [6, 6, 5, 3]]) - ''' # calculate insert and delete costs based on the first tuple in the Source S @@ -381,7 +378,6 @@ def getPossibleMovesFromLocation(self, i, j): >>> sa.getPossibleMovesFromLocation(3, 0) [0, None, None] - ''' verticalCost = int(self.distanceMatrix[i - 1][j]) if i >= 1 else None horizontalCost = int(self.distanceMatrix[i][j - 1]) if j >= 1 else None @@ -736,7 +732,6 @@ def tupleEqualityWithoutReference(self, tup1, tup2): >>> sa.tupleEqualityWithoutReference(nhwr1, nhwr3) False - ''' for val in tup1.hashItemsKeys: if getattr(tup1, val) != getattr(tup2, val): @@ -828,7 +823,6 @@ def calculateChangesList(self): 1 >>> saD.similarityScore 0.5 - ''' i = self.n j = self.m diff --git a/music21/alpha/analysis/hasher.py b/music21/alpha/analysis/hasher.py index 9da199112c..2b271010a9 100644 --- a/music21/alpha/analysis/hasher.py +++ b/music21/alpha/analysis/hasher.py @@ -186,7 +186,6 @@ def _hashMIDIPitchName(self, e, thisChord=None): >>> r = note.Rest() >>> h._hashMIDIPitchName(r, thisChord=c) 0 - ''' if thisChord and self.hashChordsAsChords: return 1 diff --git a/music21/analysis/correlate.py b/music21/analysis/correlate.py index 563d94c393..e629da9868 100644 --- a/music21/analysis/correlate.py +++ b/music21/analysis/correlate.py @@ -45,7 +45,6 @@ class ActivityMatch: .. image:: images/ScatterWeightedPitchSpaceDynamicSymbol.* :width: 600 - ''' def __init__(self, streamObj): if not hasattr(streamObj, 'classes') or 'Stream' not in streamObj.classes: @@ -59,7 +58,6 @@ def _findActive(self, objNameSrc=None, objNameDst=None): Do the analysis, finding correlations of src with dst returns an ordered list of dictionaries, in the form {'src': obj, 'dst': [objs]} - ''' if objNameSrc is None: objNameSrc = (note.Note, chord.Chord) diff --git a/music21/analysis/discrete.py b/music21/analysis/discrete.py index 13af52e373..be51d2a462 100644 --- a/music21/analysis/discrete.py +++ b/music21/analysis/discrete.py @@ -565,14 +565,12 @@ def _likelyKeys(self, sStream): def _bestKeyEnharmonic(self, pitchObj, mode, sStream=None): ''' - >>> ks = analysis.discrete.KrumhanslSchmuckler() >>> s = converter.parse('tinynotation: 4/4 b-4 e- f g-') >>> ks._bestKeyEnharmonic(pitch.Pitch('e#'), 'minor', s) >>> ks._bestKeyEnharmonic(pitch.Pitch('f-'), 'major', s) - ''' if pitchObj is None: return None @@ -881,7 +879,6 @@ def getWeights(self, weightType='major'): 12 >>> a.getWeights('major') [16.8..., 0.8..., 12.9..., 1.4..., ...] - ''' weightType = weightType.lower() # note: only one value is different from KrumhanslSchmuckler @@ -1045,7 +1042,6 @@ def getPitchSpan(self, subStream) -> tuple[pitch.Pitch, pitch.Pitch]|None: >>> s.insert(4, harmony.ChordSymbol('C6')) >>> p.getPitchSpan(s) is None True - ''' if subStream is self._referenceStream and self.minPitchObj and self.maxPitchObj: return self.minPitchObj, self.maxPitchObj @@ -1118,7 +1114,6 @@ def solutionLegend(self, compress: bool = False) -> list[ >>> x = p.process(s.parts[1]) >>> [len(y) for y in [x for x in p.solutionLegend(compress=True)]] [2, 2] - ''' colorsUsed = [] if compress: @@ -1160,7 +1155,6 @@ def solutionUnitString(self): def solutionToColor(self, solution: int|None) -> str: ''' - >>> p = analysis.discrete.Ambitus() >>> s = stream.Stream() >>> c = chord.Chord(['a2', 'b4', 'c8']) diff --git a/music21/analysis/harmonicFunction.py b/music21/analysis/harmonicFunction.py index cee140a835..25237bf19e 100644 --- a/music21/analysis/harmonicFunction.py +++ b/music21/analysis/harmonicFunction.py @@ -178,7 +178,6 @@ def functionToRoman(thisHarmonicFunction: HarmonicFunction, >>> rn = roman.RomanNumeral('vi') >>> str(analysis.harmonicFunction.romanToFunction(rn)) 'Tp' - ''' if isinstance(keyOrScale, str): keyOrScale = key.Key(keyOrScale) diff --git a/music21/analysis/metrical.py b/music21/analysis/metrical.py index 10aa5222b9..8a20f44e08 100644 --- a/music21/analysis/metrical.py +++ b/music21/analysis/metrical.py @@ -114,7 +114,6 @@ def thomassenMelodicAccent(streamIn: stream.Stream): ('E4', 0.5561) ('D4', 0.17) ('D4', 0.0) - ''' # we use .ps instead of Intervals for speed, since # we just need perceived contours diff --git a/music21/analysis/reduceChordsOld.py b/music21/analysis/reduceChordsOld.py index 2281f8c329..adf531979a 100644 --- a/music21/analysis/reduceChordsOld.py +++ b/music21/analysis/reduceChordsOld.py @@ -154,7 +154,6 @@ def reduceMeasureToNChords(self, def computeMeasureChordWeights(self, measureObj, weightAlgorithm=None): ''' - >>> s = analysis.reduceChordsOld.testMeasureStream1().notes >>> cr = analysis.reduceChordsOld.ChordReducer() >>> cws = cr.computeMeasureChordWeights(s) diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py index 7628593d9c..5c9dfbaddb 100644 --- a/music21/analysis/reduction.py +++ b/music21/analysis/reduction.py @@ -463,7 +463,6 @@ class PartReduction: of all parts. The default is True. If the `normalize` parameter is False, no normalization will take place. The default is True. - ''' def __init__(self, srcScore=None, diff --git a/music21/analysis/windowed.py b/music21/analysis/windowed.py index 64bead7e89..09e143d955 100644 --- a/music21/analysis/windowed.py +++ b/music21/analysis/windowed.py @@ -94,7 +94,6 @@ def getMinimumWindowStream(self, timeSignature='1/4'): >>> wa1 = analysis.windowed.WindowedAnalysis(s, p) >>> wa2 = analysis.windowed.WindowedAnalysis(s.flatten(), p) - ''' # create a stream that contains just a 1/4 time signature; this is # the minimum window size (and partitioning will be done by measure) @@ -151,7 +150,6 @@ def analyze(self, windowSize, windowType='overlap'): >>> a, b = wa.analyze(1, windowType='adjacentAverage') >>> len(a), len(b) (36, 36) - ''' maxWindowCount = len(self._windowedStream) # assuming that this is sorted diff --git a/music21/articulations.py b/music21/articulations.py index 95b03e1f11..6e4b8953f8 100644 --- a/music21/articulations.py +++ b/music21/articulations.py @@ -219,7 +219,6 @@ class TimbreArticulation(Articulation): # ------------------------------------------------------------------------------ class Accent(DynamicArticulation): ''' - >>> a = articulations.Accent() ''' def __init__(self, **keywords): @@ -246,7 +245,6 @@ def __init__(self, **keywords): class Staccato(LengthArticulation): ''' - >>> a = articulations.Staccato() ''' def __init__(self, **keywords): diff --git a/music21/audioSearch/scoreFollower.py b/music21/audioSearch/scoreFollower.py index 5d6584957d..073f116056 100644 --- a/music21/audioSearch/scoreFollower.py +++ b/music21/audioSearch/scoreFollower.py @@ -120,7 +120,6 @@ def repeatTranscription(self): False >>> print(ScF.lastNotePosition) 10 - ''' from music21 import audioSearch @@ -382,7 +381,6 @@ def updatePosition(self, prob, totalLengthPeriod, time_start): >>> exitType = ScF.updatePosition(prob, totalLengthPeriod, time_start) >>> print(exitType) countdownExceeded - ''' exitType = False @@ -462,7 +460,6 @@ def predictNextNotePosition(self, totalLengthPeriod, totalSeconds): ... totalLengthPeriod, totalSeconds) >>> print(predictedStartPosition) 18 - ''' extraLength = totalLengthPeriod * totalSeconds / self.seconds_recording middleRhythm = 0 diff --git a/music21/base.py b/music21/base.py index 76c3b76f07..2d0addf2ea 100644 --- a/music21/base.py +++ b/music21/base.py @@ -316,7 +316,6 @@ def _getEqualityAttributes(cls) -> frozenset[str]: True >>> 'pitch' in base._getEqualityAttributes(bar.Barline) False - ''' equalityAttributes = set() # equalityAttributesIgnore works, but not yet needed. @@ -2192,7 +2191,6 @@ def getAllContextsByClass(self, className): TODO: make it so that it does not skip over multiple matching classes at the same offset. with sortTuple - ''' el = self.getContextByClass(className) while el is not None: @@ -3505,7 +3503,6 @@ def splitAtDurations(self) -> _SplitTuple: ('64th', 0, (,)) TODO: unite this and other functions into a "split" function -- document obscure uses. - ''' atm = self.duration.aggregateTupletMultiplier() quarterLengthList = [opFrac(c.quarterLength * atm) for c in self.duration.components] diff --git a/music21/beam.py b/music21/beam.py index 86afb6273c..e6e258ed04 100644 --- a/music21/beam.py +++ b/music21/beam.py @@ -642,7 +642,6 @@ def setAll(self, type, direction=None): # type is okay @ReservedAssignment >>> a.setAll('sexy') Traceback (most recent call last): music21.beam.BeamException: beam type cannot be sexy - ''' if type not in ('start', 'stop', 'continue', 'partial'): raise BeamException(f'beam type cannot be {type}') @@ -679,7 +678,6 @@ def setByNumber(self, number, type, direction=None): # type is okay @ReservedAs >>> a.setByNumber(2, 'crazy') Traceback (most recent call last): music21.beam.BeamException: beam type cannot be crazy - ''' # permit providing one argument hyphenated if '-' in type: diff --git a/music21/braille/basic.py b/music21/braille/basic.py index 22d600e972..acbca7bf5b 100644 --- a/music21/braille/basic.py +++ b/music21/braille/basic.py @@ -592,7 +592,6 @@ def yieldBrailleArticulations(noteEl): ⠦ ⠨⠦ ⠸⠦ - ''' def _brailleArticulationsSortKey(inner_articulation): isBowing = isinstance(inner_articulation, articulations.Bowing) @@ -1237,7 +1236,6 @@ def transcribeHeading( ⠀⠀⠀⠀⠑⠀⠀⠀⠀⠀ ⠞⠗⠁⠝⠟⠥⠊⠇⠇⠕⠲ ⠀⠀⠀⠣⠣⠨⠉⠀⠀⠀ - ''' if (music21KeySignature is None and music21TimeSignature is None diff --git a/music21/braille/noteGrouping.py b/music21/braille/noteGrouping.py index 588a0d255f..9df8c7f030 100644 --- a/music21/braille/noteGrouping.py +++ b/music21/braille/noteGrouping.py @@ -118,7 +118,6 @@ def transcribeGroup(self, brailleElementGrouping=None): transcribe a group of notes, possibly excluding certain attributes. Returns a (unicode) string of brailleElementGrouping transcribed. - ''' self.reset() if brailleElementGrouping is not None: diff --git a/music21/braille/segment.py b/music21/braille/segment.py index 35b30b2e29..739c9fb824 100644 --- a/music21/braille/segment.py +++ b/music21/braille/segment.py @@ -697,7 +697,6 @@ def showLeadingOctaveFromNoteGrouping(self, noteGrouping: BrailleElementGrouping True >>> bs1.showLeadingOctaveFromNoteGrouping(beg1) True - ''' currentKey = self.currentGroupingKey previousKey = self.previousGroupingKey diff --git a/music21/braille/text.py b/music21/braille/text.py index 6335f59499..a5fa2f9508 100644 --- a/music21/braille/text.py +++ b/music21/braille/text.py @@ -147,7 +147,6 @@ def addToNewLine(self, brailleNoteGrouping): >>> print(str(bt)) hi ⠨⠜⠄⠹⠹⠹ - ''' self.makeNewLine() if self.rightHandSymbol or self.leftHandSymbol: diff --git a/music21/braille/translate.py b/music21/braille/translate.py index f7e19da882..1d693f551b 100644 --- a/music21/braille/translate.py +++ b/music21/braille/translate.py @@ -597,7 +597,6 @@ def measureToBraille(music21Measure, ⠼⠁⠀⠐⠽⠣⠅ >>> print(braille.translate.measureToBraille(p.measure(1))) ⠼⠙⠲⠀⠐⠽⠣⠅ - ''' measureToTranscribe = music21Measure if not inPlace: diff --git a/music21/capella/fromCapellaXML.py b/music21/capella/fromCapellaXML.py index 5410207ef9..820cf6e967 100644 --- a/music21/capella/fromCapellaXML.py +++ b/music21/capella/fromCapellaXML.py @@ -877,7 +877,6 @@ def barlineListFromBarline(self, barlineElement: ET.Element) -> list[bar.Barline >>> repeatTag = ci.domElementFromText('') >>> ci.barlineListFromBarline(repeatTag) [, ] - ''' barlineList: list[bar.Barline] = [] hasRepeatEnd = False diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index d258b3746a..3c5a16330c 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -925,7 +925,6 @@ def formatVectorString(vectorList: Iterable[int]) -> str: >>> c1.formatVectorString([10, 11, 3, 5]) '' - ''' msg = ['<'] for e in vectorList: # should be numbers @@ -1333,7 +1332,6 @@ def bass( # # >>> cmin_inv.bass() # - ''' if newbass: newbassPitch: pitch.Pitch @@ -1424,7 +1422,6 @@ def canBeTonic(self) -> bool: >>> a = chord.Chord(['g', 'b', 'd']) >>> a.canBeTonic() True - ''' if self.isMajorTriad() or self.isMinorTriad(): return True @@ -2152,7 +2149,6 @@ def hasRepeatedChordStep( >>> cChord.hasRepeatedChordStep(5) False - ''' if testRoot is None: testRoot = self.root() @@ -2189,7 +2185,6 @@ def intervalFromChordStep( >>> print(cmaj.intervalFromChordStep(6)) None - ''' if testRoot is None: try: @@ -2544,7 +2539,6 @@ def isAugmentedSixth(self, *, permitAnyInversion: bool = False) -> bool: True If `permitAnyInversion` is True then any inversion is allowed. - ''' # cardinality is just used to speed up the call to avoid checking multiple augmented # 6ths on a triad, etc. The fact that Ab C F# Gb will have cardinality of 3 @@ -2692,7 +2686,6 @@ def isConsonant(self) -> bool: >>> c13.isConsonant() False - ''' c2 = self.removeRedundantPitchNames(inPlace=False) if len(c2.pitches) == 1: @@ -2939,7 +2932,6 @@ def isGermanAugmentedSixth(self, *, permitAnyInversion=False) -> bool: >>> gr6d = chord.Chord(['A-3', 'C-4', 'E-4', 'F#4']) >>> gr6d.isGermanAugmentedSixth() False - ''' return self._isAugmentedSixthHelper( (4, 27, -1), @@ -4000,7 +3992,6 @@ def semiClosedPosition( ... ) >>> c1 - ''' c2 = self.closedPosition(forceOctave=forceOctave, inPlace=inPlace, @@ -4491,7 +4482,6 @@ def transpose(self, value, *, inPlace=False): >>> a.transpose(aInterval, inPlace=True) >>> a - ''' if hasattr(value, 'diatonic'): # it is an Interval class intervalObj = value @@ -4985,7 +4975,6 @@ def fullName(self) -> str: >>> chord.Chord(['d1', 'e4-', 'b3-'], quarterLength=2/3).fullName 'Chord {D in octave 1 | E-flat in octave 4 | B-flat in octave 3} Quarter Triplet (2/3 QL)' - ''' msg = [] sub = [] @@ -5823,7 +5812,6 @@ def fromForteClass(notation: str|Sequence[int]) -> Chord: >>> chord.fromForteClass((11, 1)) - ''' card = None num = 1 @@ -5879,7 +5867,6 @@ def fromIntervalVector(notation: Sequence[int], getZRelation: bool = False) -> C >>> chord.fromIntervalVector((1, 1, 1, 1, 1, 1)).getZRelation() - ''' addressList = None if common.isListLike(notation): diff --git a/music21/clef.py b/music21/clef.py index 7ff0c6c80c..a62569feec 100644 --- a/music21/clef.py +++ b/music21/clef.py @@ -107,7 +107,6 @@ class Clef(base.Music21Object): True >>> clef.NoClef().sign 'none' - ''', 'line': ''' The line, counting from the bottom up, that the clef resides on. @@ -602,7 +601,6 @@ class TenorClef(CClef): 'C' >>> a.line 4 - ''' def __init__(self, **keywords): diff --git a/music21/common/classTools.py b/music21/common/classTools.py index 024074b6e7..a8ce7ddeb8 100644 --- a/music21/common/classTools.py +++ b/music21/common/classTools.py @@ -84,7 +84,6 @@ def isNum(usrData: t.Any) -> t.TypeGuard[t.Union[float, int, Fraction]]: >>> from decimal import Decimal >>> common.isNum(Decimal('2.0')) True - ''' # noinspection PyBroadException try: diff --git a/music21/common/decorators.py b/music21/common/decorators.py index a71e37455b..390a33063b 100644 --- a/music21/common/decorators.py +++ b/music21/common/decorators.py @@ -125,7 +125,6 @@ def deprecated( Restore stderr at the end. >>> sys.stderr = saveStdErr - ''' if hasattr(method, '__qualname__'): funcName = method.__qualname__ diff --git a/music21/common/formats.py b/music21/common/formats.py index 7d9bf7cce3..40bbbc64b3 100644 --- a/music21/common/formats.py +++ b/music21/common/formats.py @@ -73,7 +73,6 @@ def findSubConverterForFormat(fmt: str) -> type[SubConverter]|None: >>> common.findSubConverterForFormat('t') - ''' fmt = fmt.lower().strip() from music21 import converter diff --git a/music21/common/numberTools.py b/music21/common/numberTools.py index 21c798fad1..34eb8cbd5c 100644 --- a/music21/common/numberTools.py +++ b/music21/common/numberTools.py @@ -931,7 +931,6 @@ def groupContiguousIntegers(src: list[int]) -> list[list[int]]: # noinspection SpellCheckingInspection def fromRoman(num: str, *, strictModern: bool = False) -> int: ''' - Convert a Roman numeral (upper or lower) to an int https://code.activestate.com/recipes/81611-roman-numerals/ diff --git a/music21/configure.py b/music21/configure.py index 772cee6ab7..06ffbecdd5 100644 --- a/music21/configure.py +++ b/music21/configure.py @@ -318,7 +318,6 @@ def prependPromptHeader(self, msg): def appendPromptHeader(self, msg): ''' - >>> d = configure.Dialog() >>> d.appendPromptHeader('test') >>> d._promptHeader diff --git a/music21/converter/subConverters.py b/music21/converter/subConverters.py index 2cfea252b0..f505ce45d6 100644 --- a/music21/converter/subConverters.py +++ b/music21/converter/subConverters.py @@ -61,7 +61,6 @@ class SubConverter: codecWrite = True or False (default False) if encodings need to be used to write stringEncoding = string (default 'utf-8'). If codecWrite is True, this specifies what encoding to use - ''' readBinary: bool = False canBePickled: bool = True diff --git a/music21/corpus/__init__.py b/music21/corpus/__init__.py index 7b4982374c..f369e711ff 100644 --- a/music21/corpus/__init__.py +++ b/music21/corpus/__init__.py @@ -109,7 +109,6 @@ def getCorePaths( >>> abcFilePaths = corpus.getCorePaths(fileExtensions=('abc',)) >>> len(abcFilePaths) >= 100 True - ''' return corpora.CoreCorpus().getPaths( fileExtensions=fileExtensions, @@ -267,7 +266,6 @@ def noCorpus(): >>> corpus.noCorpus() False - ''' return corpora.CoreCorpus().noCorpus diff --git a/music21/corpus/corpora.py b/music21/corpus/corpora.py index b394dc8b95..651d28485a 100644 --- a/music21/corpus/corpora.py +++ b/music21/corpus/corpora.py @@ -292,7 +292,6 @@ def getWorkList( >>> len(coreCorpus.getWorkList('verdi')) 1 - ''' if str(workName).startswith('schumann/'): # pragma: no cover # no default schumanns, but older examples showed this. @@ -396,7 +395,6 @@ def search( ... field='noteCount', ... ) - ''' return self.metadataBundle.search( query, @@ -660,7 +658,6 @@ def manualCoreCorpusPath(self): >>> #_DOCS_SHOW coreCorpus.manualCoreCorpusPath is None >>> True #_DOCS_HIDE True - ''' userSettings = environment.UserSettings() if 'manualCoreCorpusPath' in userSettings.keys(): @@ -967,7 +964,6 @@ def name(self): >>> corpus.corpora.LocalCorpus('funkCorpus').name 'funkCorpus' - ''' if self._name is None: return 'local' diff --git a/music21/corpus/manager.py b/music21/corpus/manager.py index f3fd0bba4c..5632bd3255 100644 --- a/music21/corpus/manager.py +++ b/music21/corpus/manager.py @@ -296,7 +296,6 @@ def search( If ``corpusNames`` is None, all corpora known to music21 will be searched. See usersGuide (chapter 11) for more information on searching - ''' # >>> corpus.search('coltrane', corpusNames=('virtual',)) # diff --git a/music21/corpus/virtual.py b/music21/corpus/virtual.py index 599448bd45..747264f739 100644 --- a/music21/corpus/virtual.py +++ b/music21/corpus/virtual.py @@ -77,7 +77,6 @@ def getUrlByExt(self, extList=None): class BachBWV1007Prelude(VirtualWork): ''' - >>> a = corpus.virtual.BachBWV1007Prelude() >>> a.getUrlByExt('.xml') ['https://kern.ccarh.org/cgi-bin/ksdata?l=cc/bach/cello&file=bwv1007-01.krn&f=xml'] diff --git a/music21/duration.py b/music21/duration.py index a0af8d0460..0f5616adb8 100644 --- a/music21/duration.py +++ b/music21/duration.py @@ -331,7 +331,6 @@ def dottedMatch(qLen: OffsetQLIn, >>> duration.dottedMatch(0.00001, 2) (False, False) - ''' for dots in range(maxDots + 1): # assume qLen has n dots, so find its non-dotted length @@ -1268,7 +1267,6 @@ def setDurationType( >>> a.setDurationType(4.0) >>> a.totalTupletLength() 8.0 - ''' self._checkFrozen() if not isinstance(durType, str): diff --git a/music21/dynamics.py b/music21/dynamics.py index 146ea79783..a48deb15c8 100644 --- a/music21/dynamics.py +++ b/music21/dynamics.py @@ -172,7 +172,6 @@ class Dynamic(base.Music21Object): .. image:: images/dynamics_simple.* :width: 344 - ''' classSortOrder = 10 _styleClass = style.TextStyle diff --git a/music21/editorial.py b/music21/editorial.py index 8ece0a43ce..7dde23f389 100644 --- a/music21/editorial.py +++ b/music21/editorial.py @@ -72,7 +72,6 @@ class Editorial(prebase.ProtoM21Object, dict): .. image:: images/noteEditorialFictaSharp.* :width: 103 - ''' _DOC_ATTR: dict[str, str] = { 'comments': ''' diff --git a/music21/environment.py b/music21/environment.py index dd724a2de5..2d805c429c 100644 --- a/music21/environment.py +++ b/music21/environment.py @@ -941,7 +941,6 @@ def getKeysToPaths(self): 'musicxmlPath' 'pdfPath' 'vectorPath' - ''' return envSingleton().getKeysToPaths() diff --git a/music21/expressions.py b/music21/expressions.py index 23674a0d42..95ec81cf24 100644 --- a/music21/expressions.py +++ b/music21/expressions.py @@ -179,7 +179,6 @@ class RehearsalMark(Expression): >>> rm = expressions.RehearsalMark('B') >>> rm - ''' classSortOrder = -30 _styleClass = style.TextStylePlacement @@ -220,7 +219,6 @@ def _getNumberingFromContent(c) -> str|None: >>> print(ex._getNumberingFromContent('*')) None - ''' if c is None: return None @@ -704,7 +702,6 @@ def name(self) -> str: >>> invertedMordent = expressions.InvertedMordent(accidental=sharp) >>> invertedMordent.name 'inverted mordent (sharp)' - ''' theName: str = super().name if self.accidental is not None: @@ -1270,7 +1267,6 @@ class Trill(Ornament): The size property has been removed and replaced with `.getSize()` (which requires a `srcObj` and optional `keySig` param). Added optional `keySig` param to `.realize()` as well. - ''' _direction: str = 'up' @@ -1299,7 +1295,6 @@ def name(self) -> str: >>> doubleSharpedTrill = expressions.Trill(accidental=pitch.Accidental('double-sharp')) >>> doubleSharpedTrill.name 'trill (double-sharp)' - ''' theName: str = super().name if self.accidental: @@ -1992,7 +1987,6 @@ def name(self) -> str: ... delay=1.0, lowerAccidental=pitch.Accidental('double-flat')) >>> delayedBy1Turn.name 'delayed(delayQL=1.0) turn (lower=double-flat)' - ''' theName: str = super().name if self.delay == OrnamentDelay.DEFAULT_DELAY: diff --git a/music21/features/base.py b/music21/features/base.py index f26214c57b..32e5e44812 100644 --- a/music21/features/base.py +++ b/music21/features/base.py @@ -213,7 +213,6 @@ def getAttributeLabels(self) -> list[str]: 'Fifths_Pitch_Histogram_3', 'Fifths_Pitch_Histogram_4', 'Fifths_Pitch_Histogram_5', 'Fifths_Pitch_Histogram_6', 'Fifths_Pitch_Histogram_7', 'Fifths_Pitch_Histogram_8', 'Fifths_Pitch_Histogram_9', 'Fifths_Pitch_Histogram_10', 'Fifths_Pitch_Histogram_11'] - ''' post: list[str] = [] if self.dimensions == 1: @@ -1224,7 +1223,6 @@ def extractorsById(idOrList: str|Iterable[str], >>> y = [x.id for x in features.extractorsById('all')] >>> y[0:3], y[-3:-1] (['M1', 'M2', 'M3'], ['CS12', 'MC1']) - ''' from music21.features import jSymbolic from music21.features import native @@ -1268,7 +1266,6 @@ def extractorById(idOrList: str|Iterable[str], >>> fe = features.extractorById('p20')(s) # call class >>> fe.extract().vector [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - ''' ebi = extractorsById(idOrList=idOrList, library=library) if ebi: diff --git a/music21/features/jSymbolic.py b/music21/features/jSymbolic.py index dc48ea6abc..e98a8b876e 100644 --- a/music21/features/jSymbolic.py +++ b/music21/features/jSymbolic.py @@ -102,7 +102,6 @@ def process(self) -> None: class MostCommonMelodicIntervalFeature(featuresModule.FeatureExtractor): ''' - >>> s = corpus.parse('bwv66.6') >>> fe = features.jSymbolic.MostCommonMelodicIntervalFeature(s) >>> f = fe.extract() @@ -129,7 +128,6 @@ def process(self) -> None: class DistanceBetweenMostCommonMelodicIntervalsFeature( featuresModule.FeatureExtractor): ''' - >>> s = corpus.parse('bwv66.6') >>> fe = features.jSymbolic.DistanceBetweenMostCommonMelodicIntervalsFeature(s) >>> f = fe.extract() @@ -192,7 +190,6 @@ def process(self) -> None: class RelativeStrengthOfMostCommonIntervalsFeature( featuresModule.FeatureExtractor): ''' - >>> s = corpus.parse('bwv66.6') >>> fe = features.jSymbolic.RelativeStrengthOfMostCommonIntervalsFeature(s) >>> f = fe.extract() @@ -856,7 +853,6 @@ def process(self) -> None: class RelativeStrengthOfTopPitchClassesFeature(featuresModule.FeatureExtractor): ''' - >>> s = corpus.parse('bwv66.6') >>> fe = features.jSymbolic.RelativeStrengthOfTopPitchClassesFeature(s) >>> fe.extract().vector @@ -924,7 +920,6 @@ def process(self) -> None: class IntervalBetweenStrongestPitchClassesFeature( featuresModule.FeatureExtractor): ''' - >>> s = corpus.parse('bwv66.6') >>> fe = features.jSymbolic.IntervalBetweenStrongestPitchClassesFeature(s) >>> fe.extract().vector @@ -1495,7 +1490,6 @@ class VibratoPrevalenceFeature(featuresModule.FeatureExtractor): Number of notes for which Pitch Bend messages change direction at least twice divided by total number of notes that have Pitch Bend messages associated with them. - ''' id = 'P25' name = 'Vibrato Prevalence' @@ -1516,7 +1510,6 @@ class PrevalenceOfMicrotonesFeature(featuresModule.FeatureExtractor): Number of Note Ons that are preceded by isolated MIDI Pitch Bend messages as a fraction of the total number of Note Ons.' - ''' id = 'P26' name = 'Prevalence Of Microtones' @@ -1580,7 +1573,6 @@ class SecondStrongestRhythmicPulseFeature(featuresModule.FeatureExtractor): >>> f = fe.extract() >>> f.vector [192] - ''' id = 'R2' name = 'Second Strongest Rhythmic Pulse' @@ -1616,7 +1608,6 @@ class HarmonicityOfTwoStrongestRhythmicPulsesFeature( >>> f = fe.extract() >>> f.vector [0.5] - ''' id = 'R3' name = 'Harmonicity of Two Strongest Rhythmic Pulses' @@ -1699,7 +1690,6 @@ class StrengthRatioOfTwoStrongestRhythmicPulsesFeature( >>> fe = features.jSymbolic.StrengthRatioOfTwoStrongestRhythmicPulsesFeature(sch) >>> fe.extract().vector[0] 7.0 - ''' id = 'R6' name = 'Strength Ratio of Two Strongest Rhythmic Pulses' @@ -1756,7 +1746,6 @@ class NumberOfStrongPulsesFeature(featuresModule.FeatureExtractor): Not yet implemented. Number of beat peaks with normalized frequencies over 0.1. - ''' id = 'R8' name = 'Number of Strong Pulses' @@ -1874,7 +1863,6 @@ class BeatHistogramFeature(featuresModule.FeatureExtractor): A feature extractor that finds a feature array with entries corresponding to the frequency values of each of the bins of the beat histogram (except the first 40 empty ones). - ''' id = 'R14' name = 'Beat Histogram' @@ -2608,7 +2596,6 @@ class VariationOfDynamicsFeature(featuresModule.FeatureExtractor): Standard deviation of loudness levels of all notes. TODO: implement - ''' id = 'D2' name = 'Variation of Dynamics' @@ -2623,7 +2610,6 @@ class VariationOfDynamicsInEachVoiceFeature(featuresModule.FeatureExtractor): channel that contains at least one note. TODO: implement - ''' id = 'D3' name = 'Variation of Dynamics In Each Voice' @@ -2639,7 +2625,6 @@ class AverageNoteToNoteDynamicsChangeFeature(featuresModule.FeatureExtractor): same channel (in MIDI velocity units). TODO: implement - ''' id = 'D4' @@ -2789,7 +2774,6 @@ class VoiceEqualityNoteDurationFeature(featuresModule.FeatureExtractor): Not implemented. TODO: implement - ''' id = 'T5' name = 'Voice Equality - Note Duration' @@ -2802,7 +2786,6 @@ class VoiceEqualityDynamicsFeature(featuresModule.FeatureExtractor): Not implemented. TODO: implement - ''' id = 'T6' name = 'Voice Equality - Dynamics' @@ -2815,7 +2798,6 @@ class VoiceEqualityMelodicLeapsFeature(featuresModule.FeatureExtractor): Not implemented. TODO: implement - ''' id = 'T7' name = 'Voice Equality - Melodic Leaps' @@ -2886,7 +2868,6 @@ class RelativeNoteDensityOfHighestLineFeature(featuresModule.FeatureExtractor): Not implemented. TODO: implement - ''' id = 'T13' name = 'Relative Note Density of Highest Line' @@ -3027,7 +3008,6 @@ def process(self) -> None: class NotePrevalenceOfPitchedInstrumentsFeature( featuresModule.FeatureExtractor): ''' - >>> s1 = stream.Stream() >>> s1.append(instrument.AcousticGuitar()) >>> s1.repeatAppend(note.Note(), 4) @@ -3089,7 +3069,6 @@ class NotePrevalenceOfUnpitchedInstrumentsFeature( Not implemented. TODO: implement - ''' id = 'I4' name = 'Note Prevalence of Unpitched Instruments' @@ -3118,7 +3097,6 @@ class TimePrevalenceOfPitchedInstrumentsFeature( in seconds of the piece.' TODO: implement - ''' id = 'I5' name = 'Time Prevalence of Pitched Instruments' @@ -3146,7 +3124,6 @@ class VariabilityOfNotePrevalenceOfPitchedInstrumentsFeature( >>> fe = features.jSymbolic.VariabilityOfNotePrevalenceOfPitchedInstrumentsFeature(s1) >>> fe.extract().vector [0.33333...] - ''' id = 'I6' name = 'Variability of Note Prevalence of Pitched Instruments' @@ -3190,7 +3167,6 @@ class VariabilityOfNotePrevalenceOfUnpitchedInstrumentsFeature( official standard. TODO: implement - ''' id = 'I7' name = 'Variability of Note Prevalence of Unpitched Instruments' @@ -3213,7 +3189,6 @@ class NumberOfPitchedInstrumentsFeature(featuresModule.FeatureExtractor): >>> fe = features.jSymbolic.NumberOfPitchedInstrumentsFeature(s1) >>> fe.extract().vector [2] - ''' id = 'I8' name = 'Number of Pitched Instruments' diff --git a/music21/features/native.py b/music21/features/native.py index 6a7cce1731..214602229a 100644 --- a/music21/features/native.py +++ b/music21/features/native.py @@ -87,7 +87,6 @@ class QualityFeature(featuresModule.FeatureExtractor): # for monophonic melodies # incomplete measures / pickups for monophonic melodies - ''' id = 'P22' name = 'Quality' @@ -741,7 +740,6 @@ class ChordBassMotionFeature(featuresModule.FeatureExtractor): 0.0, 0.0, 0.07..., 0.008...] Post 1990s music has a lot more semitone motion. - ''' id = 'CS12' name = 'Chord Bass Motion' @@ -880,7 +878,6 @@ class LanguageFeature(featuresModule.FeatureExtractor): >>> fe = features.native.LanguageFeature(s) >>> fe.extract().vector [3] - ''' id = 'TX1' name = 'Language Feature' diff --git a/music21/features/outputFormats.py b/music21/features/outputFormats.py index 5e6a06d7eb..b163329815 100644 --- a/music21/features/outputFormats.py +++ b/music21/features/outputFormats.py @@ -88,7 +88,6 @@ def getHeaderLines(self, includeClassLabel: bool = True, ['Identifier', 'Changes_of_Meter', 'Composer'] ['string', 'discrete', 'discrete'] ['meta', '', 'class'] - ''' if self._dataSet is None: # pragma: no cover raise OutputFormatException('cannot get header lines without a DataSet') @@ -217,7 +216,6 @@ def getHeaderLines(self, includeClassLabel: bool = True, @ATTRIBUTE Changes_of_Meter NUMERIC @ATTRIBUTE class {} @DATA - ''' if self._dataSet is None: # pragma: no cover raise OutputFormatException('cannot get header lines without a DataSet') diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index 86c1cb8627..e679f41d91 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -641,7 +641,6 @@ def _reprInternal(self) -> str: def _toAccidental(self) -> pitch.Accidental|None: ''' - >>> from music21.figuredBass import notation as n >>> m1 = n.Modifier('#') >>> m2 = n.Modifier('-') diff --git a/music21/figuredBass/possibility.py b/music21/figuredBass/possibility.py index 6c5bc64565..7ee1e1e146 100644 --- a/music21/figuredBass/possibility.py +++ b/music21/figuredBass/possibility.py @@ -944,7 +944,6 @@ def partPairs( (, ), (, ), (, )] - ''' return list(zip(possibA, possibB, strict=True)) diff --git a/music21/figuredBass/realizer.py b/music21/figuredBass/realizer.py index 2011a60007..7e2c0a2376 100644 --- a/music21/figuredBass/realizer.py +++ b/music21/figuredBass/realizer.py @@ -456,7 +456,6 @@ def realize( >>> r4 = fbLine4.realize() >>> r4.getNumSolutions() 13 - ''' if fbRules is None: fbRules = rules.Rules() diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py index 3176ec6fc3..c8f25caf8b 100644 --- a/music21/freezeThaw.py +++ b/music21/freezeThaw.py @@ -230,7 +230,6 @@ def packStream(self, streamObj=None): >>> sf = freezeThaw.StreamFreezer(s) >>> pprint(sf.packStream()) {'m21Version': (...), 'stream': } - ''' # do all things necessary to set up the stream if streamObj is None: @@ -442,7 +441,6 @@ def setupStoredElementOffsetTuples(self, streamObj): (, 'end')] >>> s2._storedElementOffsetTuples[2][0] is v1 True - ''' if hasattr(streamObj, '_storedElementOffsetTuples'): # in the case of a spanner storing a Stream, like a StaffGroup diff --git a/music21/graph/__init__.py b/music21/graph/__init__.py index aa362942d6..6e86c6f193 100644 --- a/music21/graph/__init__.py +++ b/music21/graph/__init__.py @@ -134,7 +134,6 @@ def plotStream( .. image:: images/HorizontalBarPitchSpaceOffset.* :width: 600 - ''' plotMake = findPlot.getPlotsToMake(graphFormat, xValue, yValue, zValue) # environLocal.printDebug(['plotClassName found', plotMake]) diff --git a/music21/graph/axis.py b/music21/graph/axis.py index 30630ebd85..807b4fcea4 100644 --- a/music21/graph/axis.py +++ b/music21/graph/axis.py @@ -121,7 +121,6 @@ def _reprInternal(self): >>> axStream = graph.axis.DynamicsAxis(s, axisName='y') >>> axStream - ''' c = self.client if c is not None: @@ -1034,7 +1033,6 @@ def getOffsetMap(self): >>> om3 = ax.getOffsetMap() >>> om3 {} - ''' s = self.stream if s is None: @@ -1322,7 +1320,6 @@ def ticks(self): >>> ax.maxValue = 6 >>> ax.ticks() [(3, '$ppp$'), (4, '$pp$'), (5, '$p$'), (6, '$mp$')] - ''' ticks = [] if self.minValue is None: diff --git a/music21/graph/findPlot.py b/music21/graph/findPlot.py index dee2df5c91..7dfd5e222d 100644 --- a/music21/graph/findPlot.py +++ b/music21/graph/findPlot.py @@ -109,7 +109,6 @@ def getAxisQuantities(synonyms=False, axesToCheck=None): >>> graph.findPlot.getAxisQuantities(True, axesToCheck=theseAxes) ['count', 'quantity', 'frequency', 'counting', 'offset', 'measure', 'offsets', 'measures', 'time'] - ''' if axesToCheck is None: axesToCheck = getAxisClasses() @@ -167,7 +166,6 @@ def getPlotClassesFromFormat(graphFormat, checkPlotClasses=None): >>> pcs = [graph.plot.ScatterWeighted, graph.plot.Dolan] >>> graph.findPlot.getPlotClassesFromFormat('scatterweighted', pcs) [] - ''' graphFormat = userFormatsToFormat(graphFormat).lower() @@ -306,7 +304,6 @@ def getPlotsToMake(graphFormat: str|None = None, OrderedDict({'x': , 'y': , 'z': }))] - ''' def _bestPlotType(graphClassesToChooseFrom): # now get the best graph type from this possibly motley list diff --git a/music21/graph/plot.py b/music21/graph/plot.py index eb87a027e9..9b10fbbc7d 100644 --- a/music21/graph/plot.py +++ b/music21/graph/plot.py @@ -671,7 +671,6 @@ class HistogramPitchClass(Histogram): .. image:: images/HistogramPitchClass.* :width: 600 - ''' axesClasses: dict[str, type[axis.Axis]] = { **Histogram.axesClasses, @@ -699,7 +698,6 @@ class HistogramQuarterLength(Histogram): .. image:: images/HistogramQuarterLength.* :width: 600 - ''' axesClasses: dict[str, type[axis.Axis]] = { **Histogram.axesClasses, @@ -779,7 +777,6 @@ class ScatterWeightedPitchClassQuarterLength(ScatterWeighted): .. image:: images/ScatterWeightedPitchClassQuarterLength.* :width: 600 - ''' axesClasses: dict[str, type[axis.Axis]] = { **ScatterWeighted.axesClasses, @@ -814,7 +811,6 @@ class ScatterWeightedPitchSpaceDynamicSymbol(ScatterWeighted): .. image:: images/ScatterWeightedPitchSpaceDynamicSymbol.* :width: 600 - ''' axesClasses: dict[str, type[axis.Axis]] = { **ScatterWeighted.axesClasses, @@ -1009,7 +1005,6 @@ class WindowedKey(WindowedAnalysis): >>> p.processorClass = analysis.discrete.TemperleyKostkaPayne >>> p.doneAction = None #_DOCS_HIDE >>> p.run() - ''' processorClassDefault = discrete.KrumhanslSchmuckler @@ -1027,7 +1022,6 @@ class WindowedAmbitus(WindowedAnalysis): :width: 600 .. image:: images/legend-WindowedAmbitus.* - ''' processorClassDefault = discrete.Ambitus @@ -1174,7 +1168,6 @@ class HorizontalBarPitchClassOffset(HorizontalBar): .. image:: images/HorizontalBarPitchClassOffset.* :width: 600 - ''' axesClasses: dict[str, type[axis.Axis]] = { **HorizontalBar.axesClasses, @@ -1305,7 +1298,6 @@ class Dolan(HorizontalBarWeighted): .. image:: images/Dolan.* :width: 600 - ''' def __init__(self, streamObj=None, **keywords): diff --git a/music21/graph/primitives.py b/music21/graph/primitives.py index 526c2dc4b5..fe460b19ea 100644 --- a/music21/graph/primitives.py +++ b/music21/graph/primitives.py @@ -739,7 +739,6 @@ class GraphColorGridLegend(Graph): .. image:: images/GraphColorGridLegend.* :width: 600 - ''' _DOC_ATTR: dict[str, str] = { 'hideLeftBottomSpines': 'bool to hide the left and bottom axis spines; default True', @@ -914,7 +913,6 @@ class GraphHorizontalBar(Graph): To make an equally spaced plot, like in a Pitch Space plot, leave empty data in the form: `('', [], {})` - ''' _DOC_ATTR: dict[str, str] = { 'barSpace': 'Amount of vertical space each bar takes; default 8', @@ -1190,7 +1188,6 @@ class GraphScatterWeighted(Graph): .. image:: images/GraphScatterWeighted.* :width: 600 - ''' _DOC_ATTR: dict[str, str] = { 'maxDiameter': 'the maximum diameter of any ellipse, default 1.25', diff --git a/music21/harmony.py b/music21/harmony.py index ec9dabb64c..f9d152f2c7 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -1177,7 +1177,6 @@ def chordSymbolFigureFromChord(inChord: chord.Chord, includeChordType=False): OMIT_FROM_DOCS >>> harmony.changeAbbreviationFor('major', '') - ''' if not inChord.pitches: return '' @@ -1212,7 +1211,6 @@ def compare(inChordNums, givenChordNums, permittedOmissions=()): The corresponding semitones are compared, and if they do not match it is determined whether this is a permitted omission, etc. - ''' m = len(givenChordNums) if m > len(inChordNums): @@ -1393,7 +1391,6 @@ def getAbbreviationListGivenChordType(chordType): >>> harmony.getAbbreviationListGivenChordType('minor-major-13th') ['mM13', 'minmaj13'] - ''' return CHORD_TYPES[chordType][1] @@ -1405,7 +1402,6 @@ def getCurrentAbbreviationFor(chordType): >>> harmony.getCurrentAbbreviationFor('dominant-seventh') '7' - ''' return getAbbreviationListGivenChordType(chordType)[0] @@ -1417,7 +1413,6 @@ def getNotationStringGivenChordType(chordType): >>> harmony.getNotationStringGivenChordType('German') '1,-3,#4,-6' - ''' return CHORD_TYPES[chordType][0] diff --git a/music21/instrument.py b/music21/instrument.py index 16772e6d23..16becc2d64 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -104,7 +104,6 @@ def bundleInstruments(streamIn: stream.Stream, Bass Drum Bass Drum Cowbell - ''' if inPlace: s = streamIn @@ -332,7 +331,6 @@ def __init__(self, **keywords): class Piano(KeyboardInstrument): ''' - >>> p = instrument.Piano() >>> p.instrumentName 'Piano' @@ -398,7 +396,6 @@ def __init__(self, **keywords): class ElectricPiano(Piano): ''' - >>> p = instrument.ElectricPiano() >>> p.instrumentName 'Electric Piano' @@ -2545,7 +2542,6 @@ def getAllNamesForInstrument(instrumentClass: Instrument, 'english', 'french', 'german', 'italian', 'russian', 'spanish', and 'abbreviation'. Note that the language string is not case-sensitive, so 'German' is also fine. - ''' language = language.lower() diff --git a/music21/interval.py b/music21/interval.py index df3357e4f9..3dde7bc268 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -1853,7 +1853,6 @@ def direction(self) -> Direction: >>> interval.DiatonicInterval('A', 1).generic.direction - ''' if self.generic.undirected != 1: return self.generic.direction @@ -2777,7 +2776,6 @@ def intervalFromGenericAndChromatic( >>> interval.intervalFromGenericAndChromatic(1, 0.5) - ''' gIntV: GenericInterval if not isinstance(gInt, GenericInterval): @@ -4096,7 +4094,6 @@ def subtract(intervalList): 'Descending Diminished Unison' >>> a.chromatic.semitones -1 - ''' from music21 import pitch if not intervalList: diff --git a/music21/key.py b/music21/key.py index 05a2d69a01..ffc80d7ca4 100644 --- a/music21/key.py +++ b/music21/key.py @@ -503,7 +503,6 @@ def alteredPitches(self) -> list[pitch.Pitch]: >>> nonTrad2.isNonTraditional = True >>> nonTrad2.alteredPitches [] - ''' if self._alteredPitches is not None: return self._alteredPitches diff --git a/music21/lily/lilyObjects.py b/music21/lily/lilyObjects.py index 7b354b8fb1..c7253e4124 100644 --- a/music21/lily/lilyObjects.py +++ b/music21/lily/lilyObjects.py @@ -487,7 +487,6 @@ def stringOutput(self) -> str: class LyIdentifierInit(LyObject): r''' - >>> lyIdInit = lily.lilyObjects.LyIdentifierInit(string='hello') >>> print(lyIdInit) "hello" @@ -856,7 +855,6 @@ def stringOutput(self) -> str: class LyOutputDefBody(LyObject): r''' - output_def_body: output_def_head_with_mode_switch '{' | output_def_head_with_mode_switch '{' @@ -1956,7 +1954,6 @@ class LyScriptAbbreviation(LyObject): Holds a script abbreviation (for articulations etc.), one of:: ^ + - | > . _ - ''' def __init__(self, value: str = '') -> None: super().__init__() @@ -1971,7 +1968,6 @@ class LyScriptDir(LyObject): Holds a script direction abbreviation (above, below etc.), one of:: _ ^ - - ''' def __init__(self, value: str = '') -> None: super().__init__() diff --git a/music21/lily/translate.py b/music21/lily/translate.py index 35e87bed4b..c573246baa 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -360,7 +360,6 @@ def loadObjectFromOpus(self, opusIn: stream.Opus, makeNotation: bool = True) -> def loadObjectFromScore(self, scoreIn: stream.Score, makeNotation: bool = True) -> None: r''' - creates a filled topLevelObject (lily.lilyObjects.LyLilypondTop) whose string representation accurately reflects this Score object. @@ -1153,7 +1152,6 @@ def appendContextFromNoteOrRest(self, noteOrRest: note.GeneralNote) -> None: fis' 4 - ''' # to be removed once grace notes are supported if noteOrRest.duration.isGrace: @@ -1218,7 +1216,6 @@ def appendContextFromChord(self, chordIn: chord.Chord) -> None: < c' f' aes' > 4 - ''' self.setContextForTupletStart(chordIn) self.appendBeamCode(chordIn) @@ -1362,7 +1359,6 @@ def appendBeamCode(self, noteOrChord: note.GeneralNote) -> None: [] >>> print(lpc.context) \set stemLeftBeamCount = #2 - ''' leftBeams = 0 rightBeams = 0 @@ -1421,7 +1417,6 @@ def appendStemCode(self, noteOrChord: note.GeneralNote) -> None: def lySimpleMusicFromChord(self, chordObj: chord.Chord) -> lyo.LySimpleMusic: ''' - >>> conv = lily.translate.LilypondConverter() >>> c1 = chord.Chord(['C#2', 'E4', 'D#5']) >>> c1.quarterLength = 3.5 @@ -1614,7 +1609,6 @@ def lyEmbeddedScmFromClef(self, clefObj: clef.Clef) -> lyo.LyEmbeddedScm: >>> lpEmbeddedScm = conv.lyEmbeddedScmFromClef(t8c) >>> print(lpEmbeddedScm) \clef "treble_8" - ''' dictTranslate = OrderedDict([ ('Treble8vbClef', 'treble_8'), @@ -1661,7 +1655,6 @@ def lyEmbeddedScmFromKeySignature(self, keyObj: key.KeySignature) -> lyo.LyEmbed >>> fSharp = key.KeySignature(6) >>> print(conv.lyEmbeddedScmFromKeySignature(fSharp)) \key fis \major - ''' keyAsKey = keyObj if isinstance(keyObj, key.Key) else keyObj.asKey('major') @@ -1896,7 +1889,6 @@ def lyPrefixCompositeMusicFromRelatedVariants( ) -> tuple[lyo.LyPrefixCompositeMusic, stream.Stream]: # noinspection PyShadowingNames r''' - >>> s1 = converter.parse('tinynotation: 4/4 a4 a a a a1') >>> s2 = converter.parse('tinynotation: 4/4 b4 b b b') >>> s3 = converter.parse('tinynotation: 4/4 c4 c c c') @@ -1988,7 +1980,6 @@ def lyPrefixCompositeMusicFromRelatedVariants( } - ''' # Order List @@ -2121,7 +2112,6 @@ def lyPrefixCompositeMusicFromVariant( ) -> lyo.LyPrefixCompositeMusic: # noinspection PyShadowingNames r''' - >>> pStream = converter.parse('tinynotation: 4/4 a4 b c d e4 f g a') >>> pStream.makeMeasures(inPlace=True) >>> p = stream.Part(pStream.elements) @@ -2156,7 +2146,6 @@ def lyPrefixCompositeMusicFromVariant( >>> print(lpc.addedVariants) ['london'] - ''' replacedElementsClef = replacedElements[0].getContextByClass(clef.Clef) @@ -2458,7 +2447,6 @@ def writeLyFile(self, ext: str = '', fp: str|pathlib.Path|None = None) -> pathli The extension should be ly. If fp is None then a named temporary file is created by environment.getTempFile. - ''' tloOut = str(self.topLevelObject) if fp is None: @@ -2487,7 +2475,6 @@ def runThroughLily( If skipWriting is True and a fileName is given then it will run that file through lilypond instead - ''' LILYEXEC = self.findLilyExec() if fileName is None: diff --git a/music21/metadata/__init__.py b/music21/metadata/__init__.py index e08de8796b..7f6b67aa58 100755 --- a/music21/metadata/__init__.py +++ b/music21/metadata/__init__.py @@ -985,7 +985,6 @@ def __setitem__(self, key: str, value: t.Any|Iterable[t.Any]): >>> md[3] = ['180 minutes'] Traceback (most recent call last): KeyError: 'metadata key must be str' - ''' if not isinstance(key, str): raise KeyError('metadata key must be str') @@ -1990,7 +1989,6 @@ def _isStandardUniqueName(self, uniqueName: str) -> bool: >>> md._isStandardUniqueName('ambitus') False - ''' return uniqueName in properties.UNIQUE_NAME_TO_PROPERTY_DESCRIPTION @@ -2498,7 +2496,6 @@ class RichMetadata(Metadata): Because RichMetadata is a Music21Object, `quarterLength` is a property that must return the length of the RichMetadata object itself and should not have been ovewritten - ''' # CLASS VARIABLES # @@ -2566,7 +2563,6 @@ def merge(self, other, favorSelf=False): >>> richMetadata.merge(md) >>> richMetadata.title 'Concerto in F' - ''' # specifically name attributes to copy, as do not want to get all # Metadata is a m21 object diff --git a/music21/metadata/bundles.py b/music21/metadata/bundles.py index efa17c2af6..3475312fb6 100644 --- a/music21/metadata/bundles.py +++ b/music21/metadata/bundles.py @@ -347,7 +347,6 @@ def __eq__(self, other): True >>> bachBundle == 'foo' False - ''' if hasattr(other, '_metadataEntries'): if self._metadataEntries == other._metadataEntries: @@ -1096,7 +1095,6 @@ def read(self, filePath=None): Traceback (most recent call last): music21.exceptions21.MetadataException: Unnamed MetadataBundles have no default file path to read from. - ''' timer = common.Timer() timer.start() diff --git a/music21/metadata/caching.py b/music21/metadata/caching.py index 01b4efe1bd..745b804941 100644 --- a/music21/metadata/caching.py +++ b/music21/metadata/caching.py @@ -316,7 +316,6 @@ def process_parallel(jobs, processCount=None): available cores. jobs is a list of :class:`~music21.metadata.MetadataCachingJob` objects. - ''' processCount = processCount or common.cpus() processCount = max(processCount, 1) diff --git a/music21/metadata/primitives.py b/music21/metadata/primitives.py index 8f73d12c07..14df0e7498 100644 --- a/music21/metadata/primitives.py +++ b/music21/metadata/primitives.py @@ -453,7 +453,6 @@ def hasError(self): ... ) >>> b.hasError False - ''' for attr in self.attrNames: if getattr(self, attr + 'Error') is not None: diff --git a/music21/meter/base.py b/music21/meter/base.py index 835752ba93..e0bd305667 100644 --- a/music21/meter/base.py +++ b/music21/meter/base.py @@ -497,7 +497,6 @@ class TimeSignature(TimeSignatureBase): >>> another44 = meter.TimeSignature() # '4/4' by default >>> one44 == another44 True - ''' _styleClass = style.TextStyle classSortOrder = 4 @@ -1261,7 +1260,6 @@ def _setDefaultAccentWeights(self, depth: int = 3) -> None: >>> ts2._setDefaultAccentWeights(3) # lower depth >>> [mt.weight for mt in ts2.accentSequence] [1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125] - ''' # NOTE: this is a performance critical method firstPartitionForm: MeterSequence|int|None diff --git a/music21/meter/core.py b/music21/meter/core.py index f3fb0b52b7..9a2a468764 100644 --- a/music21/meter/core.py +++ b/music21/meter/core.py @@ -682,7 +682,6 @@ def partitionByCount(self, countRequest: int, loadDefault: bool = True) -> None: >>> a.partitionByCount(11, loadDefault=False) Traceback (most recent call last): music21.exceptions21.MeterException: Cannot set partition by 11 (5/8) - ''' opts = self.getPartitionOptions() optMatch = None @@ -962,7 +961,6 @@ def subdividePartitionsEqual(self, divisions: int|None = None) -> None: >>> ms.subdividePartitionsEqual(5) Traceback (most recent call last): music21.exceptions21.MeterException: Cannot set partition by 5 (3/8) - ''' divisionsLocal: int = 1 for i in range(len(self)): @@ -1024,7 +1022,6 @@ def _subdivideNested(self, processObjList, divisions): False >>> post2[0] is ms[0][0] True - ''' for obj in processObjList: obj.subdividePartitionsEqual(divisions) @@ -1957,7 +1954,6 @@ def offsetToSpan(self, qLenPos, permitMeterModulus=False): >>> a.offsetToSpan(4.33333336, permitMeterModulus=True) (1.0, 2.0) - ''' qLenPos = opFrac(qLenPos) if qLenPos >= self.duration.quarterLength or qLenPos < 0: @@ -1997,7 +1993,6 @@ def offsetToWeight(self, qLenPos): Fraction(1, 3) >>> a.offsetToWeight(1.5) Fraction(1, 3) - ''' # Not sure what this does! qLenPos = opFrac(qLenPos) diff --git a/music21/midi/percussion.py b/music21/midi/percussion.py index 3cb11ec5eb..b36105214a 100644 --- a/music21/midi/percussion.py +++ b/music21/midi/percussion.py @@ -144,7 +144,6 @@ def midiPitchToInstrument(self, midiPitch: int|pitch.Pitch) -> instrument.Instru >>> oneBDInstrument.modifier '1' - ''' if isinstance(midiPitch, int): diff --git a/music21/midi/translate.py b/music21/midi/translate.py index f39564330f..6f80058da5 100644 --- a/music21/midi/translate.py +++ b/music21/midi/translate.py @@ -177,7 +177,6 @@ def ticksToDuration( '64th' >>> d2.components[2].dots 1 - ''' if inputM21DurationObject is None: d = duration.Duration() @@ -984,7 +983,6 @@ def midiEventsToTimeSignature( >>> ts = midi.translate.midiEventsToTimeSignature(me2) >>> ts - ''' # http://www.sonicspot.com/guide/midifiles.html # The time signature defined with 4 bytes, a numerator, a denominator, diff --git a/music21/musedata/__init__.py b/music21/musedata/__init__.py index 941fc5e9c7..966304b88c 100644 --- a/music21/musedata/__init__.py +++ b/music21/musedata/__init__.py @@ -159,7 +159,6 @@ def isBack(self): def _getPitchParameters(self): ''' - >>> mdr = musedata.MuseDataRecord('Ef4 1 s d ==') >>> mdr.isNote() True @@ -397,7 +396,6 @@ def getBeams(self): >>> mdr = musedata.MuseDataRecord('E2 4 q u') >>> mdr.getBeams() is None True - ''' if self.stage == 1: return None @@ -458,7 +456,6 @@ def getArticulationObjects(self): >>> mdr = musedata.MuseDataRecord('C4 12 e u [ .p>') >>> mdr.getArticulationObjects() [, ] - ''' from music21 import articulations post = [] @@ -498,7 +495,6 @@ def getExpressionObjects(self): >>> mdr = musedata.MuseDataRecord('C4 12 e u [ .p>F') >>> mdr.getExpressionObjects() [] - ''' from music21 import expressions post = [] @@ -574,7 +570,6 @@ def hasCautionaryAccidental(self): >>> mdr = musedata.MuseDataRecord('C4 12 e u [') >>> mdr.hasCautionaryAccidental() False - ''' data = self._getAdditionalNotations() if data is None: @@ -833,7 +828,6 @@ def _scrubStage1(self, src): def _getDigitsFollowingTag(self, line, tag): ''' - >>> mdp = musedata.MuseDataPart() >>> mdp._getDigitsFollowingTag('junk WK#:2345', 'WK#:') '2345' @@ -864,7 +858,6 @@ def _getDigitsFollowingTag(self, line, tag): def _getAlphasFollowingTag(self, line, tag, keepSpace=False, keepCase=False): ''' - >>> mdp = musedata.MuseDataPart() >>> mdp._getAlphasFollowingTag('Group memberships: sound, score', 'Group memberships:') 'sound,score' @@ -1386,7 +1379,6 @@ def getDivisionsPerQuarterNote(self): def _getMeasureBoundaryIndices(self, src=None): ''' - >>> mdp = musedata.MuseDataPart() >>> mdp.stage is None True diff --git a/music21/musedata/translate.py b/music21/musedata/translate.py index 58ded0dd53..6f95655db5 100644 --- a/music21/musedata/translate.py +++ b/music21/musedata/translate.py @@ -43,7 +43,6 @@ def _musedataBeamToBeams(beamSymbol): >>> translate._musedataBeamToBeams(r']/') # must escape backslash /> - ''' from music21 import beam diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index d859d1ff67..605810d917 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -218,7 +218,6 @@ def _setTagTextFromAttribute( >>> subEl = seta(acc, e, 'alter', transform=int) >>> subEl.text '-2' - ''' if attributeName is None: attributeName = common.hyphenToCamelCase(tag) @@ -543,7 +542,6 @@ def fromDynamic(self, dynamicObject): ''' Provide a complete MusicXML string from a single dynamic by putting it into a Stream first. - ''' dCopy = copy.deepcopy(dynamicObject) out = stream.Stream() @@ -1233,7 +1231,6 @@ def staffLayoutToXmlStaffLayout(self, staffLayout, mxStaffLayoutIn=None): 40.0 - ''' if mxStaffLayoutIn is None: mxStaffLayout = Element('staff-layout') @@ -1494,7 +1491,6 @@ def scorePreliminaries(self) -> None: 0 >>> emptySX.spannerBundle - ''' self.setScoreLayouts() self.setMeterStream() @@ -2482,7 +2478,6 @@ def getSupports(self) -> list[Element]: >>> SX.stream.definesExplicitPageBreaks = True >>> SX.dump(SX.getSupports()[-1]) - ''' def getSupport(element: str, supports_type: bool, attribute: str|None = None, value: str|None = None): @@ -3193,7 +3188,6 @@ def moveForward(self, byOffset: OffsetQL): >>> len(MEX.xmlRoot) 1 - ''' amountToMoveForward: int = int(round(byOffset * self.currentDivisions)) if amountToMoveForward: @@ -4785,7 +4779,6 @@ def tupletToTimeModification(self, tup) -> Element: - ''' mxTimeModification = Element('time-modification') _setTagTextFromAttribute(tup, mxTimeModification, 'actual-notes', 'numberNotesActual') @@ -6235,7 +6228,6 @@ def dynamicToXml(self, d: dynamics.Dynamic) -> Element: 10080 - ''' mxDynamics = Element('dynamics') synchronizeIds(mxDynamics, d) @@ -6295,7 +6287,6 @@ def segnoToXml(self, segno: repeat.Segno) -> Element: - ''' mxSegno = Element('segno') synchronizeIds(mxSegno, segno) @@ -6448,7 +6439,6 @@ def tempoIndicationToXml(self, ti: tempo.TempoIndication) -> Element: Andante - ''' # if writing just a sound tag, place an empty words tag in a # direction type and then follow with sound declaration @@ -7516,7 +7506,6 @@ def intervalToXmlTranspose(self, i=None): -2 -1 - ''' # TODO: number attribute (staff number) # TODO: double empty attribute diff --git a/music21/musicxml/xmlObjects.py b/music21/musicxml/xmlObjects.py index 2342e50d8b..7c8babf783 100644 --- a/music21/musicxml/xmlObjects.py +++ b/music21/musicxml/xmlObjects.py @@ -154,7 +154,6 @@ def booleanToYesNo(value): >>> musicxml.xmlObjects.booleanToYesNo(5) 'yes' - ''' if value: # purposely not "is True" return 'yes' @@ -173,7 +172,6 @@ def fractionToPercent(value): >>> musicxml.xmlObjects.fractionToPercent(0.251) '25' - ''' return str(int(value * 100)) @@ -205,7 +203,6 @@ def isValidXSDID(text): >>> musicxml.xmlObjects.isValidXSDID(12345) False - ''' if not isinstance(text, str): return False diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index cc7a9b2e7c..6b57035eaf 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -899,7 +899,6 @@ def parsePartList(self, mxScore): entries into self.mxScorePartDict[partId] and adds them to any open entries, stored as PartGroup objects in self.partGroupList - ''' mxPartList = mxScore.find('part-list') if mxPartList is None: @@ -2336,7 +2335,6 @@ def applyMultiMeasureRest(self, r: note.Rest): >>> pp.applyMultiMeasureRest(r3) >>> pp.stream.show('text') {0.0} - ''' if self.activeMultiMeasureRestSpanner is None: return diff --git a/music21/note.py b/music21/note.py index 1f1fd4f6a1..9c0ff2a282 100644 --- a/music21/note.py +++ b/music21/note.py @@ -1761,7 +1761,6 @@ def transpose(self, value, *, inPlace=False): {0.0} {1.0} {1.0} - ''' from music21 import key if isinstance(value, interval.IntervalBase): diff --git a/music21/noteworthy/binaryTranslate.py b/music21/noteworthy/binaryTranslate.py index f9a292cedd..5cc3da30c9 100644 --- a/music21/noteworthy/binaryTranslate.py +++ b/music21/noteworthy/binaryTranslate.py @@ -186,7 +186,6 @@ {36.0} {0.0} {3.0} - ''' from __future__ import annotations diff --git a/music21/noteworthy/translate.py b/music21/noteworthy/translate.py index d83f9e4f02..f4e1cc84be 100644 --- a/music21/noteworthy/translate.py +++ b/music21/noteworthy/translate.py @@ -151,7 +151,6 @@ def parseList(self, dataList): {0.0} {0.0} {0.0} - ''' # Main for pi in dataList: @@ -243,7 +242,6 @@ def setDurationForObject(self, generalNote, durationInfo): >>> nwt.setDurationForObject(n, 'Half') >>> n.duration - ''' parts = durationInfo.split(',') lengthNote = parts[0] @@ -467,7 +465,6 @@ def translateNote(self, attributes): >>> nwt.translateNote({'Dur': 'Half', 'Pos': '-3'}) >>> measure[1] - ''' durationInfo = attributes['Dur'] pitchInfo = attributes['Pos'] @@ -522,7 +519,6 @@ def translateChord(self, attributes): >>> nwt.translateChord({'Dur': ['Half'], 'Pos': ['1,3,5']}) >>> measure[1] - ''' durationInfos = attributes['Dur'] pitchInfos = attributes['Pos'] @@ -610,7 +606,6 @@ def translateRest(self, attributes): {0.0} {2.0} {2.75} - ''' durationInfo = attributes['Dur'] @@ -649,7 +644,6 @@ def createClef(self, attributes): Traceback (most recent call last): music21.noteworthy.translate.NoteworthyTranslateException: Did not find a proper clef in type, OrangeClef - ''' currentClef = None if 'OctaveShift' in attributes: @@ -778,7 +772,6 @@ def createBarlines(self, attributes): >>> nwt.currentMeasure.leftBarline - ''' self.activeAccidentals = {} diff --git a/music21/omr/correctors.py b/music21/omr/correctors.py index bbfed53183..3e3b9fc2c1 100644 --- a/music21/omr/correctors.py +++ b/music21/omr/correctors.py @@ -101,7 +101,6 @@ def getAllHashes(self): def getSinglePart(self, pn): ''' returns a NEW SinglePart object for part number pn from the score - ''' return SinglePart(self.score.parts[pn], pn) @@ -167,7 +166,6 @@ def verticalProbabilityDist(self): ''' Uses a score and returns an array of probabilities. For n in the array, n is the probability that the nth part - ''' if self.distributionArray is not None: return self.distributionArray @@ -464,7 +462,6 @@ def getIncorrectMeasureIndices(self, runFast=False): >>> p[1].insert(0, meter.TimeSignature('3/8')) >>> sp.getIncorrectMeasureIndices(runFast=False) [] - ''' from music21 import meter self.incorrectMeasures = [] @@ -555,7 +552,6 @@ def runHorizontalSearch(self, i): ''' Returns an array of the indices of the minimum distance measures given a measure (with index i) to compare to. - ''' unused_probabilityDistribution = self.horizontalProbabilityDist() incorrectMeasures = self.incorrectMeasures @@ -804,7 +800,6 @@ def hashNote(self, n): def hashGrace(self, n): ''' Gives a Grace Note a duration of a 128th note - ''' graceNoteDuration = self.hashQuarterLength(0.015625) byteEncoding = chr(graceNoteDuration) @@ -886,7 +881,6 @@ def getMeasureDifference(self, hashString): >>> hasher.setSequenceMatcher() >>> hasher.getMeasureDifference('VFUF') 1.0 - ''' self.sequenceMatcher.set_seq2(hashString) @@ -1114,7 +1108,6 @@ def getProbabilityOnSubstitute(self, source, destination): Take minimum length. Compare index to index. Any additional letters in the flagged measure get graded as additions. Any additional letters in the comparison measure get graded as omissions. - ''' ls = len(source) ld = len(destination) diff --git a/music21/omr/evaluators.py b/music21/omr/evaluators.py index 32841f35e3..fa4c91eb79 100644 --- a/music21/omr/evaluators.py +++ b/music21/omr/evaluators.py @@ -28,7 +28,6 @@ class OmrGroundTruthPair: (or a pair of music21.stream.Score objects). See below for examples. - ''' def __init__(self, omr=None, ground=None): @@ -335,7 +334,6 @@ def autoCorrelationBestMeasure(inputScore): (18, 6) >>> print( float(totalUnflaggedWithMatches) / totalUnflagged ) 0.333... - ''' ss = correctors.ScoreCorrector(inputScore) allHashes = ss.getAllHashes() diff --git a/music21/pitch.py b/music21/pitch.py index 217fd3fb8a..cda3b89d85 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -791,7 +791,6 @@ class Microtone(prebase.ProtoM21Object, SlottedObjectMixin): >>> m.alter 0.3333... - ''' # CLASS VARIABLES # @@ -2517,7 +2516,6 @@ def convertMicrotonesToQuarterTones(self, *, inPlace: bool = False) -> t.Self|No >>> p.convertMicrotonesToQuarterTones(inPlace=True) >>> str(p) 'F~2(+12c)' - ''' if inPlace: returnObj = self @@ -4622,7 +4620,6 @@ def transpose( >>> dPitch.transpose(intv, inPlace=True) >>> dPitch - ''' # environLocal.printDebug(['Pitch.transpose()', value]) if isinstance(value, interval.IntervalBase): @@ -4905,7 +4902,6 @@ def _stepInKeySignature(self, alteredPitches: list[Pitch]) -> bool: >>> b._stepInKeySignature(ks.alteredPitches) False - ''' for p in alteredPitches: # all are altered tones, must have acc if p.step == self.step: # A# to A or A# to A-, etc @@ -5390,7 +5386,6 @@ def getStringHarmonic(self, chordIn: chord.Chord) -> chord.Chord|t.Literal[False (, , ) otherwise returns False - ''' # Takes in a chord, finds the interval between the notes from music21 import note diff --git a/music21/repeat.py b/music21/repeat.py index 22dca1761a..b136311504 100644 --- a/music21/repeat.py +++ b/music21/repeat.py @@ -491,7 +491,6 @@ def insertRepeat(s, start, end, *, inPlace=False): 'start' >>> s.parts[0].measure(6).rightBarline.direction 'end' - ''' if s is None: @@ -951,7 +950,6 @@ def _daCapoOrSegno(self): Return a DaCapo object if this piece uses any form of DaCapo; return a Segno object if this piece uses any form of Segno. Returns None if incoherent or the piece uses neither. - ''' sumDc = self._dcCount + self._dcafCount + self._dcacCount # for now, only accepting one segno @@ -1943,7 +1941,6 @@ class RepeatFinder: .. image:: images/repeat-SimplifyExample_ChoraleSimplified.* :width: 600 - ''' _DOC_ORDER = ['simplify', 'getMeasureSimilarityList', @@ -2010,7 +2007,6 @@ def getQuarterLengthOfPickupMeasure(self): Traceback (most recent call last): music21.repeat.NoInternalStreamException: RepeatFinder must be initialized with a stream - ''' if self.s is None: raise NoInternalStreamException( @@ -2563,7 +2559,6 @@ def getSimilarMeasureGroups(self, threshold=1): Notice that although measures 2-3 are the same as measures 6-7, we don't have ([2, 3], [6, 7]) in our result, since ([1, 2, 3], [5, 6, 7]) already contains that information. - ''' # see if we've already done this computation if self._mGroups is None: diff --git a/music21/roman.py b/music21/roman.py index 51041220ca..714f5ab4f9 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -428,7 +428,6 @@ def correctSuffixForChordQuality(chordObj, inversionString): >>> c = chord.Chord('E3 C4 G-4') >>> roman.correctSuffixForChordQuality(c, '6') 'o6' - ''' fifthType = chordObj.semitonesFromChordStep(5) if fifthType == 6: @@ -2389,7 +2388,6 @@ class RomanNumeral(harmony.Harmony): >>> roman.RomanNumeral('II', 'C', caseMatters=False).impliedQuality '' - ''', 'impliedScale': ''' If no key or scale is passed in as the second object, then @@ -3021,7 +3019,6 @@ def _parseOmittedSteps(self, workingFigure: str) -> str: '13b3' >>> rn.omittedSteps [4, 2, 7] - ''' omittedSteps = [] match = self._omittedStepsRegex.search(workingFigure) @@ -3075,7 +3072,6 @@ def _parseBracketedAlterations(self, workingFigure: str) -> str: '7' >>> rn.bracketedAlterations [('#', 5), ('b', 3)] - ''' matches = self._bracketedAlterationRegex.finditer(workingFigure) for m in matches: @@ -3965,7 +3961,6 @@ def isNeapolitan(self, >>> rn = roman.RomanNumeral('N53') >>> rn.isNeapolitan(require1stInversion=False) True - ''' if self.scaleDegree != 2: return False diff --git a/music21/romanText/clercqTemperley.py b/music21/romanText/clercqTemperley.py index 1fa06a92df..b03687190d 100644 --- a/music21/romanText/clercqTemperley.py +++ b/music21/romanText/clercqTemperley.py @@ -323,7 +323,6 @@ class CTSong(prebase.ProtoM21Object): Fadeout: I . . V | I . . V | I . . V | Co: [2/4] I | [4/4] . . . V | I . . V | $Fadeout S: [G] $In $Vr $Ch $In*2 $Ch $Vr2 $Ch $Ch $Co - """ _DOC_ORDER = ['text', 'toPart', 'title', 'homeTimeSig', 'homeKey', 'comments', 'rules'] _DOC_ATTR: dict[str, str] = { @@ -997,7 +996,6 @@ def fixupChordAtom(self, atom: str) -> str: 'vii/o7' >>> s.fixupChordAtom('iia') 'ii+' - ''' if 'x' in atom: atom = atom.replace('x', 'o') diff --git a/music21/romanText/rtObjects.py b/music21/romanText/rtObjects.py index c373ed4679..a227876fc2 100644 --- a/music21/romanText/rtObjects.py +++ b/music21/romanText/rtObjects.py @@ -512,7 +512,6 @@ class RTMeasure(RTToken): ['a'] >>> rtm.isMeasure() True - ''' def __init__(self, src: str = ''): @@ -911,7 +910,6 @@ class RTAnalyticKey(RTKeyTypeAtom): >>> bMinor.getKey() - ''' footerStrip = ':' diff --git a/music21/romanText/tsvConverter.py b/music21/romanText/tsvConverter.py index 9c91ac5877..9b81ac4580 100644 --- a/music21/romanText/tsvConverter.py +++ b/music21/romanText/tsvConverter.py @@ -522,7 +522,6 @@ class TsvHandler: >>> out_stream = handler.toM21Stream() >>> out_stream.parts[0].measure(1)[roman.RomanNumeral][0].figure 'I' - ''' def __init__(self, tsvFile: str|pathlib.Path, dcml_version: int = 1) -> None: if dcml_version == 1: @@ -1135,7 +1134,6 @@ def getLocalKey(local_key: str, global_key: str, convertDCMLToM21: bool = False) >>> romanText.tsvConverter.getLocalKey('vii', 'a', convertDCMLToM21=True) 'g' - ''' if convertDCMLToM21: local_key = characterSwaps(local_key, minor=isMinor(global_key[0]), direction='DCML-m21') diff --git a/music21/romanText/writeRoman.py b/music21/romanText/writeRoman.py index 132564fce9..58baadb691 100644 --- a/music21/romanText/writeRoman.py +++ b/music21/romanText/writeRoman.py @@ -269,7 +269,6 @@ def prepSequentialListOfLines(self) -> None: >>> testCase.combinedList[-2] 'Time Signature: 3/4' - ''' for thisMeasure in self.container.getElementsByClass(stream.Measure): @@ -388,7 +387,6 @@ def rnString(measureNumber: int|str, As these examples show, the chordString can be a Roman numeral alone (e.g. 'viio6') or one prefixed by a change of key ('G: I'). - ''' if inString: diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 6d25c6fad0..15bcf218f1 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -488,7 +488,6 @@ def clear(self) -> None: def __eq__(self, other) -> bool: ''' - >>> edgeList1 = ['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2'] >>> edgeList2 = ['M2', 'M2', 'm2', 'M2', 'A3', 'm2'] @@ -2054,7 +2053,6 @@ def realizePitch( ['C1', 'G1', 'D2', 'A2', 'E3', 'B3', 'F#4', 'D-5', 'A-5', 'E-6', 'B-6', 'F7', 'C8'] >>> [str(p) for p in net5ths.realizePitch(pitch.Pitch('C2'))] ['C2', 'G2', 'D3', 'A3', 'E4', 'B4', 'F#5', 'D-6', 'A-6', 'E-7', 'B-7', 'F8', 'C9'] - ''' components = self.realize( pitchReference=pitchReference, @@ -2578,7 +2576,6 @@ def getRelativeNodeDegree( 2 >>> net.getRelativeNodeDegree('f6', 1, 'b3') 1 - ''' nId = self.getRelativeNodeId( pitchReference=pitchReference, @@ -2821,7 +2818,6 @@ def match(self, ['B-2', 'C3', 'E-3', 'E#3', 'F2', 'E--2'] >>> unmatched [] - ''' # these return a Node, not a nodeId # TODO: just getting first diff --git a/music21/search/base.py b/music21/search/base.py index b111c9197b..e6e856f628 100644 --- a/music21/search/base.py +++ b/music21/search/base.py @@ -1226,7 +1226,6 @@ def translateDurationToBytes(n: note.GeneralNote) -> str: '_' >>> (2 ** (ord(trans[0]) / 10)) / 256 # approximately 3 2.828... - ''' duration1to127 = 1 if n.duration.quarterLength: diff --git a/music21/search/lyrics.py b/music21/search/lyrics.py index c25120a841..ca0f886841 100644 --- a/music21/search/lyrics.py +++ b/music21/search/lyrics.py @@ -35,7 +35,6 @@ class IndexedLyric(namedtuple( )): ''' A Lyric that has been indexed to its attached element and position in a Stream. - ''' __slots__ = () _DOC_ATTR: dict[str, str] = { diff --git a/music21/search/segment.py b/music21/search/segment.py index 7b25d30852..792d00ea67 100644 --- a/music21/search/segment.py +++ b/music21/search/segment.py @@ -97,7 +97,6 @@ def translateMonophonicPartToSegments( >>> measureLists[0:2] [(1, 12), (7, 18)] - ''' from music21 import search if algorithm is None: diff --git a/music21/serial.py b/music21/serial.py index 01bbc7dacf..940696a981 100644 --- a/music21/serial.py +++ b/music21/serial.py @@ -427,7 +427,6 @@ def isSameRow(self, other): def getIntervalsAsString(self): ''' - Returns the string of intervals between consecutive pitch classes of a :class:`~music21.serial.ToneRow`. 'T' = 10, 'E' = 11. diff --git a/music21/sieve.py b/music21/sieve.py index b999fa5fc0..5f4605495b 100644 --- a/music21/sieve.py +++ b/music21/sieve.py @@ -253,7 +253,6 @@ def discreteBinaryPad(series: Iterable[int], fixRange=None) -> list[int]: >>> sieve.discreteBinaryPad([3, 4, 5]) [1, 1, 1] - ''' # make sure these are ints for x in series: @@ -293,7 +292,6 @@ def unitNormRange(series, fixRange=None): >>> sieve.unitNormRange([1, 3, 4]) [0.0, 0.666..., 1.0] - ''' if fixRange is not None: fixRange.sort() @@ -367,7 +365,6 @@ def unitNormStep(step, a=0, b=1, normalized=True): >>> post = sieve.unitNormStep(0.25, 0, 20, normalized=False) >>> len(post) 81 - ''' if a == b: return [] # no range, return boundary @@ -869,7 +866,6 @@ def _zUpdate(self, z=None): # -------------------------------------------------------------------------- def __call__(self): ''' - >>> a = sieve.CompressionSegment([3, 4, 5, 6, 7, 8]) >>> b = a() >>> str(b[0]) diff --git a/music21/sites.py b/music21/sites.py index b09a695cf6..645023faa9 100644 --- a/music21/sites.py +++ b/music21/sites.py @@ -281,7 +281,6 @@ def __len__(self): >>> aContexts.add(aObj) >>> len(aContexts) 2 - ''' return len(self.siteDict) @@ -585,7 +584,6 @@ def getAttrByName(self, attrName): >>> aSites.getAttrByName('blah') is None True - ''' post = None for obj in self.yieldSites(sortByCreationTime='reverse'): @@ -951,7 +949,6 @@ def remove(self, site): >>> aSites.remove(aSite) >>> len(aSites) 3 - ''' # must clear self._lastID = -1 # cannot be None diff --git a/music21/spanner.py b/music21/spanner.py index 6a04bd498e..ea6f9423d0 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -349,7 +349,6 @@ def purgeLocations(self, rescanIsDead=False): # -------------------------------------------------------------------------- def __getitem__(self, key): ''' - >>> n1 = note.Note('g') >>> n2 = note.Note('f#') >>> c1 = clef.BassClef() @@ -399,7 +398,6 @@ def getSpannedElements(self): def getSpannedElementsByClass(self, classFilterList): ''' - >>> n1 = note.Note('g') >>> n2 = note.Note('f#') >>> c1 = clef.AltoClef() diff --git a/music21/stream/base.py b/music21/stream/base.py index 18baebaf49..baea9a99bb 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -662,7 +662,6 @@ def __getitem__(self, still works until v9. This is an attempt to unify __getitem__ behavior in StreamIterators and Streams. - allowed iterables of qualified class names, e.g. `[note.Note, note.Rest]` - ''' # need to sort if not sorted, as this call may rely on index positions if not self.isSorted and self.autoSort: @@ -1557,7 +1556,6 @@ def index(self, el: base.Music21Object) -> int: >>> tuple(s).index(n3) 0 - ''' if not self.isSorted and self.autoSort: self.sort() # will set isSorted to True @@ -4317,7 +4315,6 @@ def getElementAfterElement(self, element, classList=None): Traceback (most recent call last): music21.exceptions21.StreamException: cannot find object () in Stream - ''' if classList is not None: classSet = set(classList) @@ -6181,7 +6178,6 @@ def extractContext(self, searchElement, before=4.0, after=4.0, NOTE: RENAME: this probably should be renamed, as we use Context in a special way. Perhaps better is extractNeighbors? - ''' display = self.cloneEmpty('extractContext') @@ -6246,7 +6242,6 @@ def _uniqueOffsetsAndEndTimes(self, offsetsOnly=False, endTimesOnly=False): >>> s.flatten()._uniqueOffsetsAndEndTimes(offsetsOnly=True, endTimesOnly=True) [] - ''' offsetDictValues = self._offsetDict.values() if endTimesOnly: @@ -9631,7 +9626,6 @@ def sliceByQuarterLengths(self, quarterLengthList, *, target=None, If `target` is None, the entire Stream is processed. Otherwise, only the element specified is manipulated. - ''' if not inPlace: # make a copy returnObj = self.coreCopyAsDerivation('sliceByQuarterLengths') @@ -10837,7 +10831,6 @@ def getOverlaps(self): >>> d = a.getOverlaps() >>> len(d[0]) 7 - ''' overlapMap = self._findLayering() # environLocal.printDebug(['overlapMap', overlapMap]) @@ -11151,7 +11144,6 @@ def allPlayingWhileSounding(self, el, elStream=None): is list, it is used like classList in elsewhere in stream to provide a list of classes that the el must be a part of. - ''' if elStream is not None: # a bit of safety elOffset = el.getOffsetBySite(elStream) @@ -11734,7 +11726,6 @@ def lyrics( >>> list(scr.lyrics(ignoreBarlines=True, recurse=False).keys()) [] - ''' returnLists: dict[int, list[RecursiveLyricList]] = {} numNotes = 0 @@ -12316,7 +12307,6 @@ def _insertDeletionVariant(self, v, matchBySpan=True): {1.0} {2.0} {3.0} - ''' from music21 import variant @@ -12452,7 +12442,6 @@ def _insertInsertionVariant(self, v, matchBySpan=True): {1.0} {2.0} {3.0} - ''' from music21 import variant @@ -12734,7 +12723,6 @@ def _fixMeasureNumbers(self, deletedMeasures, insertedMeasures): ... fixedNumbers.append( m.number ) >>> fixedNumbers [1, 2, 3, 4, 5] - ''' deletedMeasures.extend(insertedMeasures) allMeasures = deletedMeasures @@ -12857,7 +12845,6 @@ def showVariantAsOssialikePart(self, containedPart, variantGroups, *, inPlace=Fa >>> streamWithOssia = s.showVariantAsOssialikePart(sPart, ... ['variant1', 'variant2', 'variant3'], inPlace=False) >>> #_DOCS_SHOW streamWithOssia.show() - ''' from music21 import variant @@ -13460,7 +13447,6 @@ def bestTimeSignature(self): For further details about complex time signatures, etc. see `meter.bestTimeSignature()` - ''' return meter.bestTimeSignature(self) @@ -14193,7 +14179,6 @@ def partsToVoices(self, 4 >>> len(post.flatten().notes) 165 - ''' from music21 import spanner diff --git a/music21/stream/core.py b/music21/stream/core.py index fac17154a3..4fd3e901fc 100644 --- a/music21/stream/core.py +++ b/music21/stream/core.py @@ -403,7 +403,6 @@ def coreGuardBeforeAddElement(self, element, *, checkRedundancy=True): music21.exceptions21.StreamException: The object you tried to add to the Stream, 3.14159, is not a Music21Object. Use an ElementWrapper object if this is what you intend. - ''' if element is self: # cannot add this Stream into itself raise StreamException('this Stream cannot be contained within itself') diff --git a/music21/stream/filters.py b/music21/stream/filters.py index aee1f5553f..37c2392e95 100644 --- a/music21/stream/filters.py +++ b/music21/stream/filters.py @@ -63,7 +63,6 @@ class StreamFilter(prebase.ProtoM21Object): True >>> sf.classes ('StreamFilter', 'ProtoM21Object', 'object') - ''' derivationStr = 'streamFilter' @@ -115,7 +114,6 @@ class IsFilter(StreamFilter): ... print(el) - ''' derivationStr = 'is' @@ -193,7 +191,6 @@ class IdFilter(StreamFilter): No corresponding iterator call. Only a single ID can be passed in. Always returns a single item. - ''' derivationStr = 'getElementById' @@ -241,7 +238,6 @@ class ClassFilter(StreamFilter): ... print(x) - ''' derivationStr = 'getElementsByClass' diff --git a/music21/stream/iterator.py b/music21/stream/iterator.py index 3686214b23..898d95d4e4 100644 --- a/music21/stream/iterator.py +++ b/music21/stream/iterator.py @@ -466,7 +466,6 @@ def __bool__(self) -> bool: 0 >>> bool(iterator.getElementsByClass(chord.Chord)) False - ''' if self._len is not None: return bool(self._len) diff --git a/music21/stream/streamStatus.py b/music21/stream/streamStatus.py index 1830ec5cfc..36a4d18d19 100644 --- a/music21/stream/streamStatus.py +++ b/music21/stream/streamStatus.py @@ -138,7 +138,6 @@ def haveTupletBracketsBeenMade(self): >>> nTuplet.duration.tuplets[0].type = 'start' >>> s.streamStatus.haveTupletBracketsBeenMade() True - ''' foundTuplet = False for n in self.client.recurse(classFilter='GeneralNote', restoreActiveSites=False): diff --git a/music21/stream/tools.py b/music21/stream/tools.py index efd1f3fe6b..1fa145f8de 100644 --- a/music21/stream/tools.py +++ b/music21/stream/tools.py @@ -154,7 +154,6 @@ def removeDuplicates(thisStream: stream.Stream, >>> t = stream.tools.removeDuplicates(s, inPlace=False) >>> s.parts[0] == testInPlace True - ''' supportedClasses = (meter.TimeSignature, key.KeySignature, clef.Clef) diff --git a/music21/style.py b/music21/style.py index d4f6fb1f4b..dfdb43db8b 100644 --- a/music21/style.py +++ b/music21/style.py @@ -61,7 +61,6 @@ class Style(ProtoM21Object): >>> st.absoluteX = 20.4 >>> st.absoluteX 20.4 - ''' _DOC_ATTR: dict[str, str] = { 'hideObjectOnPrint': ''' diff --git a/music21/tempo.py b/music21/tempo.py index bba4a40a8e..0e2a6db74e 100644 --- a/music21/tempo.py +++ b/music21/tempo.py @@ -107,7 +107,6 @@ def convertTempoByReferent( >>> tempo.convertTempoByReferent(60, 1.5, common.opFrac(1/3)) 270.0 - ''' # find duration in seconds of quarter length srcDurPerBeat = 60 / numberSrc @@ -754,7 +753,6 @@ def getEquivalentByReferent(self, referent): >>> mm1.getEquivalentByReferent('longa') - ''' if common.isNum(referent): # assume quarter length quarterLength = referent @@ -1157,7 +1155,6 @@ def setEqualityByReferent(self, side=None, referent=1.0): => - ''' if side is None: if self._oldMetronome is None: diff --git a/music21/text.py b/music21/text.py index 92426e02f3..7675f7d946 100644 --- a/music21/text.py +++ b/music21/text.py @@ -291,7 +291,6 @@ class TextBox(base.Music21Object): .. image:: images/textBoxes-01.* :width: 600 - ''' _styleClass = style.TextStyle classSortOrder = -31 # text expressions are -30 diff --git a/music21/tree/core.py b/music21/tree/core.py index 5de72e4654..ec6980294d 100644 --- a/music21/tree/core.py +++ b/music21/tree/core.py @@ -378,7 +378,6 @@ def update(self) -> None: child notes of `n` then this would not fix that node's balance/height. This method assumes that children have the correct information and only updates the information for this node. - ''' leftHeight = self.leftChild.height if self.leftChild else -1 rightHeight = self.rightChild.height if self.rightChild else -1 diff --git a/music21/tree/fromStream.py b/music21/tree/fromStream.py index 46ab6da851..f1bcb407b5 100644 --- a/music21/tree/fromStream.py +++ b/music21/tree/fromStream.py @@ -207,7 +207,6 @@ def asTree( >>> etFlatNotes = tree.fromStream.asTree(score, flatten=True, classList=(note.Note,)) >>> etFlatNotes to 8.0) > - ''' def recurseGetTreeByClass( innerStream, diff --git a/music21/tree/spans.py b/music21/tree/spans.py index 36ed0e2731..4c34d892be 100644 --- a/music21/tree/spans.py +++ b/music21/tree/spans.py @@ -560,7 +560,6 @@ def pitches(self) -> tuple[pitch.Pitch, ...]: True >>> pts.pitches is c.pitches False - ''' return self.element.pitches @@ -623,7 +622,6 @@ def canMerge(self, other: Timespan) -> tuple[bool, str]: > >>> print(timespan_twoWrong.part) - ''' can, message = super().canMerge(other) if can is True: diff --git a/music21/tree/trees.py b/music21/tree/trees.py index 2cae3ecf14..c9d5b4f304 100644 --- a/music21/tree/trees.py +++ b/music21/tree/trees.py @@ -400,7 +400,6 @@ def __iter__(self): ... - ''' for node in self.iterNodes(): yield node.payload @@ -736,7 +735,6 @@ def _getPositionsFromElements(self, elements): In an ElementTree, this will be a list of .sortTuple() calls. In an OffsetTree, this will be a list of .offset calls - ''' return [self.getPositionFromElementUnsafe(el) for el in elements] @@ -1077,7 +1075,6 @@ def __iter__(self): ... - ''' for node in self.iterNodes(): for el in node.payload: @@ -1381,7 +1378,6 @@ def overlapTimePoints(self, includeStopPoints=False, returnVerticality=False): >>> otp = scoreOffsetTree.overlapTimePoints(returnVerticality=True) >>> otp[0] {0.5: } - ''' checkPoints = self.allOffsets() if includeStopPoints is False else self.allTimePoints() overlaps = [] diff --git a/music21/variant.py b/music21/variant.py index f365d25af8..552b748a6a 100644 --- a/music21/variant.py +++ b/music21/variant.py @@ -1327,7 +1327,6 @@ def mergePartAsOssia(mainPart, ossiaPart, ossiaName, {0.0} {2.0} ... - ''' if inPlace: returnObj = mainPart @@ -1588,7 +1587,6 @@ def refineVariant(s, sVariant, *, inPlace=False): {1.0} {2.0} {3.0} - ''' # stream that will be returned if sVariant not in s.getElementsByClass(Variant): @@ -1682,7 +1680,6 @@ def _mergeVariantMeasureStreamsCarefully(streamX, streamY, variantName, *, inPla ''' There seem to be some problems with this function, and it isn't well tested. It is not recommended to use it at this time. - ''' # stream that will be returned if inPlace: @@ -1922,7 +1919,6 @@ def _diffScore(measureX, measureY): >>> m2.append([note.Note('e'), note.Note('f'), note.Note('g#'), note.Note('a')]) >>> variant._diffScore(m1, m2) 0.4... - ''' hashes = getMeasureHashes([measureX, measureY]) if hashes[0] == hashes[1]: @@ -1957,7 +1953,6 @@ def _getRegionsFromStreams(streamX, streamY): ('replace', 1, 3, 1, 2), ('equal', 3, 6, 2, 5), ('insert', 6, 6, 5, 6)] - ''' hashesX = getMeasureHashes(streamX) hashesY = getMeasureHashes(streamY) @@ -2170,7 +2165,6 @@ def _generateVariant(noteList, originStream, start, variantName=None): >>> v.groups ['paris'] - ''' returnVariant = Variant() for n in noteList: @@ -2230,7 +2224,6 @@ def makeAllVariantsReplacements(streamWithVariants, (4.0, 'replacement', 4.0, 4.0) (12.0, 'elongation', 4.0, 12.0) (20.0, 'deletion', 8.0, 4.0) - ''' if inPlace: diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py index 0387ac7717..61060b9a5d 100644 --- a/music21/voiceLeading.py +++ b/music21/voiceLeading.py @@ -1684,7 +1684,6 @@ def getObjectsByClass(self, classFilterList, partNums=None): , ] >>> vs1.getObjectsByClass('Note', [1, 2]) [, , ] - ''' if not common.isIterable(classFilterList): classFilterList = [classFilterList] @@ -1916,7 +1915,6 @@ def hasPassingTone(self, partNumToIdentify, unaccentedOnly=False): True >>> vt.hasPassingTone(1) False - ''' if partNumToIdentify in self.tnlsDict: ret = self.tnlsDict[partNumToIdentify].couldBePassingTone() @@ -2082,7 +2080,6 @@ class ThreeNoteLinearSegment(NNoteLinearSegment): >>> defaults.pitchOctave 4 - ''' _DOC_ORDER = ['couldBePassingTone', 'couldBeDiatonicPassingTone', diff --git a/music21/volume.py b/music21/volume.py index 6b08dcd516..85f81ba615 100644 --- a/music21/volume.py +++ b/music21/volume.py @@ -231,7 +231,6 @@ def getRealized( >>> s.notes[7].volume.velocityIsRelative = False >>> s.notes[7].volume.getRealized() 0.1574803... - ''' # velocityIsRelative might be best set at import. e.g., from MIDI, # velocityIsRelative is False, but in other applications, it may not From b0720f53ec3c17f6b85dc0eee7eab97eac49d424 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:17:09 -1000 Subject: [PATCH 087/312] Align every continuation line to its opening bracket 383 E127/E128 continuation lines across 58 modules sat at an indent that had drifted from the bracket they belong to -- most of them badly over-indented, like serial.historicalDict's rows and scale's IntervalNetwork edge dicts, where the second line of each entry started fifteen columns right of the first. autopep8 --select=E12 did the reindent; `pycodestyle --select=E12 music21` is now silent. Four lines had to be rewrapped by hand: three that the new indent pushed past 100 characters (analysis/reduction, roman, stream/makeNotation) and one string in musicxml/test_xmlToM21 that had been hiding under an indent of 8. Also annotates the three graph.axis __init__s that returned mypy's annotation-unchecked note, which their bodies' inline annotations had earned. repeat.simplify and stream.filters.OffsetFilter.__init__ still raise it; both need a real signature rather than a bare `-> None` (simplify returns a Stream unless inPlace), so they are left alone. AI-assisted (Claude) --- music21/abcFormat/__init__.py | 12 +- music21/abcFormat/testFiles.py | 10 +- music21/abcFormat/translate.py | 4 +- music21/alpha/analysis/hasher.py | 6 +- music21/alpha/analysis/ornamentRecognizer.py | 2 +- music21/analysis/patel.py | 2 +- music21/analysis/reduction.py | 19 +- music21/base.py | 4 +- music21/braille/basic.py | 4 +- music21/braille/segment.py | 8 +- music21/braille/test.py | 10 +- music21/braille/translate.py | 32 +-- music21/dynamics.py | 34 +-- music21/expressions.py | 20 +- music21/features/base.py | 14 +- music21/figuredBass/notation.py | 4 +- music21/figuredBass/realizer.py | 8 +- music21/figuredBass/segment.py | 20 +- music21/freezeThaw.py | 6 +- music21/graph/axis.py | 6 +- music21/graph/plot.py | 28 +-- music21/humdrum/harmParser.py | 16 +- music21/humdrum/testFiles.py | 2 +- .../naturalLanguageObjects.py | 2 +- music21/mei/test_base.py | 6 +- music21/metadata/primitives.py | 6 +- music21/musedata/__init__.py | 4 +- music21/musedata/translate.py | 2 +- music21/musicxml/partStaffExporter.py | 8 +- music21/musicxml/test_xmlToM21.py | 9 +- music21/musicxml/xmlToM21.py | 8 +- music21/omr/evaluators.py | 2 +- music21/pitch.py | 14 +- music21/roman.py | 15 +- music21/romanText/translate.py | 2 +- music21/scale/__init__.py | 212 +++++++++--------- music21/scale/intervalNetwork.py | 14 +- music21/scale/scala/__init__.py | 20 +- music21/scale/test_scale_main.py | 6 +- music21/search/lyrics.py | 2 +- music21/search/serial.py | 6 +- music21/serial.py | 150 ++++++------- music21/sieve.py | 4 +- music21/spanner.py | 22 +- music21/stream/base.py | 8 +- music21/stream/makeNotation.py | 10 +- music21/stream/tests.py | 88 ++++---- music21/test/multiprocessTest.py | 2 +- music21/test/testRunner.py | 4 +- music21/test/test_chord.py | 18 +- music21/test/test_metadata.py | 2 +- music21/test/test_note.py | 2 +- music21/test/test_pitch.py | 2 +- music21/text.py | 2 +- music21/tree/verticality.py | 2 +- music21/variant.py | 58 ++--- music21/voiceLeading.py | 46 ++-- music21/volpiano.py | 4 +- 58 files changed, 521 insertions(+), 512 deletions(-) diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py index 4adcab5256..9c52a79554 100644 --- a/music21/abcFormat/__init__.py +++ b/music21/abcFormat/__init__.py @@ -2506,12 +2506,12 @@ def tokenize(self, strSrc: str) -> None: # H is fermata # . dot may be staccato, but should be attached to pitch if self.currentCollectStr in ('w', 'u', 'v', 'v.', 'h', 'H', 'vk', - 'uk', 'U', '~', - '.', '=', 'V', 'v.', 'S', 's', - 'i', 'I', 'ui', 'u.', 'Q', 'Hy', 'Hx', - 'r', 'm', 'M', 'n', 'N', 'o', 'O', 'P', - 'l', 'L', 'R', - 'y', 'T', 't', 'x', 'Z'): + 'uk', 'U', '~', + '.', '=', 'V', 'v.', 'S', 's', + 'i', 'I', 'ui', 'u.', 'Q', 'Hy', 'Hx', + 'r', 'm', 'M', 'n', 'N', 'o', 'O', 'P', + 'l', 'L', 'R', + 'y', 'T', 't', 'x', 'Z'): pass # these are bad chords, or other problematic notations like # "D.C."x diff --git a/music21/abcFormat/testFiles.py b/music21/abcFormat/testFiles.py index 8cbd76cad9..eee14079f7 100644 --- a/music21/abcFormat/testFiles.py +++ b/music21/abcFormat/testFiles.py @@ -633,11 +633,11 @@ # ------------------------------------------------------------------------------ ALL = [fyrareprisarn, mysteryReel, fullRiggedShip, aleIsDear, kitchGirl, - williamAndNancy, morrisonsJig, hectorTheHero, kingOfTheFairies, - sicutRosa, theAleWifesDaughter, theBeggerBoy, theBattleOfTheSnaBas, - draughtOfAle, - valentineJigg, - testPrimitive, testPrimitivePolyphonic, testPrimitiveTuplet + williamAndNancy, morrisonsJig, hectorTheHero, kingOfTheFairies, + sicutRosa, theAleWifesDaughter, theBeggerBoy, theBattleOfTheSnaBas, + draughtOfAle, + valentineJigg, + testPrimitive, testPrimitivePolyphonic, testPrimitiveTuplet ] diff --git a/music21/abcFormat/translate.py b/music21/abcFormat/translate.py index 69c943a272..6e245f12a6 100644 --- a/music21/abcFormat/translate.py +++ b/music21/abcFormat/translate.py @@ -1044,9 +1044,9 @@ def testNoChord(self): score = harmony.realizeChordSymbolDurations(score) self.assertEqual(8, score.getElementsByClass(harmony.ChordSymbol) - .last().quarterLength) + .last().quarterLength) self.assertEqual(4, score.getElementsByClass(harmony.ChordSymbol) - .first().quarterLength) + .first().quarterLength) def testAbcKeyImport(self): from music21 import abcFormat diff --git a/music21/alpha/analysis/hasher.py b/music21/alpha/analysis/hasher.py index 2b271010a9..a89f0df9b1 100644 --- a/music21/alpha/analysis/hasher.py +++ b/music21/alpha/analysis/hasher.py @@ -416,16 +416,16 @@ def hashStream(self, s): if self.hashChordsAsNotes: for n in elt: singleNoteHash = [self.hashingFunctions[hashProperty](n, thisChord=elt) - for hashProperty in self.tupleList] + for hashProperty in self.tupleList] self.addHashToFinalHash(singleNoteHash, finalHash, n) elif self.hashChordsAsChords: singleNoteHash = [self.hashingFunctions[hashProperty](None, thisChord=elt) - for hashProperty in self.tupleList] + for hashProperty in self.tupleList] self.addHashToFinalHash(singleNoteHash, finalHash, elt) else: singleNoteHash = [self.hashingFunctions[hashProperty](elt) - for hashProperty in self.tupleList] + for hashProperty in self.tupleList] self.addHashToFinalHash(singleNoteHash, finalHash, elt) # TODO: don't finalHash back and forth, return it in the smaller functions return finalHash diff --git a/music21/alpha/analysis/ornamentRecognizer.py b/music21/alpha/analysis/ornamentRecognizer.py index 029f35b968..1364d2572f 100644 --- a/music21/alpha/analysis/ornamentRecognizer.py +++ b/music21/alpha/analysis/ornamentRecognizer.py @@ -543,7 +543,7 @@ def testRecognizeTrill(self): t3Notes = stream.Stream() # C B C B C D E F t3Notes.append( [t3n1, t3n2, deepcopy(t3n1), deepcopy(t3n2), deepcopy(t3n1), - nachschlagN1, nachschlagN2, nachschlagN3] + nachschlagN1, nachschlagN2, nachschlagN3] ) testConditions.append( diff --git a/music21/analysis/patel.py b/music21/analysis/patel.py index 499fed7ea7..54d019aa9c 100644 --- a/music21/analysis/patel.py +++ b/music21/analysis/patel.py @@ -92,7 +92,7 @@ def melodicIntervalVariability(streamForAnalysis, **skipKeywords): totalElements = len(intervalStream) if totalElements < 2: # this is correct. raise ValueError('need at least three notes to have ' - + 'a std-deviation of intervals (and thus a MIV)') + + 'a std-deviation of intervals (and thus a MIV)') # summation = 0 semitoneList = [myInt.chromatic.undirected for myInt in intervalStream] return 100 * (stdev(semitoneList) / mean(semitoneList)) diff --git a/music21/analysis/reduction.py b/music21/analysis/reduction.py index 5c9dfbaddb..e4bf27dcbf 100644 --- a/music21/analysis/reduction.py +++ b/music21/analysis/reduction.py @@ -733,9 +733,14 @@ def _dynamicToWeight(targets): offsetEnd = offsetStart + ds['span'] # get all targets within the contiguous region # e.g., Dynamics objects - match = flatRef.getElementsByOffset(offsetStart, offsetEnd, - includeEndBoundary=True, mustFinishInSpan=False, - mustBeginInSpan=True).getElementsByClass(target).stream() + inRegion = flatRef.getElementsByOffset( + offsetStart, + offsetEnd, + includeEndBoundary=True, + mustFinishInSpan=False, + mustBeginInSpan=True, + ) + match = inRegion.getElementsByClass(target).stream() # environLocal.printDebug(['matched elements', target, match]) # extend duration of all found dynamics match.extendDuration(target, inPlace=True) @@ -1251,7 +1256,7 @@ def testPartReductionE(self): s.insert(0, p2) # s.show() pr = analysis.reduction.PartReduction(s, fillByMeasure=True, - segmentByTarget=False, normalize=False) + segmentByTarget=False, normalize=False) pr.process() target = pr.getGraphHorizontalBarWeightedData() match = [(0, [[0.0, 4.0, 0.178571428571, '#666666'], @@ -1264,7 +1269,7 @@ def testPartReductionE(self): self._matchWeightedData(match, target) pr = analysis.reduction.PartReduction(s, fillByMeasure=False, - segmentByTarget=True, normalize=False) + segmentByTarget=True, normalize=False) pr.process() target = pr.getGraphHorizontalBarWeightedData() match = [(0, [[0.0, 2.0, 0.05, '#666666'], @@ -1281,7 +1286,7 @@ def testPartReductionE(self): pr = analysis.reduction.PartReduction(s, fillByMeasure=False, - segmentByTarget=False) + segmentByTarget=False) pr.process() target = pr.getGraphHorizontalBarWeightedData() # print(target) @@ -1295,7 +1300,7 @@ def testPartReductionE(self): pr = analysis.reduction.PartReduction(s, fillByMeasure=True, - segmentByTarget=True) + segmentByTarget=True) pr.process() target = pr.getGraphHorizontalBarWeightedData() match = [(0, [[0.0, 2.0, 0.3888888888888, '#666666'], diff --git a/music21/base.py b/music21/base.py index 2d0addf2ea..4878c13be5 100644 --- a/music21/base.py +++ b/music21/base.py @@ -2160,7 +2160,7 @@ def contextSites( offsetAdjustedCsTuple = ContextSortTuple( derivedCsTuple.site, derivedCsTuple.offset.modify(offset=derivedCsTuple[1].offset - + offsetAppend), + + offsetAppend), derivedCsTuple.recurseType) if returnSortTuples: yield offsetAdjustedCsTuple @@ -2769,7 +2769,7 @@ def sortTuple(self, insertIndex = 0 return SortTuple(atEnd, offset, self.priority, - self.classSortOrder, isNotGrace, insertIndex) + self.classSortOrder, isNotGrace, insertIndex) # ----------------------------------------------------------------- @property diff --git a/music21/braille/basic.py b/music21/braille/basic.py index acbca7bf5b..c9218a3be9 100644 --- a/music21/braille/basic.py +++ b/music21/braille/basic.py @@ -739,8 +739,8 @@ def noteToBraille( f'transcriber-added {symbols["transcriber-added_sign"]}') tupletTrans = symbols['tuplet_prefix'] # dots 4,5,6 tupletTrans += numberToBraille(allTuplets[0].numberNotesActual, - withNumberSign=False, - lower=True) + withNumberSign=False, + lower=True) tupletTrans += symbols['dot'] noteTrans.append(tupletTrans) music21Note.editorial.brailleEnglish.append( diff --git a/music21/braille/segment.py b/music21/braille/segment.py index 739c9fb824..eb493b4a65 100644 --- a/music21/braille/segment.py +++ b/music21/braille/segment.py @@ -826,8 +826,8 @@ def extractNoteGrouping(self) -> None: # hence -- let us split this noteGrouping into two noteGroupings. try: bngA, bngB = self.splitNoteGroupingAndTranscribe(noteGrouping, - showLeadingOctave, - addSpace) + showLeadingOctave, + addSpace) self.currentLine.append(bngA, addSpace=addSpace) self.addToNewLine(bngB) except BrailleSegmentException: @@ -1072,8 +1072,8 @@ def fixOneArticulation(artic, music21NoteStart, allNotes, noteIndexStart): newSegment = self.consolidate() noteGroupings = [newSegment[gpKey] - for gpKey in newSegment.keys() - if gpKey.affinity == Affinity.NOTEGROUP] + for gpKey in newSegment.keys() + if gpKey.affinity == Affinity.NOTEGROUP] for noteGrouping in noteGroupings: allNotes_outer = [n for n in noteGrouping if isinstance(n, note.Note)] for noteIndexStart_outer in range(len(allNotes_outer)): diff --git a/music21/braille/test.py b/music21/braille/test.py index ab2c0468c4..24e7fc690e 100644 --- a/music21/braille/test.py +++ b/music21/braille/test.py @@ -1848,7 +1848,7 @@ def test_example05_3(self): def test_example05_4(self): self.methodArgs = {'suppressOctaveMarks': True} bm = converter.parse('tinynotation: 4/4 E2 F2 G1 E2 D2 C1 D2 E2 F2 E2 D1 r1 ' - 'C2 D2 E1 F2 G2 A1 G2 F2 E2 D2 C1 r1') + 'C2 D2 E1 F2 G2 A1 G2 F2 E2 D2 C1 r1') bm.makeNotation(inPlace=True, cautionaryNotImmediateRepeat=False) self.s = bm self.e = ''' @@ -3622,9 +3622,9 @@ def test_example08_7a(self): (-4, '4/4', 'Andante', '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠁⠝⠙⠁⠝⠞⠑⠲⠀⠼⠙⠣⠼⠙⠲⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'), (3, '3/8', 'Con moto', '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠉⠕⠝⠀⠍⠕⠞⠕⠲⠀⠩⠩⠩⠼⠉⠦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀'), (None, '4/4', 'Andante cantabile', - '⠀⠀⠀⠀⠀⠀⠀⠀⠠⠁⠝⠙⠁⠝⠞⠑⠀⠉⠁⠝⠞⠁⠃⠊⠇⠑⠲⠀⠼⠙⠲⠀⠀⠀⠀⠀⠀⠀⠀⠀'), + '⠀⠀⠀⠀⠀⠀⠀⠀⠠⠁⠝⠙⠁⠝⠞⠑⠀⠉⠁⠝⠞⠁⠃⠊⠇⠑⠲⠀⠼⠙⠲⠀⠀⠀⠀⠀⠀⠀⠀⠀'), (2, '7/8', 'Very brightly', - '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠧⠑⠗⠽⠀⠃⠗⠊⠛⠓⠞⠇⠽⠲⠀⠩⠩⠼⠛⠦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀') + '⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⠧⠑⠗⠽⠀⠃⠗⠊⠛⠓⠞⠇⠽⠲⠀⠩⠩⠼⠛⠦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀') ] for ks, ts, tt, r in results: if ks is not None: @@ -3637,7 +3637,7 @@ def test_example08_8(self): from music21.braille import basic self.assertEqual(basic.metronomeMarkToBraille( tempo.MetronomeMark(number=80, - referent=note.Note(type='half'))), '⠝⠶⠼⠓⠚') + referent=note.Note(type='half'))), '⠝⠶⠼⠓⠚') def test_example08_9(self): from music21.braille.basic import transcribeHeading @@ -3671,7 +3671,7 @@ def test_drill08_1(self): bm = converter.parse(''' tinynotation: 4/4 a2 g8 f8 e4 d4 e4 f8 g8 a4 g4 c'8 b8 a4 g4 f4. e8 d2 c4 e4 a4 e'4 e'4 d'4 c'8 b8 a4 a'4 g'8 f'8 e'4 d'4 c'4 a8 b8 c'2''', - makeNotation=False) + makeNotation=False) bm.replace(bm.getElementsByClass(meter.TimeSignature).first(), meter.TimeSignature('c')) bm.insert(0, tempo.TempoText('Andante maestoso')) bm.insert(0, tempo.MetronomeMark(number=92, referent=note.Note(type='quarter'))) diff --git a/music21/braille/translate.py b/music21/braille/translate.py index 1d693f551b..d7528ad4e3 100644 --- a/music21/braille/translate.py +++ b/music21/braille/translate.py @@ -244,22 +244,22 @@ def streamToBraille(music21Stream: stream.Measure|stream.Part|stream.Score|strea ''' if isinstance(music21Stream, stream.Part): return partToBraille(music21Stream, - inPlace=inPlace, - debug=debug, - cancelOutgoingKeySig=cancelOutgoingKeySig, - descendingChords=descendingChords, - dummyRestLength=dummyRestLength, - maxLineLength=maxLineLength, - segmentBreaks=segmentBreaks, - showClefSigns=showClefSigns, - showFirstMeasureNumber=showFirstMeasureNumber, - showHand=showHand, - showHeading=showHeading, - showLongSlursAndTiesTogether=showLongSlursAndTiesTogether, - showShortSlursAndTiesTogether=showShortSlursAndTiesTogether, - slurLongPhraseWithBrackets=slurLongPhraseWithBrackets, - suppressOctaveMarks=suppressOctaveMarks, - upperFirstInNoteFingering=upperFirstInNoteFingering, + inPlace=inPlace, + debug=debug, + cancelOutgoingKeySig=cancelOutgoingKeySig, + descendingChords=descendingChords, + dummyRestLength=dummyRestLength, + maxLineLength=maxLineLength, + segmentBreaks=segmentBreaks, + showClefSigns=showClefSigns, + showFirstMeasureNumber=showFirstMeasureNumber, + showHand=showHand, + showHeading=showHeading, + showLongSlursAndTiesTogether=showLongSlursAndTiesTogether, + showShortSlursAndTiesTogether=showShortSlursAndTiesTogether, + slurLongPhraseWithBrackets=slurLongPhraseWithBrackets, + suppressOctaveMarks=suppressOctaveMarks, + upperFirstInNoteFingering=upperFirstInNoteFingering, ) elif isinstance(music21Stream, stream.Measure): return measureToBraille(music21Stream, diff --git a/music21/dynamics.py b/music21/dynamics.py index a48deb15c8..b4bf812ea4 100644 --- a/music21/dynamics.py +++ b/music21/dynamics.py @@ -27,27 +27,27 @@ shortNames = ['pppppp', 'ppppp', 'pppp', 'ppp', 'pp', 'p', 'mp', - 'mf', 'f', 'fp', 'sf', 'ff', 'fff', 'ffff', 'fffff', 'ffffff'] + 'mf', 'f', 'fp', 'sf', 'ff', 'fff', 'ffff', 'fffff', 'ffffff'] longNames = {'ppp': 'pianississimo', - 'pp': 'pianissimo', - 'p': 'piano', - 'mp': 'mezzopiano', - 'mf': 'mezzoforte', - 'f': 'forte', - 'fp': 'fortepiano', - 'sf': 'sforzando', - 'ff': 'fortissimo', - 'fff': 'fortississimo'} + 'pp': 'pianissimo', + 'p': 'piano', + 'mp': 'mezzopiano', + 'mf': 'mezzoforte', + 'f': 'forte', + 'fp': 'fortepiano', + 'sf': 'sforzando', + 'ff': 'fortissimo', + 'fff': 'fortississimo'} # could be really useful for automatic description of musical events englishNames = {'ppp': 'extremely soft', - 'pp': 'very soft', - 'p': 'soft', - 'mp': 'moderately soft', - 'mf': 'moderately loud', - 'f': 'loud', - 'ff': 'very loud', - 'fff': 'extremely loud'} + 'pp': 'very soft', + 'p': 'soft', + 'mp': 'moderately soft', + 'mf': 'moderately loud', + 'f': 'loud', + 'ff': 'very loud', + 'fff': 'extremely loud'} def dynamicStrFromDecimal(n): diff --git a/music21/expressions.py b/music21/expressions.py index 95ec81cf24..fbecb96bf7 100644 --- a/music21/expressions.py +++ b/music21/expressions.py @@ -582,8 +582,8 @@ def realize( keySig: key.KeySignature|None = None, inPlace: bool = False ) -> tuple[list[note.Note|note.Unpitched], - note.Note|note.Unpitched|None, - list[note.Note|note.Unpitched]]: + note.Note|note.Unpitched|None, + list[note.Note|note.Unpitched]]: ''' Subclassable method call that takes a sourceObject and optional keySig and returns a three-element tuple of a list of notes before the @@ -911,8 +911,8 @@ def realize( keySig: key.KeySignature|None = None, inPlace: bool = False ) -> tuple[list[note.Note|note.Unpitched], - note.Note|note.Unpitched|None, - list[note.Note|note.Unpitched]]: + note.Note|note.Unpitched|None, + list[note.Note|note.Unpitched]]: ''' Realize a mordent. @@ -1576,8 +1576,8 @@ def realize( keySig: key.KeySignature|None = None, inPlace: bool = False ) -> tuple[list[note.Note|note.Unpitched], - note.Note|note.Unpitched|None, - list[note.Note|note.Unpitched]]: + note.Note|note.Unpitched|None, + list[note.Note|note.Unpitched]]: ''' Realize a trill. @@ -2449,8 +2449,8 @@ def realize( keySig: key.KeySignature|None = None, inPlace: bool = False ) -> tuple[list[note.Note|note.Unpitched], - note.Note|note.Unpitched|None, - list[note.Note|note.Unpitched]]: + note.Note|note.Unpitched|None, + list[note.Note|note.Unpitched]]: ''' Realize an appoggiatura. @@ -2597,8 +2597,8 @@ def realize( keySig: key.KeySignature|None = None, inPlace: bool = False ) -> tuple[list[note.Note|note.Unpitched], - note.Note|note.Unpitched|None, - list[note.Note|note.Unpitched]]: + note.Note|note.Unpitched|None, + list[note.Note|note.Unpitched]]: ''' Realize the ornament. diff --git a/music21/features/base.py b/music21/features/base.py index 32e5e44812..d428c8228e 100644 --- a/music21/features/base.py +++ b/music21/features/base.py @@ -1355,11 +1355,11 @@ def testStreamFormsA(self): '3-8': 2, '3-2': 2}) self.assertEqual(di['chordify.flat.getElementsByClass(Chord).typesHistogram'], - {'isMinorTriad': 6, 'isAugmentedTriad': 0, - 'isTriad': 34, 'isSeventh': 0, 'isDiminishedTriad': 4, - 'isDiminishedSeventh': 0, 'isIncompleteMajorTriad': 26, - 'isHalfDiminishedSeventh': 0, 'isMajorTriad': 24, - 'isDominantSeventh': 0, 'isIncompleteMinorTriad': 16}) + {'isMinorTriad': 6, 'isAugmentedTriad': 0, + 'isTriad': 34, 'isSeventh': 0, 'isDiminishedTriad': 4, + 'isDiminishedSeventh': 0, 'isIncompleteMajorTriad': 26, + 'isHalfDiminishedSeventh': 0, 'isMajorTriad': 24, + 'isDominantSeventh': 0, 'isIncompleteMinorTriad': 16}) self.assertEqual(di['flat.notes.quarterLengthHistogram'], {0.5: 116, 1.0: 39, 1.5: 27, 2.0: 31, 3.0: 2, 4.0: 3, @@ -1650,8 +1650,8 @@ def x_testRegionClassificationJSymbolicB(self): # pragma: no cover # features common to both collections featureExtractors = features.extractorsById( ['r31', 'r32', 'r33', 'r34', 'r35', 'p1', 'p2', 'p3', 'p4', - 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', - 'p14', 'p15', 'p16', 'p19', 'p20', 'p21'], 'jSymbolic') + 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', + 'p14', 'p15', 'p16', 'p19', 'p20', 'p21'], 'jSymbolic') # first bundle ds = features.DataSet(classLabel='Region') diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index e679f41d91..99681cc1ac 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -24,7 +24,7 @@ ''' shorthandNotation: dict[tuple[int|None, ...], tuple[int, ...]] = { - (None,): (5, 3), + (None,): (5, 3), (5,): (5, 3), (6,): (6, 3), (7,): (7, 5, 3), @@ -35,7 +35,7 @@ (4, 3): (6, 4, 3), (4, 2): (6, 4, 2), (2,): (6, 4, 2), - } +} prefixes = ['+', '#', '++', '##'] suffixes = ['\\'] diff --git a/music21/figuredBass/realizer.py b/music21/figuredBass/realizer.py index 7e2c0a2376..69e92bdb8d 100644 --- a/music21/figuredBass/realizer.py +++ b/music21/figuredBass/realizer.py @@ -367,16 +367,16 @@ def retrieveSegments( previousBassNote = bassNotes[bassNoteIndex] bassNote = t.cast(note.Note, currentMapping[allKeys[0]][-1]) previousSegment = segment.OverlaidSegment(bassNote, bassNote.editorial.notationString, - self._fbScale, - fbRules, numParts, maxPitch) + self._fbScale, + fbRules, numParts, maxPitch) previousSegment.quarterLength = previousBassNote.quarterLength segmentList.append(previousSegment) for k in allKeys[1:]: (startTime, unused_endTime) = k bassNote = t.cast(note.Note, currentMapping[k][-1]) currentSegment = segment.OverlaidSegment(bassNote, bassNote.editorial.notationString, - self._fbScale, - fbRules, numParts, maxPitch) + self._fbScale, + fbRules, numParts, maxPitch) for partNumber in range(1, len(currentMapping[k])): upperPitch = t.cast(note.Note, currentMapping[k][partNumber - 1]) currentSegment.fbRules._partPitchLimits.append((partNumber, upperPitch)) diff --git a/music21/figuredBass/segment.py b/music21/figuredBass/segment.py index ac7b183890..5bf60d4818 100644 --- a/music21/figuredBass/segment.py +++ b/music21/figuredBass/segment.py @@ -211,16 +211,16 @@ def singlePossibilityRules( singlePossibRules = [ (fbRules.forbidIncompletePossibilities, - possibility.isIncomplete, - False, - [self.pitchNamesInChord]), + possibility.isIncomplete, + False, + [self.pitchNamesInChord]), (True, - possibility.upperPartsWithinLimit, - True, - [fbRules.upperPartsMaxSemitoneSeparation]), + possibility.upperPartsWithinLimit, + True, + [fbRules.upperPartsMaxSemitoneSeparation]), (fbRules.forbidVoiceCrossing, - possibility.voiceCrossing, - False) + possibility.voiceCrossing, + False) ] return singlePossibRules @@ -856,8 +856,8 @@ def _resolveOrdinarySegment( correctB = segmentB.allCorrectSinglePossibilities() correctAB = itertools.product(correctA, correctB) return filter(lambda possibAB: self._isCorrectConsecutivePossibility(possibA=possibAB[0], - possibB=possibAB[1]), - correctAB) + possibB=possibAB[1]), + correctAB) def _resolveSpecialSegment( self, diff --git a/music21/freezeThaw.py b/music21/freezeThaw.py index c8f25caf8b..a4aa36df62 100644 --- a/music21/freezeThaw.py +++ b/music21/freezeThaw.py @@ -1206,9 +1206,9 @@ def testPickleMidi(self): from music21 import note a = str(common.getSourceFilePath() - / 'midi' - / 'testPrimitive' - / 'test03.mid') + / 'midi' + / 'testPrimitive' + / 'test03.mid') # a = 'https://github.com/ELVIS-Project/vis/raw/master/test_corpus/prolationum-sanctus.midi' c = converter.parse(a) diff --git a/music21/graph/axis.py b/music21/graph/axis.py index 807b4fcea4..b2c20cb6ea 100644 --- a/music21/graph/axis.py +++ b/music21/graph/axis.py @@ -314,7 +314,7 @@ class PitchAxis(Axis): labelDefault = 'Pitch' quantities: tuple[str, ...] = ('pitchGeneric', ) - def __init__(self, client=None, axisName='x'): + def __init__(self, client=None, axisName='x') -> None: super().__init__(client, axisName) self.showOctaves: bool|t.Literal['few'] = 'few' self.showEnharmonic = True @@ -449,7 +449,7 @@ class PitchClassAxis(PitchAxis): labelDefault = 'Pitch Class' quantities: tuple[str, ...] = ('pitchClass', 'pitchclass', 'pc') - def __init__(self, client=None, axisName='x'): + def __init__(self, client=None, axisName='x') -> None: self.showOctaves: bool|t.Literal['few'] = False super().__init__(client, axisName) self.minValue = 0 @@ -1125,7 +1125,7 @@ class QuarterLengthAxis(PositionAxis): 'duration', ) - def __init__(self, client=None, axisName='x'): + def __init__(self, client=None, axisName='x') -> None: super().__init__(client, axisName) self.useLogScale: bool|int = True self.useDurationNames = False diff --git a/music21/graph/plot.py b/music21/graph/plot.py index 9b10fbbc7d..a83d8e583b 100644 --- a/music21/graph/plot.py +++ b/music21/graph/plot.py @@ -1596,7 +1596,7 @@ def testHorizontalBarPitchClassOffset(self): a = corpus.parse('bach/bwv57.8') b = HorizontalBarPitchClassOffset(a.parts[0].measures(3, 6), - title='Bach (soprano voice, mm 3-6)') + title='Bach (soprano voice, mm 3-6)') b.run() def testScatterWeightedPitchSpaceQuarterLength(self): @@ -1792,19 +1792,19 @@ def testWindowed(self, doneAction=None): doneAction=doneAction, dpi=300) b.run() self.assertEqual(b.graphLegend.data, - [ - ['Major', - [('C#', '#f0727a'), ('D', '#ffd752'), ('E', '#eeff9a'), - ('F#', '#b9f0ff'), ('A', '#bb9aff'), ('B', '#ffb5ff') - ] - ], - ['Minor', - [('c#', '#8c0e16'), ('', '#ffffff'), ('', '#ffffff'), - ('f#', '#558caa'), ('', '#ffffff'), ('b', '#9b519b') - ] - ] - ] - ) + [ + ['Major', + [('C#', '#f0727a'), ('D', '#ffd752'), ('E', '#eeff9a'), + ('F#', '#b9f0ff'), ('A', '#bb9aff'), ('B', '#ffb5ff') + ] + ], + ['Minor', + [('c#', '#8c0e16'), ('', '#ffffff'), ('', '#ffffff'), + ('f#', '#558caa'), ('', '#ffffff'), ('b', '#9b519b') + ] + ] + ] + ) def testFeatures(self): streamList = ['bach/bwv66.6', 'schoenberg/opus19/movement2', 'corelli/opus3no1/1grave'] diff --git a/music21/humdrum/harmParser.py b/music21/humdrum/harmParser.py index 7cbc8c5a5f..7f2d76e691 100644 --- a/music21/humdrum/harmParser.py +++ b/music21/humdrum/harmParser.py @@ -186,14 +186,14 @@ class HarmDefs: ''' # The definition for a harm expr harmExpression = (r'^(' - + accidental - + roots - + attribute - + intervals - + inversion - + alternative - + secondary - + r')$') + + accidental + + roots + + attribute + + intervals + + inversion + + alternative + + secondary + + r')$') class HarmParser: diff --git a/music21/humdrum/testFiles.py b/music21/humdrum/testFiles.py index 55927f7f05..421890d9c3 100644 --- a/music21/humdrum/testFiles.py +++ b/music21/humdrum/testFiles.py @@ -93,7 +93,7 @@ ) ojibway = re.sub(r'\s\s\s\s+', '\t', - r''' + r''' !! Ojibway Indian Song !! Transcribed by Frances Densmore !! No. 84 "The Sioux Follow Me" diff --git a/music21/languageExcerpts/naturalLanguageObjects.py b/music21/languageExcerpts/naturalLanguageObjects.py index 40c8caff8b..05fbd7ed28 100644 --- a/music21/languageExcerpts/naturalLanguageObjects.py +++ b/music21/languageExcerpts/naturalLanguageObjects.py @@ -211,7 +211,7 @@ def testConvertNotes(self) -> None: self.assertEqual('', repr(toNote('Heses', 'de'))) self.assertEqual('', repr(toNote('Eisis', 'de'))) self.assertEqual('', - repr(toNote('la quadruple dièse', 'fr'))) + repr(toNote('la quadruple dièse', 'fr'))) self.assertEqual('', repr(toNote('si triple bémol', 'fr'))) def testConvertChords(self) -> None: diff --git a/music21/mei/test_base.py b/music21/mei/test_base.py index cf3e8c2258..311110bd2b 100644 --- a/music21/mei/test_base.py +++ b/music21/mei/test_base.py @@ -2652,7 +2652,7 @@ def testUnit4StaffFromElement(self, mockTrans, mockClef, mockKey, mockTime, ''' # 1.) prepare elem = ETree.Element(f'{MEI_NS}staffDef', attrib={'meter.count': '1', - 'meter.unit': '3'}) + 'meter.unit': '3'}) mockTime.return_value = 'mockTime return' mockFromString.side_effect = instrument.InstrumentException # otherwise staffDefFromElement() thinks it got a real Instrument @@ -2670,7 +2670,7 @@ def testIntegration4StaffFromElement(self): ''' # 1.) prepare elem = ETree.Element(f'{MEI_NS}staffDef', attrib={'meter.count': '1', - 'meter.unit': '3'}) + 'meter.unit': '3'}) # 2.) run actual = base.staffDefFromElement(elem) @@ -2686,7 +2686,7 @@ def testStaffGrpUnit1StaffFromElement(self, mockStaffDefFE): ''' elem = ETree.Element('staffGrp') innerElems = [ETree.Element(f'{MEI_NS}staffDef', attrib={'n': str(n)}) - for n in range(4)] + for n in range(4)] for eachElem in innerElems: elem.append(eachElem) mockStaffDefFE.side_effect = lambda x, unused_y: f"processed {x.get('n')}" diff --git a/music21/metadata/primitives.py b/music21/metadata/primitives.py index 14df0e7498..3c87afcfe2 100644 --- a/music21/metadata/primitives.py +++ b/music21/metadata/primitives.py @@ -497,9 +497,9 @@ def __eq__(self, other) -> bool: False ''' return (type(self) is type(other) - and self._data == other._data - and self._dataUncertainty == other._dataUncertainty - and self.relevance == other.relevance) + and self._data == other._data + and self._dataUncertainty == other._dataUncertainty + and self.relevance == other.relevance) def _reprInternal(self) -> str: return str(self) diff --git a/music21/musedata/__init__.py b/music21/musedata/__init__.py index 966304b88c..91a60e14aa 100644 --- a/music21/musedata/__init__.py +++ b/music21/musedata/__init__.py @@ -536,8 +536,8 @@ def getDynamicObjects(self): return post # find targets from largest to smallest targets = ('ppp', 'fff', - 'pp', 'ff', 'fp', 'mp', 'mf', - 'p', 'f', 'm', 'Z', 'Zp', 'R') + 'pp', 'ff', 'fp', 'mp', 'mf', + 'p', 'f', 'm', 'Z', 'Zp', 'R') for target in targets: pos = data.find(target) if pos < 0: diff --git a/music21/musedata/translate.py b/music21/musedata/translate.py index 6f95655db5..794f7800a8 100644 --- a/music21/musedata/translate.py +++ b/music21/musedata/translate.py @@ -366,7 +366,7 @@ def testBasic(self): from music21 import common fp1 = (common.getSourceFilePath() - / 'musedata' / 'testPrimitive' / 'test01' / '01.md') + / 'musedata' / 'testPrimitive' / 'test01' / '01.md') mdw = musedata.MuseDataWork() mdw.addFile(fp1) diff --git a/music21/musicxml/partStaffExporter.py b/music21/musicxml/partStaffExporter.py index b266576db9..00e51acd6f 100644 --- a/music21/musicxml/partStaffExporter.py +++ b/music21/musicxml/partStaffExporter.py @@ -85,7 +85,7 @@ def addStaffTags( mxStaff = Element('staff') mxStaff.text = str(staffNumber) helpers.insertBeforeElements(tag, mxStaff, - tagList=['beam', 'notations', 'lyric', 'play', + tagList=['beam', 'notations', 'lyric', 'play', 'sound']) @@ -423,8 +423,8 @@ def makeDivider(inner_sourceNumber: int|str) -> Element[t.Any]: # Or, gap in measure numbers in the subsequent part: keep iterating through target if (sourceNumber is not None - and targetNumber is not None - and helpers.measureNumberComesBefore(targetNumber, sourceNumber)): + and targetNumber is not None + and helpers.measureNumberComesBefore(targetNumber, sourceNumber)): continue # sourceMeasure is not None! # Or, gap in measure numbers in target: record necessary insertions until gap is closed @@ -587,7 +587,7 @@ def isMultiAttribute(m21Class: type[M21ObjType], mxAttributes, mxStaves, tagList=['part-symbol', 'instruments', 'clef', 'staff-details', - 'transpose', 'directive', 'measure-style'] + 'transpose', 'directive', 'measure-style'] ) if multiKey and mxAttributes is not None: diff --git a/music21/musicxml/test_xmlToM21.py b/music21/musicxml/test_xmlToM21.py index b9e5047fb0..21987c6a47 100644 --- a/music21/musicxml/test_xmlToM21.py +++ b/music21/musicxml/test_xmlToM21.py @@ -813,7 +813,8 @@ def testBarException(self): def testChordSymbolException(self): MP = MeasureParser() mxHarmony = self.EL('A' - 'add') + '' + 'add') with self.assertRaisesRegex(MusicXMLImportException, 'degree-value missing'): MP.xmlToChordSymbol(mxHarmony) @@ -1548,7 +1549,7 @@ def testDirectionPosition(self): # TextExpression s = converter.parse(testPrimitive.textExpressions) positionedEls = [el for el in s.recurse() if el.hasStyleInformation - and el.style.relativeX is not None] + and el.style.relativeX is not None] self.assertEqual(len(positionedEls), 3) self.assertEqual( list(set(type(el) for el in positionedEls)), @@ -1558,7 +1559,7 @@ def testDirectionPosition(self): # Wedge s = corpus.parse('beach') positionedEls = [el for el in s.recurse() if el.hasStyleInformation - and el.style.relativeX is not None] + and el.style.relativeX is not None] self.assertEqual(len(positionedEls), 40) self.assertEqual( sorted(set(type(el) for el in positionedEls), key=repr), @@ -1617,7 +1618,7 @@ def testClearingTuplets(self): d = duration.Duration(2 / 3) self.assertEqual(len(d.tuplets), 1) mxNoteNoType = EL('D6' - '3') + '3') MP.xmlToDuration(mxNoteNoType, inputM21=d) self.assertEqual(len(d.tuplets), 0) self.assertEqual(d.linked, True) diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 6b57035eaf..ba4089cdbd 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -2245,8 +2245,8 @@ def adjustTimeAttributesFromMeasure(self, m: stream.Measure): # If the measure is overfull by a "round" amount, assume that it was intended # otherwise it was likely the result of malformed MusicXML. if (diff > 0.5 - or nearestMultiple(diff, 0.0625)[1] < tol - or nearestMultiple(diff, 1 / 12)[1] < tol): + or nearestMultiple(diff, 0.0625)[1] < tol + or nearestMultiple(diff, 1 / 12)[1] < tol): mOffsetShift = mHighestTime else: mOffsetShift = lastTimeSignatureQuarterLength @@ -5094,8 +5094,8 @@ def findM21VoiceFromXmlVoice( useVoice = self.lastVoice if useVoice is None: # pragma: no cover warnings.warn('Cannot put in an element with a missing voice tag when ' - + 'no previous voice tag was given. Assuming voice 1... ', - MusicXMLWarning, stacklevel=2) + + 'no previous voice tag was given. Assuming voice 1... ', + MusicXMLWarning, stacklevel=2) useVoice = 1 thisVoice: stream.Voice|None = None diff --git a/music21/omr/evaluators.py b/music21/omr/evaluators.py index fa4c91eb79..bb8a02cd5c 100644 --- a/music21/omr/evaluators.py +++ b/music21/omr/evaluators.py @@ -177,7 +177,7 @@ def minEditDist(self, target, source): distance[i][j] = min(distance[i - 1][j] + 1, distance[i][j - 1] + 1, distance[i - 1][j - 1] - + self.substCost(source[j - 1], target[i - 1])) + + self.substCost(source[j - 1], target[i - 1])) return distance[n][m] def getDifferences(self): diff --git a/music21/pitch.py b/music21/pitch.py index cda3b89d85..feb281a4da 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -307,9 +307,9 @@ def _convertPsToOct(ps: int|float) -> int: def _convertPsToStep( ps: int|float ) -> tuple[StepName, - Accidental, - Microtone, - int]: + Accidental, + Microtone, + int]: ''' Utility conversion; does not process internal representations. @@ -5044,7 +5044,7 @@ def set_displayStatus(newDisplayStatus: bool) -> None: otherSimultaneousPitches and cautionaryPitchClass and any(pSimult.step == self.step and pSimult.pitchClass != self.pitchClass - for pSimult in otherSimultaneousPitches) + for pSimult in otherSimultaneousPitches) ): set_displayStatus(True) return @@ -5279,9 +5279,9 @@ def set_displayStatus(newDisplayStatus: bool) -> None: # if An or A to A#: need to make sure display is set elif ((pPast.accidental is None or pPast.accidental.name == 'natural') - and acc is not None # redundant. for mypy - and pSelf.accidental is not None - and pSelf.accidental.name != 'natural'): + and acc is not None # redundant. for mypy + and pSelf.accidental is not None + and pSelf.accidental.name != 'natural'): acc.displayStatus = True setFromPitchPast = True break diff --git a/music21/roman.py b/music21/roman.py index 714f5ab4f9..52d5df146e 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -1390,8 +1390,8 @@ def romanNumeralFromChord( and chordObj.isSeventhOfType((0, 3, 7, 10))): rnString = ft.prefix + stepRoman + minorSeventhSubs[inversionString] elif (not chordHasMajorThird - and inversionString in minorMajorSeventhSubs - and chordObj.isSeventhOfType((0, 3, 7, 11))): + and inversionString in minorMajorSeventhSubs + and chordObj.isSeventhOfType((0, 3, 7, 11))): rnString = ft.prefix + stepRoman + minorMajorSeventhSubs[inversionString] elif (not noKeyGiven @@ -1434,8 +1434,9 @@ def romanNumeralFromChord( try: rn = RomanNumeral(rnString, keyObj, updatePitches=False, - # correctRNAlterationForMinor() adds cautionary - sixthMinor=Minor67Default.CAUTIONARY, seventhMinor=Minor67Default.CAUTIONARY) + # correctRNAlterationForMinor() adds cautionary + sixthMinor=Minor67Default.CAUTIONARY, + seventhMinor=Minor67Default.CAUTIONARY) except fbNotation.ModifierException as strerror: # pragma: no cover raise RomanNumeralException( 'Could not parse ' @@ -3210,7 +3211,7 @@ def _parseRNAloneAmidstAug6( and aug6type != 'It' and workingFigure[0] == '6' and (len(workingFigure) < 2 - or not workingFigure[1].isdigit())): + or not workingFigure[1].isdigit())): # Fr6 => Fr43 workingFigure = self._aug6defaultInversions[aug6type] + workingFigure[1:] @@ -3409,8 +3410,8 @@ def _updatePitches(self) -> None: for j in range(numberNotes): i = numberNotes - j - 1 thisScaleDegree = (bassScaleDegree - + t.cast(int, self.figuresNotationObj.numbers[i]) - - 1) + + t.cast(int, self.figuresNotationObj.numbers[i]) + - 1) newPitch = t.cast(pitch.Pitch, useScale.pitchFromDegree( thisScaleDegree, direction=scale.Direction.ASCENDING)) pitchName = self.figuresNotationObj.modifiers[i].modifyPitchName(newPitch.name) diff --git a/music21/romanText/translate.py b/music21/romanText/translate.py index 0add263098..c21f3b1145 100644 --- a/music21/romanText/translate.py +++ b/music21/romanText/translate.py @@ -548,7 +548,7 @@ def translateMeasureLineToken(self, measureLineToken: rtObjects.RTMeasure) -> No ''' p = self.p skipsPriorMeasures = ((measureLineToken.number[0] > self.lastMeasureNumber + 1) - and (self.previousRn is not None)) + and (self.previousRn is not None)) isSingleMeasureCopy = (len(measureLineToken.number) == 1 and measureLineToken.isCopyDefinition) isMultipleMeasureCopy = (len(measureLineToken.number) > 1) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index 27f0af1815..20ead29ffa 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -629,13 +629,13 @@ def nextPitch(self, ''' net = t.cast('intervalNetwork.IntervalNetwork', self._net) post = net.nextPitch(pitchReference=pitchReference, - nodeName=nodeName, - pitchOrigin=pitchOrigin, - direction=direction, - stepSize=stepSize, - alteredDegrees=self._alteredDegrees, - getNeighbor=getNeighbor - ) + nodeName=nodeName, + pitchOrigin=pitchOrigin, + direction=direction, + stepSize=stepSize, + alteredDegrees=self._alteredDegrees, + getNeighbor=getNeighbor + ) return copy.deepcopy(post) def getNewTonicPitch(self, @@ -1061,54 +1061,54 @@ def buildNetwork(self, mode: t.Any = None) -> None: edges = ( # ascending {'interval': 'M2', - 'connections': ( - [Terminus.LOW, 0, Direction.ASCENDING], # c to d - )}, + 'connections': ( + [Terminus.LOW, 0, Direction.ASCENDING], # c to d + )}, {'interval': 'm3', - 'connections': ( - [0, 1, Direction.ASCENDING], # d to f - )}, + 'connections': ( + [0, 1, Direction.ASCENDING], # d to f + )}, {'interval': 'M2', - 'connections': ( - [1, 2, Direction.ASCENDING], # f to g - )}, + 'connections': ( + [1, 2, Direction.ASCENDING], # f to g + )}, {'interval': 'm2', - 'connections': ( - [2, 3, Direction.ASCENDING], # g to a- - )}, + 'connections': ( + [2, 3, Direction.ASCENDING], # g to a- + )}, {'interval': 'M3', - 'connections': ( - [3, Terminus.HIGH, Direction.ASCENDING], # a- to c - )}, + 'connections': ( + [3, Terminus.HIGH, Direction.ASCENDING], # a- to c + )}, # descending {'interval': 'M2', - 'connections': ( - [Terminus.HIGH, 4, Direction.DESCENDING], # c to b- - )}, + 'connections': ( + [Terminus.HIGH, 4, Direction.DESCENDING], # c to b- + )}, {'interval': 'M2', - 'connections': ( - [4, 5, Direction.DESCENDING], # b- to a- - )}, + 'connections': ( + [4, 5, Direction.DESCENDING], # b- to a- + )}, {'interval': 'm2', - 'connections': ( - [5, 6, Direction.DESCENDING], # a- to g - )}, + 'connections': ( + [5, 6, Direction.DESCENDING], # a- to g + )}, {'interval': 'M2', - 'connections': ( - [6, 7, Direction.DESCENDING], # g to f - )}, + 'connections': ( + [6, 7, Direction.DESCENDING], # g to f + )}, {'interval': 'M2', - 'connections': ( - [7, 8, Direction.DESCENDING], # f to e- - )}, + 'connections': ( + [7, 8, Direction.DESCENDING], # f to e- + )}, {'interval': 'm2', - 'connections': ( - [8, 9, Direction.DESCENDING], # e- to d - )}, + 'connections': ( + [8, 9, Direction.DESCENDING], # e- to d + )}, {'interval': 'M2', - 'connections': ( - [9, Terminus.LOW, Direction.DESCENDING], # d to c - )}, + 'connections': ( + [9, Terminus.LOW, Direction.DESCENDING], # d to c + )}, ) self._net = intervalNetwork.IntervalNetwork( @@ -1231,33 +1231,33 @@ def buildNetwork(self, mode: t.Any = None) -> None: edges = ( # all bidirectional {'interval': 'm3', - 'connections': ( - [Terminus.LOW, 0, Direction.BI], # c to e- - )}, + 'connections': ( + [Terminus.LOW, 0, Direction.BI], # c to e- + )}, {'interval': 'M2', - 'connections': ( - [0, 1, Direction.BI], # e- to f - )}, + 'connections': ( + [0, 1, Direction.BI], # e- to f + )}, {'interval': 'M2', - 'connections': ( - [1, 3, Direction.BI], # f to g - )}, + 'connections': ( + [1, 3, Direction.BI], # f to g + )}, {'interval': 'a1', - 'connections': ( - [1, 2, Direction.BI], # f to f# - )}, + 'connections': ( + [1, 2, Direction.BI], # f to f# + )}, {'interval': 'm2', - 'connections': ( - [2, 3, Direction.BI], # f# to g - )}, + 'connections': ( + [2, 3, Direction.BI], # f# to g + )}, {'interval': 'm3', - 'connections': ( - [3, 4, Direction.BI], # g to b- - )}, + 'connections': ( + [3, 4, Direction.BI], # g to b- + )}, {'interval': 'M2', - 'connections': ( - [4, Terminus.HIGH, Direction.BI], # b- to c - )}, + 'connections': ( + [4, Terminus.HIGH, Direction.BI], # b- to c + )}, ) self._net = intervalNetwork.IntervalNetwork( @@ -1992,45 +1992,45 @@ def getScaleDegreeAndAccidentalFromPitch( # noinspection SpellCheckingInspection _solfegSyllables = {1: {-2: 'def', -1: 'de', - 0: 'do', - 1: 'di', - 2: 'dis', + 0: 'do', + 1: 'di', + 2: 'dis', }, 2: {-2: 'raf', -1: 'ra', - 0: 're', - 1: 'ri', - 2: 'ris', + 0: 're', + 1: 'ri', + 2: 'ris', }, 3: {-2: 'mef', -1: 'me', - 0: 'mi', - 1: 'mis', - 2: 'mish', + 0: 'mi', + 1: 'mis', + 2: 'mish', }, 4: {-2: 'fef', -1: 'fe', - 0: 'fa', - 1: 'fi', - 2: 'fis', + 0: 'fa', + 1: 'fi', + 2: 'fis', }, 5: {-2: 'sef', -1: 'se', - 0: 'sol', - 1: 'si', - 2: 'sis', + 0: 'sol', + 1: 'si', + 2: 'sis', }, 6: {-2: 'lef', -1: 'le', - 0: 'la', - 1: 'li', - 2: 'lis', + 0: 'la', + 1: 'li', + 2: 'lis', }, 7: {-2: 'tef', -1: 'te', - 0: 'ti', - 1: 'tis', - 2: 'tish', + 0: 'ti', + 1: 'tis', + 2: 'tish', }, } # TOO SLOW! @@ -2042,45 +2042,45 @@ def getScaleDegreeAndAccidentalFromPitch( _humdrumSolfegSyllables = { 1: {-2: 'def', -1: 'de', - 0: 'do', - 1: 'di', - 2: 'dis', + 0: 'do', + 1: 'di', + 2: 'dis', }, 2: {-2: 'raf', -1: 'ra', - 0: 're', - 1: 'ri', - 2: 'ris', + 0: 're', + 1: 'ri', + 2: 'ris', }, 3: {-2: 'mef', -1: 'me', - 0: 'mi', - 1: 'my', - 2: 'mish', + 0: 'mi', + 1: 'my', + 2: 'mish', }, 4: {-2: 'fef', -1: 'fe', - 0: 'fa', - 1: 'fi', - 2: 'fis', + 0: 'fa', + 1: 'fi', + 2: 'fis', }, 5: {-2: 'sef', -1: 'se', - 0: 'so', - 1: 'si', - 2: 'sis', + 0: 'so', + 1: 'si', + 2: 'sis', }, 6: {-2: 'lef', -1: 'le', - 0: 'la', - 1: 'li', - 2: 'lis', + 0: 'la', + 1: 'li', + 2: 'lis', }, 7: {-2: 'tef', -1: 'te', - 0: 'ti', - 1: 'ty', - 2: 'tish', + 0: 'ti', + 1: 'ty', + 2: 'tish', }, } diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 15bcf218f1..db91d356bc 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -1402,9 +1402,9 @@ def nextPitch( else: p = self.transposePitchAndApplySimplification(intervalObj.reverse(), p) pCollect = self.processAlteredNodes(alteredDegrees=alteredDegrees, - n=n, - p=p, - direction=direction) + n=n, + p=p, + direction=direction) return pCollect @@ -1588,9 +1588,9 @@ def realizeAscending( p = self.transposePitchAndApplySimplification(intervalObj, p) pCollect = p pCollect = self.processAlteredNodes(alteredDegrees=alteredDegrees, - n=n, - p=p, - direction=Direction.ASCENDING) + n=n, + p=p, + direction=Direction.ASCENDING) if attempts >= maxAttempts: raise IntervalNetworkException( @@ -2716,7 +2716,7 @@ def getPitchFromNodeDegree( # only match this generously if we are equating termini if equateTermini: if ((realizedNId in (Terminus.HIGH, Terminus.LOW)) - and (nodeTargetId.id in (Terminus.HIGH, Terminus.LOW))): + and (nodeTargetId.id in (Terminus.HIGH, Terminus.LOW))): return realizedPitch[i] # environLocal.printDebug(['getPitchFromNodeDegree() on trial', trial, ', diff --git a/music21/scale/scala/__init__.py b/music21/scale/scala/__init__.py index e96f7eb7f3..221473e185 100644 --- a/music21/scale/scala/__init__.py +++ b/music21/scale/scala/__init__.py @@ -657,21 +657,21 @@ def testScalaScaleB(self) -> None: '699.997698171', '800.909593096', '900.026096390', - '1000.020156709', - '1088.268714730', - '1200.000000000']) + '1000.020156709', + '1088.268714730', + '1200.000000000']) self.assertEqual([f'{x:.9f}' for x in ss.getAdjacentCents()], ['100.099209825', - '99.880633466', - '99.994060319', + '99.880633466', + '99.994060319', '100.134576860', - '97.936518664', + '97.936518664', '102.043324627', - '99.909374409', + '99.909374409', '100.911894925', - '99.116503294', - '99.994060319', - '88.248558022', + '99.116503294', + '99.994060319', + '88.248558022', '111.731285270']) self.assertEqual([str(x) for x in ss.getIntervalSequence()], diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 587056715c..bc3826c0fa 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -894,9 +894,9 @@ def testDerivedScaleNoOctaves(self): e = d.deriveRanked(['C', 'E', 'G'], comparisonAttribute='name') self.assertEqual(str(e), ''.join(['[(3, ), ', - '(3, ), ', - '(2, ), ', - '(2, )]']), + '(3, ), ', + '(2, ), ', + '(2, )]']), str(e) ) diff --git a/music21/search/lyrics.py b/music21/search/lyrics.py index ca0f886841..8623c1b940 100644 --- a/music21/search/lyrics.py +++ b/music21/search/lyrics.py @@ -206,7 +206,7 @@ def index(self, s: stream.Stream|None = None) -> list[IndexedLyric]: indexByIdentifier: OrderedDict[str|int, list[IndexedLyric]] = OrderedDict() iTextByIdentifier: OrderedDict[str|int, str] = OrderedDict() lastSyllabicByIdentifier: OrderedDict[str|int, - str|None] = OrderedDict() + str|None] = OrderedDict() for n in s.recurse().notes: ls: list[note.Lyric] = n.lyrics diff --git a/music21/search/serial.py b/music21/search/serial.py index d7b8cd79eb..b820fda448 100644 --- a/music21/search/serial.py +++ b/music21/search/serial.py @@ -852,9 +852,9 @@ def searchRowsOnlyInclude(self, n: note.NotRest, partNumber: int | None) -> None rowSuperset = csn.readPitchClassesFromBottom() lowerBound = max([0, len(rowSuperset) - - self.searchLength - - len(self.activeChordList[-1].pitches) - + 1]) + - self.searchLength + - len(self.activeChordList[-1].pitches) + + 1]) upperBound = min([len(self.activeChordList[0].pitches), len(rowSuperset) - self.searchLength + 1]) for j in range(lowerBound, upperBound): diff --git a/music21/serial.py b/music21/serial.py index 940696a981..b98c6f7da4 100644 --- a/music21/serial.py +++ b/music21/serial.py @@ -99,151 +99,151 @@ def _reprInternal(self): # noinspection SpellCheckingInspection historicalDict = { 'WebernOp29': ('Webern', 'Op. 29', 'Cantata I', - [3, 11, 2, 1, 5, 4, 7, 6, 10, 9, 0, 8]), + [3, 11, 2, 1, 5, 4, 7, 6, 10, 9, 0, 8]), 'WebernOp28': ('Webern', 'Op. 28', 'String Quartet', - [1, 0, 3, 2, 6, 7, 4, 5, 9, 8, 11, 10]), + [1, 0, 3, 2, 6, 7, 4, 5, 9, 8, 11, 10]), 'SchoenbergOp24Mvmt5': ('Schoenberg', 'Op. 24', 'Serenade, Mvt. 5, "Tanzscene"', - [9, 10, 0, 3, 4, 6, 5, 7, 8, 11, 1, 2]), + [9, 10, 0, 3, 4, 6, 5, 7, 8, 11, 1, 2]), 'SchoenbergOp24Mvmt4': ('Schoenberg', 'Op. 24', 'Serenade, Mvt. 4, "Sonett"', - [4, 2, 3, 11, 0, 1, 8, 6, 9, 5, 7, 10]), + [4, 2, 3, 11, 0, 1, 8, 6, 9, 5, 7, 10]), 'SchoenbergJakobsleiter': ('Schoenberg', None, 'Die Jakobsleiter', - [1, 2, 5, 4, 8, 7, 0, 3, 11, 10, 6, 9]), + [1, 2, 5, 4, 8, 7, 0, 3, 11, 10, 6, 9]), 'SchoenbergOp27No4': ('Schoenberg', 'Op. 27 No. 4', 'Four Pieces for Mixed Chorus, No. 4', - [1, 3, 10, 6, 8, 4, 11, 0, 2, 9, 5, 7]), + [1, 3, 10, 6, 8, 4, 11, 0, 2, 9, 5, 7]), 'WebernOp23': ('Webern', 'Op. 23', 'Three Songs', - [8, 3, 7, 4, 10, 6, 2, 5, 1, 0, 9, 11]), + [8, 3, 7, 4, 10, 6, 2, 5, 1, 0, 9, 11]), 'BergLuluActIIScene1': ('Berg', 'Lulu, Act II, Scene 1', - 'Perm. (Every 5th Note Of Transposed Primary Row)', - [10, 7, 1, 0, 9, 2, 4, 11, 5, 8, 3, 6]), + 'Perm. (Every 5th Note Of Transposed Primary Row)', + [10, 7, 1, 0, 9, 2, 4, 11, 5, 8, 3, 6]), 'SchoenbergOp27No1': ('Schoenberg', 'Op. 27 No. 1', 'Four Pieces for Mixed Chorus, No. 1', - [6, 5, 2, 8, 7, 1, 3, 4, 10, 9, 11, 0]), + [6, 5, 2, 8, 7, 1, 3, 4, 10, 9, 11, 0]), 'BergLuluActIScene20': ('Berg', 'Lulu, Act I , Scene XX', - 'Perm. (Every 7th Note Of Transposed Primary Row)', - [10, 6, 3, 8, 5, 11, 4, 2, 9, 0, 1, 7]), + 'Perm. (Every 7th Note Of Transposed Primary Row)', + [10, 6, 3, 8, 5, 11, 4, 2, 9, 0, 1, 7]), 'SchoenbergOp27No3': ('Schoenberg', 'Op. 27 No. 3', 'Four Pieces for Mixed Chorus, No. 3', - [7, 6, 2, 4, 5, 3, 11, 0, 8, 10, 9, 1]), + [7, 6, 2, 4, 5, 3, 11, 0, 8, 10, 9, 1]), 'SchoenbergOp27No2': ('Schoenberg', 'Op. 27 No. 2', 'Four Pieces for Mixed Chorus, No. 2', - [0, 11, 4, 10, 2, 8, 3, 7, 6, 5, 9, 1]), + [0, 11, 4, 10, 2, 8, 3, 7, 6, 5, 9, 1]), 'SchoenbergFragPiano': ('Schoenberg', None, 'Fragment For Piano', - [6, 9, 0, 7, 1, 2, 8, 11, 5, 10, 4, 3]), + [6, 9, 0, 7, 1, 2, 8, 11, 5, 10, 4, 3]), 'SchoenbergOp50B': ('Schoenberg', 'Op. 50B', 'De Profundis', - [3, 9, 8, 4, 2, 10, 7, 11, 0, 6, 5, 1]), + [3, 9, 8, 4, 2, 10, 7, 11, 0, 6, 5, 1]), 'SchoenbergOp50C': ('Schoenberg', 'Op. 50C', 'Modern Psalms, The First Psalm', - [4, 3, 0, 8, 11, 7, 5, 9, 6, 10, 1, 2]), + [4, 3, 0, 8, 11, 7, 5, 9, 6, 10, 1, 2]), 'SchoenbergOp50A': ('Schoenberg', 'Op. 50A', 'Three Times A Thousand Years', - [7, 9, 6, 4, 5, 11, 10, 2, 0, 1, 3, 8]), + [7, 9, 6, 4, 5, 11, 10, 2, 0, 1, 3, 8]), 'SchoenbergMosesAron': ('Schoenberg', None, 'Moses And Aron', - [9, 10, 4, 2, 3, 1, 7, 5, 6, 8, 11, 0]), + [9, 10, 4, 2, 3, 1, 7, 5, 6, 8, 11, 0]), 'WebernOp25': ('Webern', 'Op. 25', 'Three Songs', - [7, 4, 3, 6, 1, 5, 2, 11, 10, 0, 9, 8]), + [7, 4, 3, 6, 1, 5, 2, 11, 10, 0, 9, 8]), 'SchoenbergOp23No5': ('Schoenberg', 'Op. 23, No. 5', 'Five Piano Pieces', - [1, 9, 11, 7, 8, 6, 10, 2, 4, 3, 0, 5]), + [1, 9, 11, 7, 8, 6, 10, 2, 4, 3, 0, 5]), 'SchoenbergOp28No1': ('Schoenberg', 'Op. 28 No. 1', - 'Three Satires for Mixed Chorus, No. 1', - [0, 4, 7, 1, 9, 11, 5, 3, 2, 6, 8, 10]), + 'Three Satires for Mixed Chorus, No. 1', + [0, 4, 7, 1, 9, 11, 5, 3, 2, 6, 8, 10]), 'SchoenbergOp28No3': ('Schoenberg', 'Op. 28 No. 3', - 'Three Satires for Mixed Chorus, No. 3', - [5, 6, 4, 8, 2, 10, 7, 9, 3, 11, 1, 0]), + 'Three Satires for Mixed Chorus, No. 3', + [5, 6, 4, 8, 2, 10, 7, 9, 3, 11, 1, 0]), 'WebernOp21': ('Webern', 'Op. 21', 'Chamber Symphony', - [5, 8, 7, 6, 10, 9, 3, 4, 0, 1, 2, 11]), + [5, 8, 7, 6, 10, 9, 3, 4, 0, 1, 2, 11]), 'SchoenbergIsraelExists': ('Schoenberg', None, 'Israel Exists Again', - [0, 3, 4, 9, 11, 5, 2, 1, 10, 8, 6, 7]), + [0, 3, 4, 9, 11, 5, 2, 1, 10, 8, 6, 7]), 'SchoenbergOp35No2': ('Schoenberg', 'Op. 35', 'Six Pieces for Male Chorus, No. 2', - [6, 9, 7, 1, 0, 2, 5, 11, 10, 3, 4, 8]), + [6, 9, 7, 1, 0, 2, 5, 11, 10, 3, 4, 8]), 'SchoenbergOp35No3': ('Schoenberg', 'Op. 35', 'Six Pieces for Male Chorus, No. 3', - [3, 6, 7, 8, 5, 0, 9, 10, 4, 11, 2, 1]), + [3, 6, 7, 8, 5, 0, 9, 10, 4, 11, 2, 1]), 'SchoenbergOp35No1': ('Schoenberg', 'Op. 35', 'Six Pieces for Male Chorus, No. 1', - [2, 11, 3, 5, 4, 1, 8, 10, 9, 6, 0, 7]), + [2, 11, 3, 5, 4, 1, 8, 10, 9, 6, 0, 7]), 'SchoenbergOp48No1': ('Schoenberg', 'Op. 48', 'Three Songs, No. 1, "Sommermud"', - [1, 2, 0, 6, 3, 5, 4, 10, 11, 7, 9, 8]), + [1, 2, 0, 6, 3, 5, 4, 10, 11, 7, 9, 8]), 'SchoenbergOp35No5': ('Schoenberg', 'Op. 35', 'Six Pieces for Male Chorus, No. 5', - [1, 7, 10, 2, 3, 11, 8, 4, 0, 6, 5, 9]), + [1, 7, 10, 2, 3, 11, 8, 4, 0, 6, 5, 9]), 'SchoenbergOp29': ('Schoenberg', 'Op. 29', 'Suite', - [3, 7, 6, 10, 2, 11, 0, 9, 8, 4, 5, 1]), + [3, 7, 6, 10, 2, 11, 0, 9, 8, 4, 5, 1]), 'BergLyricSuitePerm': ('Berg', None, 'Lyric Suite, Last Mvt. Permutation', - [5, 6, 10, 4, 1, 9, 2, 8, 7, 3, 0, 11]), + [5, 6, 10, 4, 1, 9, 2, 8, 7, 3, 0, 11]), 'WebernOp20': ('Webern', 'Op. 20', 'String Trio', - [8, 7, 2, 1, 6, 5, 9, 10, 3, 4, 0, 11]), + [8, 7, 2, 1, 6, 5, 9, 10, 3, 4, 0, 11]), 'SchoenbergOp46': ('Schoenberg', 'Op. 46', 'A Survivor From Warsaw', - [6, 7, 0, 8, 4, 3, 11, 10, 5, 9, 1, 2]), + [6, 7, 0, 8, 4, 3, 11, 10, 5, 9, 1, 2]), 'SchoenbergFragOrganSonata': ('Schoenberg', None, 'Fragment of Sonata For Organ', - [1, 7, 11, 3, 9, 2, 8, 6, 10, 5, 0, 4]), + [1, 7, 11, 3, 9, 2, 8, 6, 10, 5, 0, 4]), 'SchoenbergOp44': ('Schoenberg', 'Op. 44', 'Prelude To A Suite From "Genesis"', - [10, 6, 2, 5, 4, 0, 11, 8, 1, 3, 9, 7]), + [10, 6, 2, 5, 4, 0, 11, 8, 1, 3, 9, 7]), 'SchoenbergOp45': ('Schoenberg', 'Op. 45', 'String Trio', - [2, 10, 3, 9, 4, 1, 11, 8, 6, 7, 5, 0]), + [2, 10, 3, 9, 4, 1, 11, 8, 6, 7, 5, 0]), 'SchoenbergOp33A': ('Schoenberg', 'Op. 33A', 'Two Piano Pieces, No. 1', - [10, 5, 0, 11, 9, 6, 1, 3, 7, 8, 2, 4]), + [10, 5, 0, 11, 9, 6, 1, 3, 7, 8, 2, 4]), 'SchoenbergOp25': ('Schoenberg', 'Op.25', 'Suite for Piano', - [4, 5, 7, 1, 6, 3, 8, 2, 11, 0, 9, 10]), + [4, 5, 7, 1, 6, 3, 8, 2, 11, 0, 9, 10]), 'SchoenbergOp26': ('Schoenberg', 'Op. 26', 'Wind Quintet', - [3, 7, 9, 11, 1, 0, 10, 2, 4, 6, 8, 5]), + [3, 7, 9, 11, 1, 0, 10, 2, 4, 6, 8, 5]), 'SchoenbergOp33B': ('Schoenberg', 'Op. 33B', 'Two Piano Pieces, No. 2', - [11, 1, 5, 3, 9, 8, 6, 10, 7, 4, 0, 2]), + [11, 1, 5, 3, 9, 8, 6, 10, 7, 4, 0, 2]), 'BergViolinConcerto': ('Berg', None, 'Concerto For Violin And Orchestra', - [7, 10, 2, 6, 9, 0, 4, 8, 11, 1, 3, 5]), + [7, 10, 2, 6, 9, 0, 4, 8, 11, 1, 3, 5]), 'WebernOp22': ('Webern', 'Op. 22', 'Quartet For Violin, Clarinet, Tenor Sax, And Piano', - [6, 3, 2, 5, 4, 8, 9, 10, 11, 1, 7, 0]), + [6, 3, 2, 5, 4, 8, 9, 10, 11, 1, 7, 0]), 'BergLulu': ('Berg', None, 'Lulu: Primary Row', - [0, 4, 5, 2, 7, 9, 6, 8, 11, 10, 3, 1]), + [0, 4, 5, 2, 7, 9, 6, 8, 11, 10, 3, 1]), 'WebernOp30': ('Webern', 'Op. 30', 'Variations For Orchestra', - [9, 10, 1, 0, 11, 2, 3, 6, 5, 4, 7, 8]), + [9, 10, 1, 0, 11, 2, 3, 6, 5, 4, 7, 8]), 'WebernOp31': ('Webern', 'Op. 31', 'Cantata II', - [6, 9, 5, 4, 8, 3, 7, 11, 10, 2, 1, 0]), + [6, 9, 5, 4, 8, 3, 7, 11, 10, 2, 1, 0]), 'WebernOpNo17No1': ('Webern', 'Op. 17, No. 1', '"Armer Sunder, Du"', - [11, 10, 5, 6, 3, 4, 7, 8, 9, 0, 1, 2]), + [11, 10, 5, 6, 3, 4, 7, 8, 9, 0, 1, 2]), 'WebernOp24': ('Webern', 'Op. 24', 'Concerto For Nine Instruments', - [11, 10, 2, 3, 7, 6, 8, 4, 5, 0, 1, 9]), + [11, 10, 2, 3, 7, 6, 8, 4, 5, 0, 1, 9]), 'SchoenbergOp48No2': ('Schoenberg', 'Op. 48', 'Three Songs, No. 2, "Tot"', - [2, 3, 9, 1, 10, 4, 8, 7, 0, 11, 5, 6]), + [2, 3, 9, 1, 10, 4, 8, 7, 0, 11, 5, 6]), 'WebernOp27': ('Webern', 'Op. 27', 'Variations For Piano', - [3, 11, 10, 2, 1, 0, 6, 4, 7, 5, 9, 8]), + [3, 11, 10, 2, 1, 0, 6, 4, 7, 5, 9, 8]), 'SchoenbergOp47': ('Schoenberg', 'Op. 47', 'Fantasy For Violin And Piano', - [10, 9, 1, 11, 5, 7, 3, 4, 0, 2, 8, 6]), + [10, 9, 1, 11, 5, 7, 3, 4, 0, 2, 8, 6]), 'WebernOp19No2': ('Webern', 'Op. 19, No. 2', '"Ziehn Die Schafe"', - [8, 4, 9, 6, 7, 0, 11, 5, 3, 2, 10, 1]), + [8, 4, 9, 6, 7, 0, 11, 5, 3, 2, 10, 1]), 'WebernOp19No1': ('Webern', 'Op. 19, No. 1', '"Weiss Wie Lilien"', - [7, 10, 6, 5, 3, 9, 8, 1, 2, 11, 4, 0]), + [7, 10, 6, 5, 3, 9, 8, 1, 2, 11, 4, 0]), 'WebernOp26': ('Webern', 'Op. 26', 'Das Augenlicht', - [8, 10, 9, 0, 11, 3, 4, 1, 5, 2, 6, 7]), + [8, 10, 9, 0, 11, 3, 4, 1, 5, 2, 6, 7]), 'SchoenbergFragPianoPhantasia': ('Schoenberg', None, 'Fragment of Phantasia For Piano', - [1, 5, 3, 6, 4, 8, 0, 11, 2, 9, 10, 7]), + [1, 5, 3, 6, 4, 8, 0, 11, 2, 9, 10, 7]), 'BergDerWein': ('Berg', None, 'Der Wein', - [2, 4, 5, 7, 9, 10, 1, 6, 8, 0, 11, 3]), + [2, 4, 5, 7, 9, 10, 1, 6, 8, 0, 11, 3]), 'BergWozzeckPassacaglia': ('Berg', None, 'Wozzeck, Act I, Scene 4 "Passacaglia"', - [3, 11, 7, 1, 0, 6, 4, 10, 9, 5, 8, 2]), + [3, 11, 7, 1, 0, 6, 4, 10, 9, 5, 8, 2]), 'WebernOp18No1': ('Webern', 'Op. 18, No. 1', '"Schatzerl Klein"', - [0, 11, 5, 8, 10, 9, 3, 4, 1, 7, 2, 6]), + [0, 11, 5, 8, 10, 9, 3, 4, 1, 7, 2, 6]), 'WebernOp18No2': ('Webern', 'Op. 18, No. 2', '"Erlosung"', - [6, 9, 5, 8, 4, 7, 3, 11, 2, 10, 1, 0]), + [6, 9, 5, 8, 4, 7, 3, 11, 2, 10, 1, 0]), 'WebernOp18No3': ('Webern', 'Op. 18, No. 3', '"Ave, Regina Coelorum"', - [4, 3, 7, 6, 5, 11, 10, 2, 1, 0, 9, 8]), + [4, 3, 7, 6, 5, 11, 10, 2, 1, 0, 9, 8]), 'SchoenbergOp42': ('Schoenberg', 'Op. 42', 'Concerto For Piano And Orchestra', - [3, 10, 2, 5, 4, 0, 6, 8, 1, 9, 11, 7]), + [3, 10, 2, 5, 4, 0, 6, 8, 1, 9, 11, 7]), 'SchoenbergOp48No3': ('Schoenberg', 'Op. 48', 'Three Songs, No, 3, "Madchenlied"', - [1, 7, 9, 11, 3, 5, 10, 6, 4, 0, 8, 2]), + [1, 7, 9, 11, 3, 5, 10, 6, 4, 0, 8, 2]), 'SchoenbergOp37': ('Schoenberg', 'Op. 37', 'Fourth String Quartet', - [2, 1, 9, 10, 5, 3, 4, 0, 8, 7, 6, 11]), + [2, 1, 9, 10, 5, 3, 4, 0, 8, 7, 6, 11]), 'SchoenbergOp36': ('Schoenberg', 'Op. 36', 'Concerto for Violin and Orchestra', - [9, 10, 3, 11, 4, 6, 0, 1, 7, 8, 2, 5]), + [9, 10, 3, 11, 4, 6, 0, 1, 7, 8, 2, 5]), 'SchoenbergOp34': ('Schoenberg', 'Op. 34', 'Accompaniment to a Film Scene', - [3, 6, 2, 4, 1, 0, 9, 11, 10, 8, 5, 7]), + [3, 6, 2, 4, 1, 0, 9, 11, 10, 8, 5, 7]), 'BergChamberConcerto': ('Berg', None, 'Chamber Concerto', - [11, 7, 5, 9, 2, 3, 6, 8, 0, 1, 4, 10]), + [11, 7, 5, 9, 2, 3, 6, 8, 0, 1, 4, 10]), 'SchoenbergOp32': ('Schoenberg', 'Op. 32', 'Von Heute Auf Morgen', - [2, 3, 9, 1, 11, 5, 8, 7, 4, 0, 10, 6]), + [2, 3, 9, 1, 11, 5, 8, 7, 4, 0, 10, 6]), 'SchoenbergOp31': ('Schoenberg', 'Op. 31', 'Variations for Orchestra', - [10, 4, 6, 3, 5, 9, 2, 1, 7, 8, 11, 0]), + [10, 4, 6, 3, 5, 9, 2, 1, 7, 8, 11, 0]), 'SchoenbergOp30': ('Schoenberg', 'Op. 30', 'Third String Quartet', - [7, 4, 3, 9, 0, 5, 6, 11, 10, 1, 8, 2]), + [7, 4, 3, 9, 0, 5, 6, 11, 10, 1, 8, 2]), 'BergLyricSuite': ('Berg', None, 'Lyric Suite Primary Row', - [5, 4, 0, 9, 7, 2, 8, 1, 3, 6, 10, 11]), + [5, 4, 0, 9, 7, 2, 8, 1, 3, 6, 10, 11]), 'SchoenbergOp41': ('Schoenberg', 'Op. 41', 'Ode To Napoleon', - [1, 0, 4, 5, 9, 8, 3, 2, 6, 7, 11, 10]), + [1, 0, 4, 5, 9, 8, 3, 2, 6, 7, 11, 10]), 'WebernOp17No3': ('Webern', 'Op. 17, No. 3', '"Heiland, Unsere Missetaten..."', - [8, 5, 4, 3, 7, 6, 0, 1, 2, 11, 10, 9]), + [8, 5, 4, 3, 7, 6, 0, 1, 2, 11, 10, 9]), 'WebernOp17No2': ('Webern', 'Op. 17, No. 2', '"Liebste Jungfrau"', - [1, 0, 11, 7, 8, 2, 3, 6, 5, 4, 9, 10]) + [1, 0, 11, 7, 8, 2, 3, 6, 5, 4, 9, 10]) } # ------------------------------------------------------------------------------ diff --git a/music21/sieve.py b/music21/sieve.py index 5f4605495b..a5466e1ec7 100644 --- a/music21/sieve.py +++ b/music21/sieve.py @@ -203,7 +203,7 @@ def rabinMiller(n): # primes up to 100; 2, 3 handled by mod 6 primes = [5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, - 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] + 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] if n <= 100: if n in primes: @@ -484,7 +484,7 @@ def _fillRange(self): segNeg = [-x for x in segNeg] # make negative if len(segNeg) < self.length: segPos = self._fillRabinMiller(0, self.length - len(segNeg), - None, 'up') + None, 'up') self.seg = segNeg + segPos else: # add positive values self.seg = segNeg diff --git a/music21/spanner.py b/music21/spanner.py index ea6f9423d0..c4e035f3c4 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -698,16 +698,16 @@ def fill( endOffsetInHierarchy = opFrac(startOffsetInHierarchy + startElement.quarterLength) matchIterator = (searchStream - .recurse() - .getElementsByOffsetInHierarchy( - startOffsetInHierarchy, - endOffsetInHierarchy, - includeEndBoundary=includeEndBoundary, - mustFinishInSpan=mustFinishInSpan, - mustBeginInSpan=mustBeginInSpan, - includeElementsThatEndAtStart=includeElementsThatEndAtStart) - .getElementsByClass(self.fillElementTypes) - ) + .recurse() + .getElementsByOffsetInHierarchy( + startOffsetInHierarchy, + endOffsetInHierarchy, + includeEndBoundary=includeEndBoundary, + mustFinishInSpan=mustFinishInSpan, + mustBeginInSpan=mustBeginInSpan, + includeElementsThatEndAtStart=includeElementsThatEndAtStart) + .getElementsByClass(self.fillElementTypes) + ) for foundElement in matchIterator: if foundElement is startElement: @@ -2965,7 +2965,7 @@ def testLineA(self): n3 = s.notes[-1] sp1 = Line(n1, n2, startTick='up', lineType='dotted') sp2 = Line(n2, n3, startTick='down', lineType='dashed', - endHeight=40) + endHeight=40) s.append(sp1) s.append(sp2) # s.show('t') diff --git a/music21/stream/base.py b/music21/stream/base.py index baea9a99bb..f5f0036add 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -4409,13 +4409,13 @@ def hasMeasureNumberInformation(measureIterator: iterator.StreamIterator[Measure matches = [m for m in mStreamIter if m.number in matchingMeasureNumbers] else: matches = [m for i, m in enumerate(mStreamIter) - if i + 1 in matchingMeasureNumbers] + if i + 1 in matchingMeasureNumbers] else: if hasUniqueMeasureNumbers: matches = [m for m in mStreamIter if m.number >= numberStart] else: matches = [m for i, m in enumerate(mStreamIter) - if i + 1 >= numberStart] + if i + 1 >= numberStart] if startSuffix is not None: oldMatches = matches @@ -6253,7 +6253,7 @@ def _uniqueOffsetsAndEndTimes(self, offsetsOnly=False, endTimesOnly=False): endTimes = set() else: endTimes = {opFrac(v[0] + v[1].duration.quarterLength) - for v in offsetDictValues} + for v in offsetDictValues} return sorted(offsets.union(endTimes)) def chordify( @@ -11559,7 +11559,7 @@ def doOneMeasureWithVoices(mInner): mEmpty.mergeAttributes(m) # Propagate bar, meter, key elements to lower parts mEmpty.mergeElements(m, classFilterList=('Barline', - 'TimeSignature', 'KeySignature')) + 'TimeSignature', 'KeySignature')) s.parts[i].insert(self.elementOffset(m), mEmpty) # if part has no measures but has voices, contents of each voice go into the part elif self.hasVoices(): diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py index b3523a4404..c681463fbb 100644 --- a/music21/stream/makeNotation.py +++ b/music21/stream/makeNotation.py @@ -1701,9 +1701,11 @@ def makeAccidentalsInMeasureStream( # just get the chromatic pitches from previous measure # G-naturals in C major following G-flats in F major need cautionary # G-naturals in C major following G-flats in Db major don't - pitchPastMeasure = [p for p in - measuresOnly[i - 1].pitches + ornamentalPitches(measuresOnly[i - 1]) - if p.name not in ksLastDiatonic] + previousMeasure = measuresOnly[i - 1] + pitchPastMeasure = [ + p for p in previousMeasure.pitches + ornamentalPitches(previousMeasure) + if p.name not in ksLastDiatonic + ] # Get tiePitchSet from previous measure try: previousNoteOrChord = measuresOnly[i - 1][note.NotRest][-1] @@ -2319,7 +2321,7 @@ def testStreamExceptions(self): with self.assertRaises(stream.StreamException) as cm: p.makeMeasures(meterStream=duration.Duration()) self.assertEqual(str(cm.exception), - 'meterStream is neither a Stream nor a TimeSignature!') + 'meterStream is neither a Stream nor a TimeSignature!') def testMakeTiesChangingTimeSignatures(self): ''' diff --git a/music21/stream/tests.py b/music21/stream/tests.py index 0686170f98..8006c018fa 100644 --- a/music21/stream/tests.py +++ b/music21/stream/tests.py @@ -526,8 +526,8 @@ def testStreamSortRecursion(self): offsets = [a.offset for a in fs_fs] # safer is a.getOffsetBySite(fs_fs) offsetsBrief = offsets[:20] self.assertEqual(offsetsBrief, - [0, 2, 4, 5, 6, 7, 9, 10, - 11, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19]) + [0, 2, 4, 5, 6, 7, 9, 10, + 11, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19]) def testOverlapsA(self): a = Stream() @@ -1046,17 +1046,17 @@ def testFindConsecutiveNotes(self): consec = m.findConsecutiveNotes() self.assertEqual([repr(x) for x in consec], - ['', - '', - '', - '', - 'None', - '', - '', - '', - '', - ] - ) + ['', + '', + '', + '', + 'None', + '', + '', + '', + '', + ] + ) # with voices in each of two measures m.insert(meter.TimeSignature('2/4')) @@ -1356,13 +1356,13 @@ def testStripTiesClearBeaming(self): p = p.splitAtDurations(recurse=True)[0] p.makeBeams(inPlace=True) self.assertEqual([repr(el.beams) for el in p[note.Note]], - ['', - '>', - '>', - '>', - '>' - ] - ) + ['', + '>', + '>', + '>', + '>' + ] + ) def testStripTiesStopTieChordFollowsRest(self): ''' @@ -2220,16 +2220,16 @@ def testMakeRestsB(self): match = str(list(s.flatten().notesAndRests)) self.assertEqual(match, '[, , ' - + ', , ' - + ', ]') + + ', , ' + + ', ]') match = str([(n, n.duration) for n in s.flatten().notesAndRests]) self.assertEqual(match, '[(, ), ' - + '(, ), ' - + '(, ), ' - + '(, ), ' - + '(, ), ' - + '(, )]') + + '(, ), ' + + '(, ), ' + + '(, ), ' + + '(, ), ' + + '(, )]') GEX = m21ToXml.GeneralObjectExporter() unused_mx = GEX.parse(s).decode('utf-8') @@ -2738,7 +2738,7 @@ def testMakeAccidentalsB(self): c = m34.getElementsByClass(chord.Chord) # assuming not showing accidental b/c of key self.assertEqual(str(c[1].pitches), '(, ' - + ', )') + + ', )') # because of key self.assertEqual(str(c[1].pitches[0].accidental.displayStatus), 'False') @@ -2748,7 +2748,7 @@ def testMakeAccidentalsB(self): # has correct pitches but natural not showing on C self.assertEqual(str(c[0].pitches), '(, , ' - + ')') + + ')') self.assertEqual(str(c[0].pitches[0].accidental), 'None') def testMakeAccidentalsC(self): @@ -4140,7 +4140,7 @@ def testAnalyze(self): s = corpus.parse('bach/bwv66.6') sub = [s.parts[0], s.parts[1], s.measures(4, 5), - s.parts[2].measures(4, 5)] + s.parts[2].measures(4, 5)] matchAmbitus = [interval.Interval(12), interval.Interval(15), @@ -4866,11 +4866,11 @@ def testMakeChordsBuiltC(self): sMod = s1.chordify(removeRedundantPitches=True) self.assertEqual([p.nameWithOctave for p in sMod.getElementsByClass(chord.Chord)[0].pitches], - ['C2', 'G2']) + ['C2', 'G2']) self.assertEqual([p.nameWithOctave for p in sMod.getElementsByClass(chord.Chord)[1].pitches], - ['E4', 'F#4']) + ['E4', 'F#4']) # without redundant pitch gathering sMod = s1.chordify(removeRedundantPitches=False) @@ -5529,8 +5529,8 @@ def testChordifyImported(self): 21.0, 21.5, 22.0, 22.5, 23.0, 23.5, 24.0, 24.5, 25.0, 25.5, 26.0, 26.5, 27.0, 30.0, 33.0, 34.5, 35.5, 36.0, 37.5, 38.0, 39.0, 40.0, 40.5, 41.0, 42.0, 43.5, 45.0, 45.5, 46.0, 46.5, - 47.0, 47.5, 48.0, 49.5, 51.0, 51.5, 52.0, 52.5, 53.0, 53.5, - 54.0, 54.5, 55.0, 55.5, 56.0, 56.5, 57.0, 58.5, 59.5]) + 47.0, 47.5, 48.0, 49.5, 51.0, 51.5, 52.0, 52.5, 53.0, 53.5, + 54.0, 54.5, 55.0, 55.5, 56.0, 56.5, 57.0, 58.5, 59.5]) self.assertEqual(len(post[chord.Chord]), 71) # Careful! one version of the caching is screwing up m. 20 which definitely should # not have rests in it -- was creating 69 notes, not 71. @@ -6341,7 +6341,7 @@ def testStripTiesBuiltB(self): self.assertEqual([n.offset for n in first_m_notesAndRests], [0.0, 1.0, 3.0]) self.assertEqual([n.quarterLength - for n in first_m_notesAndRests], + for n in first_m_notesAndRests], [1.0, 2.0, 2.0]) self.assertEqual([n.beatStr for n in first_m_notesAndRests], ['1', '2', '4']) @@ -6440,7 +6440,7 @@ def testDerivationA(self): m4 = p1.measure(4) self.assertIs(m4.flatten().notesAndRests.stream().derivation.rootDerivation, m4, - list(m4.flatten().notesAndRests.stream().derivation.chain())) + list(m4.flatten().notesAndRests.stream().derivation.chain())) # part is the root derivation of a measures() call mRange = p1.measures(4, 6) @@ -7549,12 +7549,12 @@ def testTransposeByPitchB(self): self.assertEqual([str(p) for p in s.parts[0].pitches], ['D4', 'E4', 'F#4', 'G4', 'A4', 'B4', 'C#5', 'D5']) self.assertEqual([None if p.accidental is None else p.accidental.displayStatus - for p in s.parts[0].pitches], + for p in s.parts[0].pitches], [True, None, False, None, None, None, False, None]) self.assertEqual([str(p) for p in s.parts[1].pitches], ['A4', 'B4', 'C#5', 'D5', 'E5', 'F#5', 'G#5', 'A5']) self.assertEqual([None if p.accidental is None else p.accidental.displayStatus - for p in s.parts[1].pitches], + for p in s.parts[1].pitches], [True, None, False, None, None, False, False, None]) self.assertEqual(s.atSoundingPitch, 'unknown') @@ -7563,12 +7563,12 @@ def testTransposeByPitchB(self): self.assertEqual([str(p) for p in s.parts[0].pitches], ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']) self.assertEqual([None if p.accidental is None else p.accidental.displayStatus - for p in s.parts[0].pitches], + for p in s.parts[0].pitches], [True, None, None, None, None, None, None, None]) self.assertEqual([str(p) for p in s.parts[1].pitches], ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']) self.assertEqual([None if p.accidental is None else p.accidental.displayStatus - for p in s.parts[1].pitches], + for p in s.parts[1].pitches], [True, None, None, None, None, None, None, None]) def testTransposeByPitchC(self): @@ -7609,9 +7609,9 @@ def testExtendTiesA(self): '' ], '', - ['None', - '' - ] + ['None', + '' + ] ]) def testExtendTiesB(self): @@ -7889,7 +7889,7 @@ def testChordifyF(self): self.maxDiff = None self.assertMultiLineEqual( s.parts[0].getElementsByClass(Measure)[0]._reprText(addEndTimes=True, - useMixedNumerals=True), + useMixedNumerals=True), '''{0 - 0} {0 - 0} {0 - 0} diff --git a/music21/test/multiprocessTest.py b/music21/test/multiprocessTest.py index bfec2e3dea..f45cbaa144 100644 --- a/music21/test/multiprocessTest.py +++ b/music21/test/multiprocessTest.py @@ -158,7 +158,7 @@ def mainPoolRunner(testGroup=('test',), restoreEnvironmentDefaults=False, leaveO # unordered says that results can RETURN in any order; not that # they'd be pooled out in any order. res = pool.imap_unordered(runOneModuleWithoutImp, - ((modGather, fp) for fp in pathsToRun)) + ((modGather, fp) for fp in pathsToRun)) continueIt = True timeouts = 0 diff --git a/music21/test/testRunner.py b/music21/test/testRunner.py index 62c822ba4f..f5539daab8 100644 --- a/music21/test/testRunner.py +++ b/music21/test/testRunner.py @@ -253,8 +253,8 @@ def testHello(self): # search all names for case-insensitive match for name in dir(tObj): if (name.lower() == runThisTest.lower() - or name.lower() == ('test' + runThisTest.lower()) - or name.lower() == ('xtest' + runThisTest.lower())): + or name.lower() == ('test' + runThisTest.lower()) + or name.lower() == ('xtest' + runThisTest.lower())): runThisTest = name break if hasattr(tObj, runThisTest): diff --git a/music21/test/test_chord.py b/music21/test/test_chord.py index 3c06f08ac4..caa50790e5 100644 --- a/music21/test/test_chord.py +++ b/music21/test/test_chord.py @@ -619,15 +619,15 @@ def testVolumePerPitchC(self): amps = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8] for accent in [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0.5, - 1, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 0.5, - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 1, 0.5, 0.5, 0.5, 0.5, - 0.5, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, - 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, - 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 1, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 0.5, + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 1, 0.5, 0.5, 0.5, 0.5, + 0.5, 1, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, ]: cNew = copy.deepcopy(c) if accent is not None: diff --git a/music21/test/test_metadata.py b/music21/test/test_metadata.py index 3784dd4e36..06e21df60e 100644 --- a/music21/test/test_metadata.py +++ b/music21/test/test_metadata.py @@ -430,7 +430,7 @@ def checkUniqueNamedItem( # self.assertIsInstance isn't sufficient, apparently. assert isinstance(itemNamespaceName, metadata.Contributor) self.assertEqual(itemNamespaceName.role, - contributorRole if contributorRole else uniqueName) + contributorRole if contributorRole else uniqueName) def testUniqueNameAccess(self): self.checkUniqueNamedItem('abstract', 'dcterms') diff --git a/music21/test/test_note.py b/music21/test/test_note.py index 35fa34cc66..7c039df7ae 100644 --- a/music21/test/test_note.py +++ b/music21/test/test_note.py @@ -246,7 +246,7 @@ def testNoteEquality(self): articulations.StrongAccent()] for a, b, c, d, match in [(n1, n4, a1, a1, True), - (n1, n2, a1, a1, False), (n1, n3, a1, a1, False), + (n1, n2, a1, a1, False), (n1, n3, a1, a1, False), # same pitch different orderings (n1, n4, a2, a3, True), (n1, n4, a4, a5, True), # different pitch same orderings diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 309fe3f3cb..fbd4379bb8 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -266,7 +266,7 @@ def compare(_past, _result): pList = [Pitch('f#3'), Pitch('f3'), Pitch('f#3'), Pitch('g3'), Pitch('f#4'), Pitch('f#4')] result = [('sharp', False), ('natural', True), ('sharp', True), - (None, None), ('sharp', True), ('sharp', False)] + (None, None), ('sharp', True), ('sharp', False)] # no 4 is a dicey affair; could go either way ks = key.KeySignature(1) proc(pList, [], ks.alteredPitches) diff --git a/music21/text.py b/music21/text.py index 7675f7d946..28a6dfab09 100644 --- a/music21/text.py +++ b/music21/text.py @@ -416,7 +416,7 @@ def readExcerpts(cls): ''' for languageCode in cls.languageCodes: thisExcerpt = (common.getSourceFilePath() / 'languageExcerpts' - / 'trainingData' / (languageCode + '.txt')) + / 'trainingData' / (languageCode + '.txt')) with thisExcerpt.open(encoding='utf-8') as f: excerptWords = f.read().split() diff --git a/music21/tree/verticality.py b/music21/tree/verticality.py index 789d6466a6..07d6f1f920 100644 --- a/music21/tree/verticality.py +++ b/music21/tree/verticality.py @@ -1064,7 +1064,7 @@ def getAllVoiceLeadingQuartets( if ((verticalityStreamParts[pp[0]] == thisQuartetTopPart or verticalityStreamParts[pp[0]] == thisQuartetBottomPart) and (verticalityStreamParts[pp[1]] == thisQuartetTopPart - or verticalityStreamParts[pp[1]] == thisQuartetBottomPart)): + or verticalityStreamParts[pp[1]] == thisQuartetBottomPart)): isAppropriate = True break if not isAppropriate: diff --git a/music21/variant.py b/music21/variant.py index 552b748a6a..8c30a75b76 100644 --- a/music21/variant.py +++ b/music21/variant.py @@ -470,10 +470,10 @@ def replacedElements(self, contextStream=None, classList=None, if classList is not None: classes.extend(classList) returnStream = contextStream.getElementsByOffset(vStart, vEnd, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=classes).stream() + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=classes).stream() elif self.lengthType == 'deletion': vMiddle = vStart + self.containedHighestTime @@ -484,14 +484,14 @@ def replacedElements(self, contextStream=None, classList=None, if classList is not None: classes.extend(classList) returnPart1 = contextStream.getElementsByOffset(vStart, vMiddle, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=classes).stream() + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=classes).stream() returnPart2 = contextStream.getElementsByOffset(vMiddle, vEnd, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True).stream() + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True).stream() returnStream = returnPart1 for e in returnPart2.elements: @@ -1860,7 +1860,7 @@ def _getBestListAndScore(streamX, streamY, badnessDict, listDict, # Check the added bar case: kList, kBadness = _getBestListAndScore(streamX, streamY, badnessDict, listDict, - isNone=True, streamXIndex=streamXIndex, streamYIndex=streamYIndex + 1) + isNone=True, streamXIndex=streamXIndex, streamYIndex=streamYIndex + 1) if kList is None: kList = [] if kList: @@ -2427,22 +2427,22 @@ def _getNextElements(s, v, numberOfElements=1): if lengthType == 'elongation': variantOffset = v.getOffsetBySite(s) potentialTargets = s.getElementsByOffset(variantOffset, - offsetEnd=s.highestTime, - includeEndBoundary=True, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=[vClass]) + offsetEnd=s.highestTime, + includeEndBoundary=True, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=[vClass]) returnElement = potentialTargets.first() else: replacementDuration = v.replacementQuarterLength variantOffset = v.getOffsetBySite(s) potentialTargets = s.getElementsByOffset(variantOffset + replacementDuration, - offsetEnd=s.highestTime, - includeEndBoundary=True, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=[vClass]) + offsetEnd=s.highestTime, + includeEndBoundary=True, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=[vClass]) returnElement = potentialTargets.first() @@ -2643,19 +2643,19 @@ def testDeepCopyVariantA(self): # normal in-place variant functionality s.insert(5, v1) self.assertEqual(self.pitchOut(s.pitches), - '[G4, G4, G4, G4, G4, G4, G4, G4]') + '[G4, G4, G4, G4, G4, G4, G4, G4]') sv = s.activateVariants(inPlace=False) self.assertEqual(self.pitchOut(sv.pitches), - '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') + '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') # test functionality on a deepcopy sCopy = copy.deepcopy(s) self.assertEqual(len(sCopy.getElementsByClass(Variant)), 1) self.assertEqual(self.pitchOut(sCopy.pitches), - '[G4, G4, G4, G4, G4, G4, G4, G4]') + '[G4, G4, G4, G4, G4, G4, G4, G4]') sCopy.activateVariants(inPlace=True) self.assertEqual(self.pitchOut(sCopy.pitches), - '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') + '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') def testDeepCopyVariantB(self): s = stream.Stream() @@ -2671,11 +2671,11 @@ def testDeepCopyVariantB(self): sCopy = copy.deepcopy(s) sCopy.activateVariants(inPlace=True) self.assertEqual(self.pitchOut(sCopy.pitches), - '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') + '[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(sCopy.pitches), - '[G4, G4, G4, G4, G4, F#5, A-4, G4, G4]') + '[G4, G4, G4, G4, G4, F#5, A-4, G4, G4]') # however, if the Variant deepcopy still references the original # notes it had, then when we try to activate the variant in the @@ -2683,7 +2683,7 @@ def testDeepCopyVariantB(self): s.activateVariants(inPlace=True) self.assertEqual(self.pitchOut(s.pitches), - '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') + '[G4, G4, G4, G4, G4, F#4, A-4, G4, G4]') class TestExternal(unittest.TestCase): diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py index 61060b9a5d..7a137cf302 100644 --- a/music21/voiceLeading.py +++ b/music21/voiceLeading.py @@ -577,15 +577,15 @@ def parallelMotion( if isinstance(requiredInterval, str): requiredInterval = interval.Interval(requiredInterval) intervalsAreValid = (vInt0.semiSimpleName - == requiredInterval.semiSimpleName + == requiredInterval.semiSimpleName and vInt1.semiSimpleName - == requiredInterval.semiSimpleName) + == requiredInterval.semiSimpleName) elif isinstance(requiredInterval, (interval.Interval, interval.DiatonicInterval)): intervalsAreValid = (vInt0.semiSimpleName - == requiredInterval.semiSimpleName + == requiredInterval.semiSimpleName and vInt1.semiSimpleName - == requiredInterval.semiSimpleName) + == requiredInterval.semiSimpleName) return intervalsAreValid @@ -1161,7 +1161,7 @@ def isProperResolution(self) -> bool: if keyScale and n2degree != 3: return False return (self.outwardContraryMotion() - and secondHarmony == 6) + and secondHarmony == 6) elif firstHarmony == 'd5': if keyScale and n1degree != 7: @@ -1169,7 +1169,7 @@ def isProperResolution(self) -> bool: if keyScale and n2degree != 1: return False return (self.inwardContraryMotion() - and secondHarmony == 3) + and secondHarmony == 3) elif firstHarmony == 'm7': if keyScale and n1degree != 5: @@ -1291,9 +1291,9 @@ def modalOpening(self) -> bool: openingIntervals = ('P1', 'P5') openingFunctions = ('I', 'V') return ((v0.simpleName in openingIntervals - or v1.simpleName in openingIntervals) - and (r1[0].upper() in openingFunctions if r1 is not False else False - or r2[0].upper() in openingFunctions if r2 is not False else False)) + or v1.simpleName in openingIntervals) + and (r1[0].upper() in openingFunctions if r1 is not False else False + or r2[0].upper() in openingFunctions if r2 is not False else False)) @common.decorators.deprecated( 'June 2026', 'v12', 'Use `not vlq.modalOpening()` instead.' @@ -1367,10 +1367,10 @@ def clausulaVera(self) -> bool: tonicName = self.key.tonic.name hIntervalNames = {self.hIntervals[0].name, self.hIntervals[1].name} return (hIntervalNames == {'m2', 'M2'} - and self.contraryMotion() - and self.vIntervals[1].name in ('P1', 'P8') - and self.v1n2.name == tonicName - and self.v2n2.name == tonicName) + and self.contraryMotion() + and self.vIntervals[1].name in ('P1', 'P8') + and self.v1n2.name == tonicName + and self.v2n2.name == tonicName) @common.decorators.deprecated( 'June 2026', 'v12', 'Use `not vlq.clausulaVera()` instead.' @@ -1882,8 +1882,8 @@ def _calcTNLS(self): Calculates the three note linear segments if only three Verticalities provided. ''' for partNum in range(min(len(self.verticalities[0].getObjectsByClass(note.Note)), - len(self.verticalities[1].getObjectsByClass(note.Note)), - len(self.verticalities[2].getObjectsByClass(note.Note))) + len(self.verticalities[1].getObjectsByClass(note.Note)), + len(self.verticalities[2].getObjectsByClass(note.Note))) ): self.tnlsDict[partNum] = ThreeNoteLinearSegment( [ @@ -2339,10 +2339,10 @@ def couldBeDiatonicNeighborTone(self) -> bool: ''' return (self._isComplete() - and self.n1.nameWithOctave == self.n3.nameWithOctave - and self.iLeft.chromatic.undirected == 2 - and self.iRight.chromatic.undirected == 2 - and self.iLeft.direction * self.iRight.direction == -1) + and self.n1.nameWithOctave == self.n3.nameWithOctave + and self.iLeft.chromatic.undirected == 2 + and self.iRight.chromatic.undirected == 2 + and self.iLeft.direction * self.iRight.direction == -1) def couldBeChromaticNeighborTone(self) -> bool: @@ -2360,10 +2360,10 @@ def couldBeChromaticNeighborTone(self) -> bool: False ''' return (self._isComplete() - and (self.n1.nameWithOctave == self.n3.nameWithOctave - and self.iLeft.isChromaticStep - and self.iRight.isChromaticStep - and (self.iLeft.direction * self.iRight.direction == -1))) + and (self.n1.nameWithOctave == self.n3.nameWithOctave + and self.iLeft.isChromaticStep + and self.iRight.isChromaticStep + and (self.iLeft.direction * self.iRight.direction == -1))) # Below: beginnings of an implementation for any object segments, diff --git a/music21/volpiano.py b/music21/volpiano.py index 58f8b30e95..2c12459ee6 100644 --- a/music21/volpiano.py +++ b/music21/volpiano.py @@ -392,8 +392,8 @@ def setAccFromPitch(dist, setNatural=False): continue if n.notehead == 'x' or (n.hasEditorialInformation - and 'liquescence' in n.editorial - and n.editorial.liquescence): + and 'liquescence' in n.editorial + and n.editorial.liquescence): tokenName = liquescentPitches[indexInPitchString] else: tokenName = normalPitches[indexInPitchString] From c064d120c7aa8f23b6caa67f4bda73cd95247dde Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:20:05 -1000 Subject: [PATCH 088/312] Hang the abcFormat articulation tuple off its own line Aligned to the opening bracket, the tuple of articulation strings started at column 46 and every row after the first was mostly leading whitespace. Break after the paren instead and indent the strings one step, closing on its own line. Also restores repeat.py's continuation fix, lost when the file was reverted. AI-assisted (Claude) --- music21/abcFormat/__init__.py | 16 +++++++++------- music21/repeat.py | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/music21/abcFormat/__init__.py b/music21/abcFormat/__init__.py index 9c52a79554..b84ec42818 100644 --- a/music21/abcFormat/__init__.py +++ b/music21/abcFormat/__init__.py @@ -2505,13 +2505,15 @@ def tokenize(self, strSrc: str) -> None: # 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 self.currentCollectStr in ('w', 'u', 'v', 'v.', 'h', 'H', 'vk', - 'uk', 'U', '~', - '.', '=', 'V', 'v.', 'S', 's', - 'i', 'I', 'ui', 'u.', 'Q', 'Hy', 'Hx', - 'r', 'm', 'M', 'n', 'N', 'o', 'O', 'P', - 'l', 'L', 'R', - 'y', 'T', 't', 'x', 'Z'): + if self.currentCollectStr in ( + 'w', 'u', 'v', 'v.', 'h', 'H', 'vk', + 'uk', 'U', '~', + '.', '=', 'V', 'v.', 'S', 's', + 'i', 'I', 'ui', 'u.', 'Q', 'Hy', 'Hx', + 'r', 'm', 'M', 'n', 'N', 'o', 'O', 'P', + 'l', 'L', 'R', + 'y', 'T', 't', 'x', 'Z', + ): pass # these are bad chords, or other problematic notations like # "D.C."x diff --git a/music21/repeat.py b/music21/repeat.py index b136311504..4ba93d5b6b 100644 --- a/music21/repeat.py +++ b/music21/repeat.py @@ -2214,7 +2214,7 @@ def _getSimilarMeasuresHelper(self, measures, source, compare, resDict, useDict) # we have a repeated section at least 2 measures in length; # check to see how far it goes nextOne = self._getSimilarMeasuresHelper(measures, source + 1, compare + 1, - resDict, useDict) + resDict, useDict) # make sure we don't have overlap res = ([source], [compare]) res[0].extend(nextOne[0]) From 8353a29dd0fd65ab5c3f1a7b22d661331cc26c2b Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:23:54 -1000 Subject: [PATCH 089/312] Align shorthandNotation's entries with its first one Every entry after `(None,)` sat at column 21, aligned to nothing -- the dict opens with a hanging indent, so pycodestyle's E12 checks accept whatever the rows do as long as the first one is indented. AI-assisted (Claude) --- music21/figuredBass/notation.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index 99681cc1ac..563b24c2a2 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -25,16 +25,16 @@ shorthandNotation: dict[tuple[int|None, ...], tuple[int, ...]] = { (None,): (5, 3), - (5,): (5, 3), - (6,): (6, 3), - (7,): (7, 5, 3), - (9,): (9, 7, 5, 3), - (11,): (11, 9, 7, 5, 3), - (13,): (13, 11, 9, 7, 5, 3), - (6, 5): (6, 5, 3), - (4, 3): (6, 4, 3), - (4, 2): (6, 4, 2), - (2,): (6, 4, 2), + (5,): (5, 3), + (6,): (6, 3), + (7,): (7, 5, 3), + (9,): (9, 7, 5, 3), + (11,): (11, 9, 7, 5, 3), + (13,): (13, 11, 9, 7, 5, 3), + (6, 5): (6, 5, 3), + (4, 3): (6, 4, 3), + (4, 2): (6, 4, 2), + (2,): (6, 4, 2), } prefixes = ['+', '#', '++', '##'] From cfa8de976312dd55074411a822bb32902e235e8d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:25:52 -1000 Subject: [PATCH 090/312] Hang testWindowed's assertEqual off its own line Aligning to the opening bracket pushed the expected legend out to column 25 and its rows past that. Break after `assertEqual(` so both arguments sit one step in, which is where the list was before. AI-assisted (Claude) --- music21/graph/plot.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/music21/graph/plot.py b/music21/graph/plot.py index a83d8e583b..0584280628 100644 --- a/music21/graph/plot.py +++ b/music21/graph/plot.py @@ -1791,20 +1791,21 @@ def testWindowed(self, doneAction=None): minWindow=1, windowStep=windowStep, doneAction=doneAction, dpi=300) b.run() - self.assertEqual(b.graphLegend.data, - [ - ['Major', - [('C#', '#f0727a'), ('D', '#ffd752'), ('E', '#eeff9a'), - ('F#', '#b9f0ff'), ('A', '#bb9aff'), ('B', '#ffb5ff') - ] - ], - ['Minor', - [('c#', '#8c0e16'), ('', '#ffffff'), ('', '#ffffff'), - ('f#', '#558caa'), ('', '#ffffff'), ('b', '#9b519b') - ] - ] - ] - ) + self.assertEqual( + b.graphLegend.data, + [ + ['Major', + [('C#', '#f0727a'), ('D', '#ffd752'), ('E', '#eeff9a'), + ('F#', '#b9f0ff'), ('A', '#bb9aff'), ('B', '#ffb5ff') + ] + ], + ['Minor', + [('c#', '#8c0e16'), ('', '#ffffff'), ('', '#ffffff'), + ('f#', '#558caa'), ('', '#ffffff'), ('b', '#9b519b') + ] + ] + ] + ) def testFeatures(self): streamList = ['bach/bwv66.6', 'schoenberg/opus19/movement2', 'corelli/opus3no1/1grave'] From ca3d76093dbc961eab43bec78b558ca7e5ee715d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:30:16 -1000 Subject: [PATCH 091/312] Bring stray continuation rows back onto their siblings' column 'sound' in partStaffExporter's tagList sat eight columns right of the other four tags. Five multi-line `if` conditions had the same drift in a different form: the first `and` at the opening paren's column, the next one four further in, so neither row lined up with anything. Put every row of each on one column -- the +8 the rest of the codebase uses for a wrapped condition. pycodestyle's E12 checks pass either way; they accept several alternative indents once brackets nest, and only look at a row in isolation. AI-assisted (Claude) --- music21/interval.py | 2 +- music21/musicxml/partStaffExporter.py | 4 ++-- music21/musicxml/xmlToM21.py | 2 +- music21/stream/base.py | 2 +- music21/test/testRunner.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/music21/interval.py b/music21/interval.py index 3dde7bc268..2390cdaa1b 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -1736,7 +1736,7 @@ def __eq__(self, other): # if self.direction != other.direction: # return False if (self.generic == other.generic - and self.specifier == other.specifier + and self.specifier == other.specifier and self.direction == other.direction): return True else: diff --git a/music21/musicxml/partStaffExporter.py b/music21/musicxml/partStaffExporter.py index 00e51acd6f..3cce7b8b34 100644 --- a/music21/musicxml/partStaffExporter.py +++ b/music21/musicxml/partStaffExporter.py @@ -86,7 +86,7 @@ def addStaffTags( mxStaff.text = str(staffNumber) helpers.insertBeforeElements(tag, mxStaff, tagList=['beam', 'notations', 'lyric', 'play', - 'sound']) + 'sound']) class PartStaffExporterMixin: @@ -423,7 +423,7 @@ def makeDivider(inner_sourceNumber: int|str) -> Element[t.Any]: # Or, gap in measure numbers in the subsequent part: keep iterating through target if (sourceNumber is not None - and targetNumber is not None + and targetNumber is not None and helpers.measureNumberComesBefore(targetNumber, sourceNumber)): continue # sourceMeasure is not None! diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index ba4089cdbd..df091660f3 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -2245,7 +2245,7 @@ def adjustTimeAttributesFromMeasure(self, m: stream.Measure): # If the measure is overfull by a "round" amount, assume that it was intended # otherwise it was likely the result of malformed MusicXML. if (diff > 0.5 - or nearestMultiple(diff, 0.0625)[1] < tol + or nearestMultiple(diff, 0.0625)[1] < tol or nearestMultiple(diff, 1 / 12)[1] < tol): mOffsetShift = mHighestTime else: diff --git a/music21/stream/base.py b/music21/stream/base.py index f5f0036add..d9c9190151 100644 --- a/music21/stream/base.py +++ b/music21/stream/base.py @@ -5810,7 +5810,7 @@ def getInstruments(self, if searchActiveSite: # if isinstance(self.activeSite, Stream) and self.activeSite != self: if (self.activeSite is not None - and self.activeSite.isStream + and self.activeSite.isStream and self.activeSite is not self): # environLocal.printDebug(['searching activeSite Stream', # self, self.activeSite]) diff --git a/music21/test/testRunner.py b/music21/test/testRunner.py index f5539daab8..190256e017 100644 --- a/music21/test/testRunner.py +++ b/music21/test/testRunner.py @@ -253,7 +253,7 @@ def testHello(self): # search all names for case-insensitive match for name in dir(tObj): if (name.lower() == runThisTest.lower() - or name.lower() == ('test' + runThisTest.lower()) + or name.lower() == ('test' + runThisTest.lower()) or name.lower() == ('xtest' + runThisTest.lower())): runThisTest = name break From e510d5430dc4809a177a869f1c3dd421f507fba7 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:31:13 -1000 Subject: [PATCH 092/312] Drop searchStream onto its own line in Spanner.fill Aligned to the opening paren, the method chain started at column 25 and its arguments ran out past 29. Break after the paren so the whole chain sits one step in, which is the spacing it had. AI-assisted (Claude) --- music21/spanner.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/music21/spanner.py b/music21/spanner.py index c4e035f3c4..c12c6aab66 100644 --- a/music21/spanner.py +++ b/music21/spanner.py @@ -697,17 +697,18 @@ def fill( else: endOffsetInHierarchy = opFrac(startOffsetInHierarchy + startElement.quarterLength) - matchIterator = (searchStream - .recurse() - .getElementsByOffsetInHierarchy( - startOffsetInHierarchy, - endOffsetInHierarchy, - includeEndBoundary=includeEndBoundary, - mustFinishInSpan=mustFinishInSpan, - mustBeginInSpan=mustBeginInSpan, - includeElementsThatEndAtStart=includeElementsThatEndAtStart) - .getElementsByClass(self.fillElementTypes) - ) + matchIterator = ( + searchStream + .recurse() + .getElementsByOffsetInHierarchy( + startOffsetInHierarchy, + endOffsetInHierarchy, + includeEndBoundary=includeEndBoundary, + mustFinishInSpan=mustFinishInSpan, + mustBeginInSpan=mustBeginInSpan, + includeElementsThatEndAtStart=includeElementsThatEndAtStart) + .getElementsByClass(self.fillElementTypes) + ) for foundElement in matchIterator: if foundElement is startElement: From bb911da4eb7f93038257fe0d8bb962f1bcb7ade3 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:36:59 -1000 Subject: [PATCH 093/312] Drop the first argument down where the visual indent ran deep Aligning to the opening bracket only pays when there is room to the right of it. scala's two cents assertions were aligned at column 73, leaving eight characters of usable width; variant's getElementsByOffset calls at 49-61. Break after the paren in those, so the arguments start one step in. Covers every call in the branch whose visual indent passed 40, plus the two stream/tests assertions and testMakeRestsB. variant's eight getElementsByOffset calls now share one shape, one argument per row. AI-assisted (Claude) --- music21/scale/intervalNetwork.py | 18 ++++---- music21/scale/scala/__init__.py | 54 ++++++++++++----------- music21/stream/tests.py | 45 +++++++++++--------- music21/variant.py | 73 ++++++++++++++++++-------------- 4 files changed, 104 insertions(+), 86 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index db91d356bc..384b181a55 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -1401,10 +1401,11 @@ def nextPitch( p = self.transposePitchAndApplySimplification(intervalObj, p) else: p = self.transposePitchAndApplySimplification(intervalObj.reverse(), p) - pCollect = self.processAlteredNodes(alteredDegrees=alteredDegrees, - n=n, - p=p, - direction=direction) + pCollect = self.processAlteredNodes( + alteredDegrees=alteredDegrees, + n=n, + p=p, + direction=direction) return pCollect @@ -1587,10 +1588,11 @@ def realizeAscending( p = self.transposePitchAndApplySimplification(intervalObj, p) pCollect = p - pCollect = self.processAlteredNodes(alteredDegrees=alteredDegrees, - n=n, - p=p, - direction=Direction.ASCENDING) + pCollect = self.processAlteredNodes( + alteredDegrees=alteredDegrees, + n=n, + p=p, + direction=Direction.ASCENDING) if attempts >= maxAttempts: raise IntervalNetworkException( diff --git a/music21/scale/scala/__init__.py b/music21/scale/scala/__init__.py index 221473e185..f161224540 100644 --- a/music21/scale/scala/__init__.py +++ b/music21/scale/scala/__init__.py @@ -648,31 +648,35 @@ def testScalaScaleB(self) -> None: self.assertEqual(ss.description, 'Franck Jedrzejewski continued fractions approx. of 12-tet') - self.assertEqual([f'{x:.9f}' for x in ss.getCentsAboveTonic()], ['100.099209825', - '199.979843291', - '299.973903610', - '400.108480470', - '498.044999135', - '600.088323762', - '699.997698171', - '800.909593096', - '900.026096390', - '1000.020156709', - '1088.268714730', - '1200.000000000']) - - self.assertEqual([f'{x:.9f}' for x in ss.getAdjacentCents()], ['100.099209825', - '99.880633466', - '99.994060319', - '100.134576860', - '97.936518664', - '102.043324627', - '99.909374409', - '100.911894925', - '99.116503294', - '99.994060319', - '88.248558022', - '111.731285270']) + self.assertEqual( + [f'{x:.9f}' for x in ss.getCentsAboveTonic()], + ['100.099209825', + '199.979843291', + '299.973903610', + '400.108480470', + '498.044999135', + '600.088323762', + '699.997698171', + '800.909593096', + '900.026096390', + '1000.020156709', + '1088.268714730', + '1200.000000000']) + + self.assertEqual( + [f'{x:.9f}' for x in ss.getAdjacentCents()], + ['100.099209825', + '99.880633466', + '99.994060319', + '100.134576860', + '97.936518664', + '102.043324627', + '99.909374409', + '100.911894925', + '99.116503294', + '99.994060319', + '88.248558022', + '111.731285270']) self.assertEqual([str(x) for x in ss.getIntervalSequence()], ['', diff --git a/music21/stream/tests.py b/music21/stream/tests.py index 8006c018fa..6e49f36179 100644 --- a/music21/stream/tests.py +++ b/music21/stream/tests.py @@ -1045,18 +1045,19 @@ def testFindConsecutiveNotes(self): consec = m.findConsecutiveNotes() - self.assertEqual([repr(x) for x in consec], - ['', - '', - '', - '', - 'None', - '', - '', - '', - '', - ] - ) + self.assertEqual( + [repr(x) for x in consec], + ['', + '', + '', + '', + 'None', + '', + '', + '', + '', + ] + ) # with voices in each of two measures m.insert(meter.TimeSignature('2/4')) @@ -1355,14 +1356,15 @@ def testStripTiesClearBeaming(self): self.assertEqual(p.streamStatus.beams, False) p = p.splitAtDurations(recurse=True)[0] p.makeBeams(inPlace=True) - self.assertEqual([repr(el.beams) for el in p[note.Note]], - ['', - '>', - '>', - '>', - '>' - ] - ) + self.assertEqual( + [repr(el.beams) for el in p[note.Note]], + ['', + '>', + '>', + '>', + '>' + ] + ) def testStripTiesStopTieChordFollowsRest(self): ''' @@ -2219,7 +2221,8 @@ def testMakeRestsB(self): # m2.show() match = str(list(s.flatten().notesAndRests)) - self.assertEqual(match, '[, , ' + self.assertEqual(match, + '[, , ' + ', , ' + ', ]') match = str([(n, n.duration) for n in s.flatten().notesAndRests]) diff --git a/music21/variant.py b/music21/variant.py index 8c30a75b76..77f997c8cf 100644 --- a/music21/variant.py +++ b/music21/variant.py @@ -469,11 +469,13 @@ def replacedElements(self, contextStream=None, classList=None, classes.append(e.classes[0]) if classList is not None: classes.extend(classList) - returnStream = contextStream.getElementsByOffset(vStart, vEnd, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=classes).stream() + returnStream = contextStream.getElementsByOffset( + vStart, + vEnd, + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=classes).stream() elif self.lengthType == 'deletion': vMiddle = vStart + self.containedHighestTime @@ -483,15 +485,19 @@ def replacedElements(self, contextStream=None, classList=None, classes.append(e.classes[0]) if classList is not None: classes.extend(classList) - returnPart1 = contextStream.getElementsByOffset(vStart, vMiddle, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=classes).stream() - returnPart2 = contextStream.getElementsByOffset(vMiddle, vEnd, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True).stream() + returnPart1 = contextStream.getElementsByOffset( + vStart, + vMiddle, + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=classes).stream() + returnPart2 = contextStream.getElementsByOffset( + vMiddle, + vEnd, + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True).stream() returnStream = returnPart1 for e in returnPart2.elements: @@ -2426,23 +2432,25 @@ def _getNextElements(s, v, numberOfElements=1): # Get next element in s after v which is of type vClass if lengthType == 'elongation': variantOffset = v.getOffsetBySite(s) - potentialTargets = s.getElementsByOffset(variantOffset, - offsetEnd=s.highestTime, - includeEndBoundary=True, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=[vClass]) + potentialTargets = s.getElementsByOffset( + variantOffset, + offsetEnd=s.highestTime, + includeEndBoundary=True, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=[vClass]) returnElement = potentialTargets.first() else: replacementDuration = v.replacementQuarterLength variantOffset = v.getOffsetBySite(s) - potentialTargets = s.getElementsByOffset(variantOffset + replacementDuration, - offsetEnd=s.highestTime, - includeEndBoundary=True, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=[vClass]) + potentialTargets = s.getElementsByOffset( + variantOffset + replacementDuration, + offsetEnd=s.highestTime, + includeEndBoundary=True, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=[vClass]) returnElement = potentialTargets.first() @@ -2528,12 +2536,13 @@ def makeVariantBlocks(s): for v in variantsToBeDone: startOffset = s.elementOffset(v) endOffset = v.replacementQuarterLength + startOffset - conflictingVariants = s.getElementsByOffset(offsetStart=startOffset, - offsetEnd=endOffset, - includeEndBoundary=False, - mustFinishInSpan=False, - mustBeginInSpan=True, - classList=[Variant]) + conflictingVariants = s.getElementsByOffset( + offsetStart=startOffset, + offsetEnd=endOffset, + includeEndBoundary=False, + mustFinishInSpan=False, + mustBeginInSpan=True, + classList=[Variant]) for cV in conflictingVariants: oldReplacementDuration = cV.replacementQuarterLength if s.elementOffset(cV) == startOffset: From 866fe6c3a9cc06aa55de42aac8fc530ceffb136f Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 28 Aug 2026 13:40:17 -1000 Subject: [PATCH 094/312] running-tests: name multiprocessTest as the one to reach for Both runners were listed as equals, with testSingleCoreAll marked "use before pushing to a PR" -- so it got run routinely, at 45 seconds against multiprocessTest's 10. That is a long time to leave someone waiting for an answer the parallel runner gives just as well. Says which one to use and confines the single-core runner to what it alone can tell you: a release check, a CI failure that will not reproduce, one core. AI-assisted (Claude) --- .agents/skills/running-tests/SKILL.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.agents/skills/running-tests/SKILL.md b/.agents/skills/running-tests/SKILL.md index 92601b045a..dc1538c1c3 100644 --- a/.agents/skills/running-tests/SKILL.md +++ b/.agents/skills/running-tests/SKILL.md @@ -75,17 +75,25 @@ pytest is for a module. For the whole suite use the runners, and note that a green pytest run does not mean CI will be green -- they gather and order modules differently. +Use `multiprocessTest` -- it runs on n-1 cores and finishes in about 10 seconds, +against roughly 45 for the single-core runner: + ```bash -# everyday full run, on n-1 cores uv run python music21/test/multiprocessTest.py +``` -# exactly what GitHub Actions runs (~1 minute); use before pushing to a PR +That gap is the whole story when someone is waiting on the answer. Reach for +`testSingleCoreAll` only for what `multiprocessTest` cannot tell you: it is what +GitHub Actions runs, so it is the one to check before a release, when chasing a +CI failure that will not reproduce, or on a single-core machine. + +```bash uv run python -c 'from music21.test.testSingleCoreAll import ciMain as ci; ci()' ``` The two runners see slightly different sets of modules -- `multiprocessTest` walks the package tree for modules reachable from `import music21`, while -`testSingleCoreAll` gathers module files from disk. Run both before a release. +`testSingleCoreAll` gathers module files from disk. Module **order** differs between the two runners and again from pytest. `multiprocessTest` goes in reverse-alphabetical order (with the known-slow From b6c0c06ae458d808b77bb650414ce70bcabe7fc8 Mon Sep 17 00:00:00 2001 From: Dylan Pulver Date: Tue, 1 Sep 2026 17:59:55 +0300 Subject: [PATCH 095/312] Give the flat-side accidentals their own unicode spellings unicodeFromModifier mapped three flat-side accidentals to glyphs that belong to something else, so Accidental.unicode could not tell them apart: triple-flat U+266D -> same string as flat half-flat U+1D132 -> QUARTER TONE SHARP one-and-a-half-flat U+266D U+1D132 -> flat + QUARTER TONE SHARP Thirteen accidental names collapsed to eleven distinct spellings. The sharp side was already right: '###' is sharp + double sharp, '~' is QUARTER TONE SHARP. The flat entries now mirror it, using U+1D133 MUSICAL SYMBOL QUARTER TONE FLAT (unused until now) and U+266D + U+1D12B for the triple flat. AI-assisted (Claude). --- music21/pitch.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index feb281a4da..251214fadb 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -5,7 +5,7 @@ # Authors: Michael Scott Asato Cuthbert # Christopher Ariza # -# Copyright: Copyright © 2008-2019 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2008-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -120,11 +120,11 @@ ('#', '\u266f'), ('~', chr(0x1d132)), # 1D132 ('----', chr(0x1d12b) + chr(0x1d12b)), - ('---', '\u266D'), + ('---', '\u266D' + chr(0x1d12b)), ('--', chr(0x1d12b)), - ('-`', '\u266D' + chr(0x1d132)), + ('-`', '\u266D' + chr(0x1d133)), # 1D133 ('-', '\u266D'), - ('`', chr(0x1d132)), # 1D132; raised flat: 1D12C + ('`', chr(0x1d133)), # 1D133; raised flat: 1D12C ('', '\u266e'), # natural ]) @@ -5444,6 +5444,21 @@ def testCopyAndDeepcopy(self) -> None: from music21.test.commonTest import testCopyAll testCopyAll(self, globals()) + def testUnicodeDistinguishesEveryAccidental(self) -> None: + ''' + Each accidental gets its own unicode spelling, and the flat side + mirrors the sharp side. AI-assisted (Claude). + ''' + spellings = {name: Accidental(name).unicode for name in accidentalNameToModifier} + self.assertEqual(len(set(spellings.values())), len(spellings)) + # QUARTER TONE FLAT, not QUARTER TONE SHARP + self.assertEqual(spellings['half-flat'], '\U0001d133') + self.assertEqual(spellings['one-and-a-half-flat'], '♭\U0001d133') + # three flats is a flat plus a double flat, as three sharps is a sharp + # plus a double sharp + self.assertEqual(spellings['triple-flat'], '♭\U0001d12b') + self.assertEqual(spellings['triple-sharp'], '♯\U0001d12a') + # define presented order in documentation _DOC_ORDER = [Pitch, Accidental, Microtone] From 717183de50e98e306b21c2c52aa7ff62a25db819 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Sep 2026 21:31:57 +0000 Subject: [PATCH 096/312] build(deps): bump tornado from 6.5.7 to 6.5.8 Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.5.7 to 6.5.8. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.5.7...v6.5.8) --- updated-dependencies: - dependency-name: tornado dependency-version: 6.5.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/uv.lock b/uv.lock index 3af3e5ad2f..4b8e2e6472 100644 --- a/uv.lock +++ b/uv.lock @@ -3107,19 +3107,19 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/64/24/95ec527ad67b76d59299e5465b3935d05e4294b7e0290a3924b7487df30b/tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2", size = 519252, upload-time = "2026-06-08T17:34:51.232Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/dc/c7043cab6fed8ae159fc1923ce829ada35c4dbd797d408a43858ffaf9639/tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163", size = 448543, upload-time = "2026-06-08T17:34:38.052Z" }, - { url = "https://files.pythonhosted.org/packages/92/4f/090b1431e5a43df696feceffc268c5383cc079ecb5f08ce58f917109aafe/tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100", size = 446707, upload-time = "2026-06-08T17:34:39.594Z" }, - { url = "https://files.pythonhosted.org/packages/37/d8/ef374952fd5da67d4463122c2b8e5a96536ec10b4b339254c6dcde81d01c/tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972", size = 449774, upload-time = "2026-06-08T17:34:41.204Z" }, - { url = "https://files.pythonhosted.org/packages/35/37/d434c73f4c6e014b745b9b37085f34f40c022f007efff3d7fe65991899f3/tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b", size = 450745, upload-time = "2026-06-08T17:34:42.531Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2b/56b9aff361d7f1ab728a805ec7d7ea835f8807afa9f5cc690ea0e630efb9/tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92", size = 450578, upload-time = "2026-06-08T17:34:43.787Z" }, - { url = "https://files.pythonhosted.org/packages/02/30/a7444fb23aa76860a14198fab96ac79f1866b0a6e19e26c4381b0938e50f/tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5", size = 449985, upload-time = "2026-06-08T17:34:45.326Z" }, - { url = "https://files.pythonhosted.org/packages/5c/42/5f0e56c01e8d9d36f4e23f367b85ae6cae0c1ecddd5e6977d8388ad27488/tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4", size = 451047, upload-time = "2026-06-08T17:34:46.784Z" }, - { url = "https://files.pythonhosted.org/packages/c9/a4/b393076ffb21b469eec5b328a0534cf03a3b90bfc6b1f09507cdd075d938/tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4", size = 451485, upload-time = "2026-06-08T17:34:48.248Z" }, - { url = "https://files.pythonhosted.org/packages/71/2e/7b1c769803121b809112cf9a00681c472eae1d80e32d7ec0e0bd61d0d0e1/tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796", size = 450506, upload-time = "2026-06-08T17:34:49.702Z" }, +version = "6.5.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/d3/343e5bb989d6515b1646cf3d40135d73f3d5e45339bded401b56cdac24dd/tornado-6.5.8.tar.gz", hash = "sha256:9452e1b208a8bd771e2cb1f2ff564985b9b214bdebbe622793e1799e0a6bd23f", size = 520493, upload-time = "2026-08-07T02:12:42.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/d5/007086fd8df5489338e204f65adce33fd4f21a4999dbb2b9cff2f897b5f4/tornado-6.5.8-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:cc6aa787d7cfab7c3d35189dc7a56fbd2399a569624c730c6b55b3d6531d0403", size = 449487, upload-time = "2026-08-07T02:12:28.682Z" }, + { url = "https://files.pythonhosted.org/packages/70/c8/5a24a99495903f594f6a199dd7beead1cbc0a13e2cb9102727bcaaf2a997/tornado-6.5.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9715b5eb79735b2bcd454ce216a9275b7c0470e64ea1bf5742f78b2f72b26eeb", size = 447649, upload-time = "2026-08-07T02:12:30.306Z" }, + { url = "https://files.pythonhosted.org/packages/6e/de/f2e733f386b85962d1b1dc82cd63d169b5b4580062b35397eac9244a41fe/tornado-6.5.8-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:547d63f450d570c14fe0e8db2cfb14c9bbd1c2503b4a6612586267955aa47b58", size = 450707, upload-time = "2026-08-07T02:12:31.95Z" }, + { url = "https://files.pythonhosted.org/packages/0b/94/20efeee9a01c141e9ac47c397f81679dfda24b32768fc4fff24e76d36c2c/tornado-6.5.8-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e2360a0ffbe145eca8af0b19cb7203d79b1a98dd4cccdd6b368f6f49c2e3808", size = 451677, upload-time = "2026-08-07T02:12:33.512Z" }, + { url = "https://files.pythonhosted.org/packages/42/ec/a96ccb8ccf0de2b7bc2c5fa1608a4803735018242e90c4882365a9fd418f/tornado-6.5.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5d242290bdf7ab3151bc1065fdd75c0dcc21cbc7b49f22a4c56329c2d6566d22", size = 451510, upload-time = "2026-08-07T02:12:35.346Z" }, + { url = "https://files.pythonhosted.org/packages/29/b5/93185859245ad3f00e62175f29607346788b696369347f0146e0421286bb/tornado-6.5.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7b94ff0e128fe0542f3bd331fb44d06260fc4ac16881545159f34ef08aad4195", size = 450917, upload-time = "2026-08-07T02:12:36.963Z" }, + { url = "https://files.pythonhosted.org/packages/97/cf/fe33cf062834487d34d1559746a4a12521033c22645b6d74d4bca702e018/tornado-6.5.8-cp39-abi3-win32.whl", hash = "sha256:67832909c4779c64942380cb5f044a5c6163d00831472d80e25e115de9917836", size = 451952, upload-time = "2026-08-07T02:12:38.512Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e1/468ad54333e92ccb62627e62cb88e5fc14a2171daa67ed47b1b8542d5b86/tornado-6.5.8-cp39-abi3-win_amd64.whl", hash = "sha256:11881db6b7c168494be2c2d12e65931451bdf7ee718535418ae1d8855dd5a0ee", size = 452391, upload-time = "2026-08-07T02:12:39.971Z" }, + { url = "https://files.pythonhosted.org/packages/ad/3e/cd5e4f06e34cde33b8ef66cf36aa2b5ad46354cc1af7d2136bbe365fee1d/tornado-6.5.8-cp39-abi3-win_arm64.whl", hash = "sha256:68a7468c7e289f8514d7d664101753903217eff1bb6822c6b5994a0b5f5bcb26", size = 451411, upload-time = "2026-08-07T02:12:41.469Z" }, ] [[package]] From e9c3c2b886adabc3b2547c732e5cc5b602ff2b20 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 1 Sep 2026 12:48:51 -1000 Subject: [PATCH 097/312] Pitch.octave is always an int (#2023) Implements #2009 -- p.octave += 1 is always safe now. --- .agents/skills/writing-docs/SKILL.md | 5 + AGENTS.md | 10 + documentation/source/about/index.rst | 1 + documentation/source/about/migratingToV11.rst | 268 ++++++++++++++++++ .../usersGuide/usersGuide_03_pitches.ipynb | 243 ++++++++++------ music21/capella/fromCapellaXML.py | 2 +- music21/chord/__init__.py | 15 +- music21/expressions.py | 21 +- music21/figuredBass/realizerScale.py | 2 +- music21/figuredBass/segment.py | 6 +- music21/harmony.py | 4 - music21/interval.py | 27 +- music21/key.py | 10 +- music21/lily/translate.py | 8 +- music21/musedata/base40.py | 4 +- music21/musicxml/m21ToXml.py | 4 +- music21/note.py | 16 +- music21/pitch.py | 202 +++++++------ music21/roman.py | 12 +- music21/scale/__init__.py | 21 +- music21/scale/intervalNetwork.py | 44 ++- music21/serial.py | 8 +- music21/stream/tests.py | 4 +- music21/test/test_pitch.py | 76 ++++- 24 files changed, 728 insertions(+), 285 deletions(-) create mode 100644 documentation/source/about/migratingToV11.rst diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 0e393cfcc8..8950cd7ec8 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -47,8 +47,13 @@ what a reader must act on; drop the before-picture. * Changed in v11: `stringOutput()` always returns a `str`, never None. * Changed in v11: emits `\tuplet`; the arguments are now actual, normal. * New in v11. +* Deprecated in v11: use `.octave`, which is now always an int. ``` +A deprecated function or method also gets `@common.deprecated(...)`. A property +gets only the marker, since IDEs read every property while inspecting an object; +leave `# Add real deprecation message here in vX` in its body instead. + A plain bug fix — code now does what it always claimed — gets no marker and no doctest. It goes in the commit message. diff --git a/AGENTS.md b/AGENTS.md index 6b9738c6f2..2e7640dcab 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,10 @@ `environLocal.printDebug(...)`, or `environLocal.warn(...)` when the user should hear about it every time. `test/toggleDebug.py` switches debug output on and off. - New exceptions subclass `exceptions21.Music21Exception`. +- Deprecate a function or method with `@common.deprecated(...)`. Never put it on a + property: IDEs read every property while inspecting an object, so the warning would + fire on people who never used it. For a property, write `* Deprecated in vX: use Y.` + in the docstring and leave `# Add real deprecation message here in vY` in the body. - Return named things — a small class, a `namedtuple`, a dict — never a positional tuple of more than 2 (maybe 3) elements whose elements each mean something different and unrelated (x, y, z is okay for instance). Nobody should write `returned[3][0][7]`. - Don't reuse a variable name once the type of what it holds changes @@ -128,6 +132,12 @@ after addressing the problem. (A blind close or close with "not accepted" etc. generally means that the issue/PR has too many problems to easily solve and has become a burden for the maintainer). - Do not include a "Tests run" section unless the testing procedure was unusual (like it affects part of the system without standard tests, like the testing system itself.) +- While someone is reviewing a PR or a pushed branch, "do X" is not "commit and push X": + make the change and leave it unstaged. When the list looks finished (or you hear "done!" + or "push it"), offer to commit, or to commit and push. Batch a round's small fixes into + one commit; no micro-commit trains. Prefer new commits to amend + force-push, since the + reviewer may have pulled the branch; if asked to fold a fix into the commit it changes, + amend, force-push with `--force-with-lease` against an explicit SHA, and say so. # Writing style diff --git a/documentation/source/about/index.rst b/documentation/source/about/index.rst index ee319489b2..d0be870496 100644 --- a/documentation/source/about/index.rst +++ b/documentation/source/about/index.rst @@ -8,4 +8,5 @@ About `music21` about applications faq + migratingToV11 referenceCorpus diff --git a/documentation/source/about/migratingToV11.rst b/documentation/source/about/migratingToV11.rst new file mode 100644 index 0000000000..acc455dfde --- /dev/null +++ b/documentation/source/about/migratingToV11.rst @@ -0,0 +1,268 @@ +.. _migratingToV11: + +Migrating to music21 v11 +======================== + +*(This is an in-progress guide that was automatically generated by Myke's AI Agent.)* + +What changed between the v10 line and v11, and what to use instead. The big, code-breaking items come first, then smaller +improvements by area, then one table of everything removed or deprecated. + + +Python 3.12 or newer +-------------------- + +Python 3.11 support is gone; 3.12, 3.13, and 3.14 are tested. The payoff is +real generics: ``Stream`` and its iterators use the PEP 695 bracket form, so a +type checker can follow ``stream.Stream[note.Note]()`` all the way to +``for n in s: n.pitch``. + + +Octaves are always integers +--------------------------- + +**In one line:** ``octave`` is always an ``int``, and ``octaveIsImplicit`` says whether you gave one. + +Before v11, ``pitch.Pitch('F#').octave`` was ``None``: a lovely idea (an +F-sharp in *any* octave) that crashed the moment someone wrote +``p.octave + 1``. The pitch knew all along which octave it would use for +MIDI or a staff. It kept that in ``implicitOctave``, a property that nobody +remembered. Now ``octave`` does that job itself: + +>>> from music21 import * +>>> anyFSharp = pitch.Pitch('F#') +>>> anyFSharp.octave +4 +>>> anyFSharp.octaveIsImplicit +True + +Nothing else about an octave-less pitch changes. It still prints without a +number, transposes without one, and is not equal to an explicit F#4: + +>>> anyFSharp + +>>> anyFSharp.transpose('P8') + +>>> anyFSharp == pitch.Pitch('F#4') +False + +Give it an octave and the flag flips: + +>>> anyFSharp.octave = 5 +>>> anyFSharp.octaveIsImplicit +False +>>> anyFSharp + + +Notes follow along: ``note.Note('B-').octave`` is ``4``, and the flag lives +on the note's pitch, ``n.pitch.octaveIsImplicit``. + +.. list-table:: + :header-rows: 1 + :widths: 50 50 + + * - Before v11 + - v11 + * - ``if p.octave is None:`` + - ``if p.octaveIsImplicit:`` + * - ``p.octave = None`` + - ``p.octaveIsImplicit = True`` (the old spelling is deprecated; removed in v13) + * - ``p.implicitOctave`` + - ``p.octave`` + * - ``if p.octave is None: p.octave = p.implicitOctave`` + - ``p.octaveIsImplicit = False`` + * - ``octave: int | None`` + - ``octave: int`` + +``p.octave + 1`` and ``f'{p.name}{p.octave}'`` no longer crash. The few +methods that need a pitch's *own* octave, such as +:meth:`~music21.pitch.Pitch.transposeAboveTarget`, still raise for an +implicit one; set ``.octave`` first. ``implicitOctave`` stays as a synonym +so nothing breaks today, but it is deprecated and starts warning in v12. + + +KeySignature.sharps is always an int +------------------------------------ + +Similar idea to Octave. ``KeySignature.sharps`` (and ``Key.sharps``) is an +``int`` you can add to and subtract from. A non-traditional signature is a +flag plus a list of pitches. ``sharps=None`` still works but warns: + +>>> unusual = key.KeySignature() +>>> unusual.isNonTraditional = True +>>> unusual.alteredPitches = ['E-', 'G#4'] +>>> unusual + + + +Chord indexing gets simpler +--------------------------- + +``c[1]``, ``c['G4']`` and ``c[somePitch]`` still return the chord's +:class:`~music21.note.Note`; everything fancier is gone. Attribute paths +such as ``c['2.tie']`` and ``c['D-4.style.color']`` no longer work, a Note is +no longer accepted as a key (use its pitch), and the per-note getters and +setters (``getTie``, ``setColor``, ``getNotehead``, and their siblings) are +deprecated. Index, then use the Note: + +>>> c = chord.Chord('C4 E4 G4') +>>> c['E4'].tie = tie.Tie('start') +>>> c[1].tie + + + +VoiceLeadingQuartet needs its four notes +---------------------------------------- + +``voiceLeading.VoiceLeadingQuartet(v1n1, v1n2, v2n1, v2n2)`` requires all +four notes up front, the first Music21Object ever to insist on arguments. In +return they are always Notes, never ``None``. ``analyticKey`` is now +``key``, ``vIntervals`` and ``hIntervals`` are tuples, and two dogmatic names +got truer ones: ``opensIncorrectly()`` is ``not modalOpening()`` and +``closesIncorrectly()`` is ``not clausulaVera()``. + + +Roman numerals +-------------- + +* ``romanNumeralFromChord`` keeps the sharp on major-quality chords built on + the raised sixth and seventh degrees in minor: ``#VI``, not ``VI#63``. + ``correctRNAlterationForMinor`` gained a ``chordHasMajorThird`` keyword for + the same reason. +* Figures containing ``x``, ``y`` or ``z`` raise. ``RomanNumeral('IIIx')`` + used to be a III13 chord by accident. +* ``FigureTuple`` and ``PitchFigureTuple`` are typed NamedTuples, and + ``FigureTuple.fromPitchAndReference()`` replaces ``figureTupleSolo``. + + +Durations and sorting +--------------------- + +* Two Durations with ``expressionIsInferred`` True are equal when their + quarterLengths match; type, dots and tuplets are free to be re-expressed. +* ``sorting.SortTuple`` is a modern NamedTuple. ``priority`` and + ``classSortOrder`` may be floats, and ``modify()`` with a bad field name + raises ``ValueError``. +* tinyNotation's undocumented ``0`` duration (a whole bar plus a fermata) is + deprecated. + + +Streams +------- + +* New ``Stream.isAtEnd(el)`` says whether ``storeAtEnd`` put ``el`` there. +* ``Stream.hasElement(el)`` is removed: write ``el in s``. + ``hasElementOfClass`` is deprecated: write ``if s.getElementsByClass(X):``. + + +Pitches +------- + +``pitch.simplifyMultipleEnharmonics`` takes ``criterion`` and ``keyContext`` +as keyword-only arguments. + + +File formats +------------ + +* **ABC**: ``w:`` lyric lines are imported, with hyphenation and ``*`` skips. + ``abcToStreamOpus`` always returns an Opus. +* **Humdrum**: grace notes keep their written duration instead of becoming + eighths; duration parsing lives in a new ``hdStringToDuration``; a token + with no duration warns when a quarter is assumed; ``GlobalReference`` is no + longer a Music21Object. +* **LilyPond**: output targets current LilyPond (``\tuplet``, modern barline + names, ``\markuplist``), and every ``stringOutput()`` returns a ``str``. +* **MIDI**: ``midiEventsToInstrument`` is removed; use + ``midiEventToInstrument``. +* **Vexflow**: the ``music21.vexflow`` module is gone. It had not worked in + a decade; music21j is the way to draw in a browser. +* **configure**: choosing MuseScore as the MusicXML reader also sets + ``musescoreDirectPNGPath``. + + +figuredBass, features, tree +--------------------------- + +* figuredBass: ``hiddenFifth`` and ``hiddenOctave`` are now ``hiddenFifths`` + and ``hiddenOctaves``, matching ``parallelFifths``. ``FiguredBassScale``'s + first argument is ``scaleTonic``, not ``scaleValue``. Rests inside a + possibility are typed sentinels rather than the string ``'RT'``. +* features: ``Feature.vector`` starts as ``[]`` rather than ``None``, and + ``FeatureExtractor.dimensions`` defaults to 1. +* tree: ``ElementTimespan`` and ``PitchedTimespan`` take their arguments in + the same order as ``Timespan``; ``offset``, ``endTime`` and ``element`` are + required. + + +For developers +-------------- + +* Type annotations across nearly the whole library, with ``t.cast()`` for + narrowing and ``@property`` decorators throughout. +* ``common.enums.ContainsEnum`` is not needed: Python 3.12's ``StrEnum`` + suffices, or ``HexEnum`` for hex values such as MIDI. The alias leaves in + v12. ``common.defaultlist`` is deprecated. +* The test runners import modules the normal way, so a module's tests no + longer need ``from music21.key import KeySignature`` to compare types. +* AI agents get their own instructions, skills and shared memory in + ``AGENTS.md`` and ``.agents/``. + + +Removed and deprecated +---------------------- + +.. list-table:: Removed in v11 + :header-rows: 1 + :widths: 50 50 + + * - Gone + - Use instead + * - Python 3.11 + - Python 3.12 or newer + * - ``music21.vexflow`` + - music21j + * - ``midi.translate.midiEventsToInstrument`` + - ``midiEventToInstrument`` + * - ``Stream.hasElement(el)`` + - ``el in s`` + * - ``musicxml.xmlToM21.MusicXMLImporter.identificationToMetadata`` + - ``addIdentificationToMetadata`` + * - a string for ``musicxml.xmlToM21.MeasureParser.getStaffNumber`` + - an ``int`` + * - ``VoiceLeadingQuartet.unison`` / ``.fifth`` / ``.octave`` + - ``interval.Interval('P1')`` and friends + * - ``c['2.tie']`` attribute paths into a Chord + - ``c[2].tie`` + * - a Note as a Chord index, ``c[someNote]`` + - ``c[someNote.pitch]`` + +.. list-table:: Deprecated in v11 (removed in v12 unless noted) + :header-rows: 1 + :widths: 50 50 + + * - Deprecated + - Use instead + * - ``Stream.hasElementOfClass(X)`` + - ``if s.getElementsByClass(X):`` + * - ``Chord.getTie``, ``setTie``, ``getColor``, ``setColor``, + ``getNotehead``, ``setNotehead``, ``getNoteheadFill``, + ``setNoteheadFill``, ``getStemDirection``, ``setStemDirection`` + - ``c[i].tie``, ``c[i].style.color``, ``c[i].notehead``, + ``c[i].noteheadFill``, ``c[i].stemDirection`` + * - ``roman.figureTupleSolo`` + - ``FigureTuple.fromPitchAndReference()`` + * - ``VoiceLeadingQuartet.opensIncorrectly()`` / ``closesIncorrectly()`` + - ``not modalOpening()`` / ``not clausulaVera()`` + * - ``KeySignature(sharps=None)`` + - ``ks.isNonTraditional = True`` + * - ``variant.addVariant(replacementDuration=...)`` + - ``replacementQuarterLength=...`` + * - ``common.defaultlist`` + - a ``list`` or a ``dict`` + * - ``common.enums.ContainsEnum`` + - not needed: ``enum.StrEnum`` suffices, or ``HexEnum`` for hex values such as MIDI + * - tinyNotation ``0`` duration + - the duration plus an ``expressions.Fermata`` + * - ``Pitch.implicitOctave`` + - ``Pitch.octave`` (warns from v12, removed later) diff --git a/documentation/source/usersGuide/usersGuide_03_pitches.ipynb b/documentation/source/usersGuide/usersGuide_03_pitches.ipynb index 2026ffef60..40c4f43f81 100644 --- a/documentation/source/usersGuide/usersGuide_03_pitches.ipynb +++ b/documentation/source/usersGuide/usersGuide_03_pitches.ipynb @@ -54,7 +54,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -85,7 +85,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -94,7 +94,7 @@ "4" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -105,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -114,7 +114,7 @@ "10" ] }, - "execution_count": 4, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -125,7 +125,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -134,7 +134,7 @@ "'B-'" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -145,7 +145,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -154,7 +154,7 @@ "-1.0" ] }, - "execution_count": 6, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -177,7 +177,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -186,7 +186,7 @@ "'B-4'" ] }, - "execution_count": 7, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -197,7 +197,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -206,7 +206,7 @@ "70" ] }, - "execution_count": 8, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -215,6 +215,91 @@ "p1.midi" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Leave the octave off and the `Pitch` means \"a B-flat in *any* octave.\"\n", + "It prints without a number:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "anyBFlat = pitch.Pitch('B-')\n", + "anyBFlat" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But `music21` often needs an octave anyhow, to play the pitch or put it\n", + "on a staff, so `.octave` still answers with the default, `4` (the octave\n", + "of middle C), and `.octaveIsImplicit` remembers that you didn't give one:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "anyBFlat.octave" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "anyBFlat.octaveIsImplicit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set `.octave` yourself and `.octaveIsImplicit` becomes `False`." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -233,7 +318,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -242,7 +327,7 @@ "'D#3'" ] }, - "execution_count": 9, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -263,7 +348,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -272,7 +357,7 @@ "" ] }, - "execution_count": 10, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -306,7 +391,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -315,7 +400,7 @@ "'C#'" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -327,7 +412,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -336,7 +421,7 @@ "'C#'" ] }, - "execution_count": 12, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -347,7 +432,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -356,7 +441,7 @@ "4" ] }, - "execution_count": 13, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -367,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -376,7 +461,7 @@ "4" ] }, - "execution_count": 14, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -394,7 +479,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -403,7 +488,7 @@ "'do sostenido'" ] }, - "execution_count": 15, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -421,7 +506,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": { "tags": [ "nbval-raises-exception" @@ -453,7 +538,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -477,7 +562,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -550,7 +635,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -583,7 +668,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -604,7 +689,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -613,7 +698,7 @@ "1.5" ] }, - "execution_count": 21, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -624,7 +709,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -633,7 +718,7 @@ "2.0" ] }, - "execution_count": 22, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -652,7 +737,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -661,7 +746,7 @@ "'half'" ] }, - "execution_count": 23, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -672,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -681,7 +766,7 @@ "'quarter'" ] }, - "execution_count": 24, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -703,7 +788,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -712,7 +797,7 @@ "0" ] }, - "execution_count": 25, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -723,7 +808,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 28, "metadata": {}, "outputs": [ { @@ -732,7 +817,7 @@ "1" ] }, - "execution_count": 26, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -754,7 +839,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -763,7 +848,7 @@ "1.75" ] }, - "execution_count": 27, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -775,7 +860,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -784,7 +869,7 @@ "1.875" ] }, - "execution_count": 28, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -796,7 +881,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -805,7 +890,7 @@ "1.9375" ] }, - "execution_count": 29, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -825,7 +910,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -834,7 +919,7 @@ "'16th'" ] }, - "execution_count": 30, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -846,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -855,7 +940,7 @@ "0" ] }, - "execution_count": 31, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -913,7 +998,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -922,7 +1007,7 @@ "" ] }, - "execution_count": 47, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -934,7 +1019,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -943,7 +1028,7 @@ "" ] }, - "execution_count": 48, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -961,7 +1046,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -978,7 +1063,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -987,7 +1072,7 @@ "'half'" ] }, - "execution_count": 50, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -998,7 +1083,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -1007,7 +1092,7 @@ "1" ] }, - "execution_count": 51, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1018,7 +1103,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -1027,7 +1112,7 @@ "'E-'" ] }, - "execution_count": 52, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1038,7 +1123,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -1047,7 +1132,7 @@ "" ] }, - "execution_count": 53, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1058,7 +1143,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -1067,7 +1152,7 @@ "5" ] }, - "execution_count": 54, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1087,7 +1172,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -1096,7 +1181,7 @@ "'E-'" ] }, - "execution_count": 55, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1107,7 +1192,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -1116,7 +1201,7 @@ "3.0" ] }, - "execution_count": 56, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1134,7 +1219,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -1153,7 +1238,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ @@ -1173,7 +1258,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 46, "metadata": {}, "outputs": [], "source": [ @@ -1191,7 +1276,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -1215,7 +1300,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 48, "metadata": {}, "outputs": [ { @@ -1250,7 +1335,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 49, "metadata": {}, "outputs": [ { diff --git a/music21/capella/fromCapellaXML.py b/music21/capella/fromCapellaXML.py index 820cf6e967..8730568e7e 100644 --- a/music21/capella/fromCapellaXML.py +++ b/music21/capella/fromCapellaXML.py @@ -539,7 +539,7 @@ def noteFromHead(self, headElement: ET.Element) -> note.Note: noteNameWithOctave = headElement.attrib['pitch'] n = note.Note() n.nameWithOctave = noteNameWithOctave - n.octave = n.pitch.implicitOctave - 1 # capella octaves are 1 off + n.octave = n.pitch.octave - 1 # capella octaves are 1 off alters = headElement.findall('alter') if len(alters) > 1: diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index 3c5a16330c..a0d6789f68 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -1544,9 +1544,6 @@ def closedPosition( pBass = returnObj.bass() # returns a reference, not a copy if forceOctave is not None: pBassOctave = pBass.octave - if pBassOctave is None: - pBassOctave = pBass.implicitOctave - if pBassOctave > forceOctave: dif = -1 elif pBassOctave < forceOctave: @@ -1557,16 +1554,13 @@ def closedPosition( while pBass.octave != forceOctave: # shift octave of all pitches for p in returnObj.pitches: - if p.octave is None: - p.octave = p.implicitOctave p.octave += dif # can change these pitches in place for p in returnObj.pitches: # bring each pitch down octaves until pitch space is # within an octave - if p.octave is None: - p.octave = p.implicitOctave + p.octaveIsImplicit = False while p.ps >= pBass.ps + 12: p.octave -= 1 # check for a bass of C4 and the note B#7 added to it, should be B#4 not B#3... @@ -2394,10 +2388,7 @@ def _setInversion( currentMaxMidi = max(self.pitches).ps tempBassPitch = self.bass() while tempBassPitch.ps < currentMaxMidi: - if tempBassPitch.octave is not None: - tempBassPitch.octave += 1 - else: - tempBassPitch.octave = tempBassPitch.implicitOctave + 1 + tempBassPitch.octave += 1 # housekeeping for next loop tests self.clearCache() @@ -4012,7 +4003,7 @@ def semiClosedPosition( if p.step not in usedSteps: usedSteps.append(p.step) else: - p.octave = p.implicitOctave + 1 + p.octave += 1 newRemainingPitches.append(p) remainingPitches = newRemainingPitches diff --git a/music21/expressions.py b/music21/expressions.py index fbecb96bf7..bcbf71a59d 100644 --- a/music21/expressions.py +++ b/music21/expressions.py @@ -760,8 +760,7 @@ def getSize( ornamentalPitch: pitch.Pitch = copy.deepcopy(srcPitch) ornamentalPitch.accidental = None - if ornamentalPitch.octave is None: - ornamentalPitch.octave = ornamentalPitch.implicitOctave + ornamentalPitch.octaveIsImplicit = False if self._direction == 'up': ornamentalPitch.transpose(interval.GenericInterval(2), inPlace=True) @@ -828,8 +827,7 @@ def resolveOrnamentalPitches( transposeInterval: interval.IntervalBase = self.getSize(srcObj, keySig=keySig) ornamentalPitch: pitch.Pitch = copy.deepcopy(srcPitch) - if ornamentalPitch.octave is None: - ornamentalPitch.octave = ornamentalPitch.implicitOctave + ornamentalPitch.octaveIsImplicit = False ornamentalPitch.transpose(transposeInterval, inPlace=True) # if there are microtones, see if they can be converted to quarter tones. if ornamentalPitch.microtone.cents != 0: @@ -1375,8 +1373,7 @@ def getSize( ornamentalPitch: pitch.Pitch = copy.deepcopy(srcPitch) ornamentalPitch.accidental = None - if ornamentalPitch.octave is None: - ornamentalPitch.octave = ornamentalPitch.implicitOctave + ornamentalPitch.octaveIsImplicit = False if self._direction == 'up': ornamentalPitch.transpose(interval.GenericInterval(2), inPlace=True) @@ -1424,8 +1421,7 @@ def resolveOrnamentalPitches( transposeInterval: interval.IntervalBase = self.getSize(srcObj, keySig=keySig) ornamentalPitch: pitch.Pitch = copy.deepcopy(srcPitch) - if ornamentalPitch.octave is None: - ornamentalPitch.octave = ornamentalPitch.implicitOctave + ornamentalPitch.octaveIsImplicit = False ornamentalPitch.transpose(transposeInterval, inPlace=True) # if there are microtones, see if they can be converted to quarter tones. if ornamentalPitch.microtone.cents != 0: @@ -2043,8 +2039,7 @@ def getSize( ornamentalPitch: pitch.Pitch = copy.deepcopy(srcPitch) ornamentalPitch.accidental = None - if ornamentalPitch.octave is None: - ornamentalPitch.octave = ornamentalPitch.implicitOctave + ornamentalPitch.octaveIsImplicit = False accidental: pitch.Accidental|None = None if which == 'upper': @@ -2119,16 +2114,14 @@ def resolveOrnamentalPitches( srcObj, 'lower', keySig=keySig) upperPitch: pitch.Pitch = copy.deepcopy(srcPitch) - if upperPitch.octave is None: - upperPitch.octave = upperPitch.implicitOctave + upperPitch.octaveIsImplicit = False upperPitch.transpose(transposeIntervalUp, inPlace=True) # if there are microtones, see if they can be converted to quarter tones. if upperPitch.microtone.cents != 0: upperPitch.convertMicrotonesToQuarterTones(inPlace=True) lowerPitch: pitch.Pitch = copy.deepcopy(srcPitch) - if lowerPitch.octave is None: - lowerPitch.octave = lowerPitch.implicitOctave + lowerPitch.octaveIsImplicit = False lowerPitch.transpose(transposeIntervalDown, inPlace=True) # if there are microtones, see if they can be converted to quarter tones. if lowerPitch.microtone.cents != 0: diff --git a/music21/figuredBass/realizerScale.py b/music21/figuredBass/realizerScale.py index 3f3345fc19..a1b6575dcd 100644 --- a/music21/figuredBass/realizerScale.py +++ b/music21/figuredBass/realizerScale.py @@ -203,7 +203,7 @@ def getPitches(self, bassPitch = convertToPitch(bassPitch) maxPitch = convertToPitch(maxPitch) pitchNames = self.getPitchNames(bassPitch, notationString) - maxOctave = maxPitch.implicitOctave + maxOctave = maxPitch.octave iter1 = itertools.product(pitchNames, range(maxOctave + 1)) iter2 = map(lambda x: pitch.Pitch(x[0] + str(x[1])), iter1) iter3 = itertools.filterfalse(lambda samplePitch: bassPitch > samplePitch, iter2) diff --git a/music21/figuredBass/segment.py b/music21/figuredBass/segment.py index 5bf60d4818..466ccca6d0 100644 --- a/music21/figuredBass/segment.py +++ b/music21/figuredBass/segment.py @@ -930,15 +930,15 @@ def getPitches(pitchNames: Iterable[str] = ('C', 'E', 'G'), >>> segment.getPitches(maxPitch=pitch.Pitch('E')) Traceback (most recent call last): - ValueError: maxPitch must be given an octave + ValueError: maxPitch must not have an implicit octave ''' if isinstance(bassPitch, str): bassPitch = pitch.Pitch(bassPitch) if isinstance(maxPitch, str): maxPitch = pitch.Pitch(maxPitch) - if maxPitch.octave is None: - raise ValueError('maxPitch must be given an octave') + if maxPitch.octaveIsImplicit: + raise ValueError('maxPitch must not have an implicit octave') iter1 = itertools.product(pitchNames, range(maxPitch.octave + 1)) iter2 = map(lambda x: pitch.Pitch(x[0] + str(x[1])), iter1) iter3 = itertools.filterfalse(lambda samplePitch: bassPitch > samplePitch, iter2) diff --git a/music21/harmony.py b/music21/harmony.py index f9d152f2c7..1210fc6ad8 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -2676,10 +2676,6 @@ def testHarmonyPreservesInversionAndBass(self): self.assertEqual(explicitFm6.root(find=False).name, 'F') fm6bassOctave = explicitFm6.bass(find=False).octave fm6rootOctave = explicitFm6.root(find=False).octave - self.assertIsNotNone(fm6bassOctave) - self.assertIsNotNone(fm6rootOctave) - assert fm6bassOctave is not None - assert fm6rootOctave is not None self.assertLess(fm6bassOctave, fm6rootOctave) def testClassSortOrderHarmony(self): diff --git a/music21/interval.py b/music21/interval.py index 2390cdaa1b..01cac6ca4f 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -1409,10 +1409,7 @@ def transposePitch(self, p: pitch.Pitch, *, inPlace=False): >>> gSharp ''' - if p.octave is None: - useImplicitOctave = True - else: - useImplicitOctave = False + useImplicitOctave = p.octaveIsImplicit pitchDNN = p.diatonicNoteNum if inPlace: @@ -1422,7 +1419,7 @@ def transposePitch(self, p: pitch.Pitch, *, inPlace=False): newPitch.diatonicNoteNum = pitchDNN + self.staffDistance if useImplicitOctave: - newPitch.octave = None + newPitch.octaveIsImplicit = True if not inPlace: return newPitch @@ -2500,10 +2497,7 @@ def transposePitch(self, p: pitch.Pitch, *, inPlace=False): * Changed in v6: added inPlace. ''' - if p.octave is None: - useImplicitOctave = True - else: - useImplicitOctave = False + useImplicitOctave = p.octaveIsImplicit pps = p.ps if not inPlace: @@ -2512,8 +2506,8 @@ def transposePitch(self, p: pitch.Pitch, *, inPlace=False): newPitch = p newPitch.ps = pps + self.semitones - if useImplicitOctave is True: - newPitch.octave = None + if useImplicitOctave: + newPitch.octaveIsImplicit = True if not inPlace: return newPitch @@ -3426,8 +3420,8 @@ def transposePitch(self, maxAccidental=maxAccidental, ) - if p.fundamental.octave is None: - pOut.fundamental.octave = None + if p.fundamental.octaveIsImplicit: + pOut.fundamental.octaveIsImplicit = True if not inPlace: return pOut @@ -3450,10 +3444,7 @@ def _diatonicTransposePitch(self, # true unison and any multiple of true octave inheritAccidentalDisplayStatus = True - if p.octave is None: - useImplicitOctave = True - else: - useImplicitOctave = False + useImplicitOctave = p.octaveIsImplicit pitch1 = p pitch2 = copy.deepcopy(pitch1) @@ -3543,7 +3534,7 @@ def _diatonicTransposePitch(self, pitch2.microtone = pitch2.microtone.cents + centsOrigin if useImplicitOctave: - pitch2.octave = None + pitch2.octaveIsImplicit = True if not inPlace: return pitch2 diff --git a/music21/key.py b/music21/key.py index ffc80d7ca4..631a49a13c 100644 --- a/music21/key.py +++ b/music21/key.py @@ -127,7 +127,7 @@ def sharpsToPitch(sharpCount: int) -> pitch.Pitch: return copy.deepcopy(_sharpsToPitchCache[sharpCount]) pitchInit = pitch.Pitch('C') - pitchInit.octave = None + pitchInit.octaveIsImplicit = True # keyPc = (self.sharps * 7) % 12 if sharpCount > 0: intervalStr = 'P5' @@ -139,7 +139,7 @@ def sharpsToPitch(sharpCount: int) -> pitch.Pitch: intervalObj = interval.Interval(intervalStr) for i in range(abs(sharpCount)): pitchInit = intervalObj.transposePitch(pitchInit) - pitchInit.octave = None + pitchInit.octaveIsImplicit = True _sharpsToPitchCache[sharpCount] = pitchInit return pitchInit @@ -518,7 +518,7 @@ def alteredPitches(self) -> list[pitch.Pitch]: for i in range(self.sharps): pKeep.transpose('P5', inPlace=True) p = copy.deepcopy(pKeep) - p.octave = None + p.octaveIsImplicit = True post.append(p) elif self.sharps < 0: @@ -526,7 +526,7 @@ def alteredPitches(self) -> list[pitch.Pitch]: for i in range(abs(self.sharps)): pKeep.transpose('P4', inPlace=True) p = copy.deepcopy(pKeep) - p.octave = None + p.octaveIsImplicit = True post.append(p) return post @@ -780,7 +780,7 @@ def transposePitchFromC(self, p: pitch.Pitch, *, inPlace=False) -> pitch.Pitch|N for i in range(transTimes): transInterval.transposePitch(p, inPlace=True) - if originalOctave is not None: + if not p.octaveIsImplicit: p.octave = originalOctave if not inPlace: diff --git a/music21/lily/translate.py b/music21/lily/translate.py index c573246baa..335c9728e4 100644 --- a/music21/lily/translate.py +++ b/music21/lily/translate.py @@ -1514,12 +1514,12 @@ def octaveCharactersFromPitch(self, pitchObj: pitch.Pitch) -> str: returns a string of single-quotes or commas or '' representing the octave of a :class:`~music21.pitch.Pitch` object ''' - implicitOctave = pitchObj.implicitOctave - if implicitOctave < 3: - correctedOctave = 3 - implicitOctave + octave = pitchObj.octave + if octave < 3: + correctedOctave = 3 - octave octaveModChars = ',' * correctedOctave # C2 = c, C1 = c,, else: - correctedOctave = implicitOctave - 3 + correctedOctave = octave - 3 octaveModChars = "'" * correctedOctave # C4 = c', C5 = c'' etc. return octaveModChars diff --git a/music21/musedata/base40.py b/music21/musedata/base40.py index 073537e524..3a4d6c5417 100644 --- a/music21/musedata/base40.py +++ b/music21/musedata/base40.py @@ -233,7 +233,7 @@ def base40ToPitch(base40Num: int) -> pitch.Pitch: ''' p = pitch.Pitch() p.octave = ((base40Num - 1) / 40) + 1 - tableNum = base40Num - 40 * (p.implicitOctave - 1) + tableNum = base40Num - 40 * (p.octave - 1) pitchName = base40Equivalent.get(tableNum, '') if pitchName: p.name = pitchName @@ -266,7 +266,7 @@ def pitchToBase40(pitchToConvert: str|pitch.Pitch) -> int: pitchObj = pitchToConvert if pitchObj.name in base40Representation: tableNum = base40Representation[pitchObj.name] - base40Num = (40 * (pitchObj.implicitOctave - 1)) + tableNum + base40Num = (40 * (pitchObj.octave - 1)) + tableNum return base40Num raise Base40Exception('Base40 cannot handle this pitch ' + pitchObj.nameWithOctave) diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index 605810d917..e54b522059 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -4580,7 +4580,7 @@ def pitchToXml(self, p: pitch.Pitch): if p.accidental is not None: mxAlter = SubElement(mxPitch, 'alter') mxAlter.text = str(common.numToIntOrFloat(p.accidental.alter)) - _setTagTextFromAttribute(p, mxPitch, 'octave', 'implicitOctave') + _setTagTextFromAttribute(p, mxPitch, 'octave') return mxPitch def unpitchedToXml(self, @@ -7397,7 +7397,7 @@ def keySignatureToXml(self, keyOrKeySignature: key.KeySignature) -> Element: # TODO: key-accidental for i, p in enumerate(keyOrKeySignature.alteredPitches): - if p.octave is not None: + if not p.octaveIsImplicit: mxKeyOctave = SubElement(mxKey, 'key-octave') mxKeyOctave.text = str(p.octave) mxKeyOctave.set('number', str(i + 1)) diff --git a/music21/note.py b/music21/note.py index 9c0ff2a282..1d69923fce 100644 --- a/music21/note.py +++ b/music21/note.py @@ -5,7 +5,7 @@ # Authors: Michael Scott Asato Cuthbert # Christopher Ariza # -# Copyright: Copyright © 2006-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2006-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -1446,10 +1446,10 @@ class Note(NotRest): >>> n = note.Note('B-') >>> n.name 'B-' - >>> n.octave is None - True - >>> n.pitch.implicitOctave + >>> n.octave 4 + >>> n.pitch.octaveIsImplicit + True >>> n = note.Note(name='D#') >>> n.name @@ -1657,15 +1657,17 @@ def step(self, value: StepName): self.pitch.step = value @property - def octave(self) -> int|None: + def octave(self) -> int: ''' Return or set the octave value from the :class:`~music21.pitch.Pitch` object. See :attr:`~music21.pitch.Pitch.octave`. + + * Changed in v11: always an int; see :attr:`~music21.pitch.Pitch.octaveIsImplicit`. ''' return self.pitch.octave @octave.setter - def octave(self, value: int|None): + def octave(self, value: int|float|None): self.pitch.octave = value @property @@ -1886,7 +1888,7 @@ def __init__( if displayName: display_pitch = Pitch(displayName) self.displayStep = display_pitch.step - self.displayOctave = display_pitch.implicitOctave + self.displayOctave = display_pitch.octave def _reprInternal(self): if not self.storedInstrument: diff --git a/music21/pitch.py b/music21/pitch.py index feb281a4da..dba07baa3e 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -5,7 +5,7 @@ # Authors: Michael Scott Asato Cuthbert # Christopher Ariza # -# Copyright: Copyright © 2008-2019 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2008-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -579,7 +579,7 @@ def _dissonanceScore(pitches: list[Pitch], intervals = [] for p1, p2 in itertools.combinations(pitches, 2): p2 = copy.deepcopy(p2) - p2.octave = None + p2.octaveIsImplicit = True this_interval = interval.Interval(noteStart=p1, noteEnd=p2) intervals.append(this_interval) except interval.IntervalException: @@ -1681,31 +1681,29 @@ class Pitch(prebase.ProtoM21Object): >>> alters [1.0, -1.0] - If a `Pitch` doesn't have an associated octave, then its - `.octave` value is None. This means that it represents - any G#, regardless of octave. Transposing this note up - an octave doesn't change anything. + A `Pitch` created without an octave represents any G#, regardless + of octave: it prints without an octave number, and transposing it + up an octave changes nothing. >>> anyGSharp = pitch.Pitch('G#') - >>> anyGSharp.octave is None - True + >>> anyGSharp + >>> print(anyGSharp.transpose('P8')) G# - Sometimes we need an octave for a `Pitch` even if it's not - specified. For instance, we can't play an octave-less `Pitch` - in MIDI or display it on a staff. So there is an `.implicitOctave` - tag to deal with these situations; by default it's always 4 (unless - defaults.pitchOctave is changed) + Yet an octave is often needed anyhow, to play the `Pitch` in MIDI or + put it on a staff, so `.octave` is always an integer: the default octave, + 4 (`defaults.pitchOctave`), when none was given. `.octaveIsImplicit` + tells the two cases apart. - >>> anyGSharp.implicitOctave + >>> anyGSharp.octave 4 - - If a `Pitch` has its `.octave` explicitly set, then `.implicitOctave` - always equals `.octave`. - - >>> highEflat.implicitOctave + >>> anyGSharp.octaveIsImplicit + True + >>> highEflat.octave 6 + >>> highEflat.octaveIsImplicit + False If an integer or float >= 12 is passed to the constructor then it is used as the `.ps` attribute, which is for most common piano notes, the @@ -1724,7 +1722,7 @@ class Pitch(prebase.ProtoM21Object): >>> p2 = pitch.Pitch(3) >>> p2 - >>> p2.octave is None + >>> p2.octaveIsImplicit True Since in instantiating pitches from numbers, @@ -1846,8 +1844,8 @@ class Pitch(prebase.ProtoM21Object): and cannot be put into Streams ''' # define order for presenting names in documentation; use strings - _DOC_ORDER = ['name', 'nameWithOctave', 'step', 'pitchClass', 'octave', 'midi', 'german', - 'french', 'spanish', 'italian', 'dutch'] + _DOC_ORDER = ['name', 'nameWithOctave', 'step', 'pitchClass', 'octave', 'octaveIsImplicit', + 'midi', 'german', 'french', 'spanish', 'italian', 'dutch'] # documentation for all attributes (not properties or methods) # _DOC_ATTR: dict[str, str] = { # } @@ -1934,12 +1932,7 @@ def __init__(self, # 5% of pitch creation time; it'll be created in a sec anyhow self._microtone: Microtone|None = None - # # CA, Q: should this remain an attribute or only refer to value in defaults? - # # MSC A: no, it's a useful attribute for cases such as scales where if there are - # # no octaves we give a defaultOctave higher than the previous - # # MSC 12 years later: maybe Chris was right! - # self.defaultOctave: int = defaults.pitchOctave - # # MSC: even later: Chris Ariza was right + # None means implicit: .octave then reports defaults.pitchOctave self._octave: int|None = None # if True, accidental is not known; is determined algorithmically @@ -2046,7 +2039,7 @@ def __eq__(self, other: object) -> bool: ''' if not isinstance(other, Pitch): return NotImplemented - if (self.octave == other.octave + if (self._octave == other._octave and self.step == other.step and self.accidental == other.accidental and self.microtone == other.microtone): @@ -2085,7 +2078,7 @@ def __hash__(self) -> int: self.fundamental, self.spellingIsInferred, self.microtone, - self.octave, + self._octave, self.step, type(self), ) @@ -2592,15 +2585,15 @@ def ps(self) -> float: >>> print(f'{p.ps:.1f}') 60.2 - Octaveless pitches use their .implicitOctave attributes: + A pitch without an octave of its own uses the default octave, 4: >>> d = pitch.Pitch('D#') - >>> d.octave is None + >>> d.octaveIsImplicit True - >>> d.implicitOctave - 4 >>> d.ps 63.0 + >>> d.ps == pitch.Pitch('D#4').ps + True >>> d.octave = 5 >>> d.ps @@ -2628,7 +2621,7 @@ def ps(self) -> float: or self.accidental are changed. ''' step = self._step - ps = float(((self.implicitOctave + 1) * 12) + STEPREF[step]) + ps = float(((self.octave + 1) * 12) + STEPREF[step]) if self.accidental is not None: ps = ps + self.accidental.alter if self._microtone is not None: @@ -2892,9 +2885,9 @@ def nameWithOctave(self) -> str: Traceback (most recent call last): music21.pitch.PitchException: Cannot set a nameWithOctave with 'C#' - Set octave to None explicitly instead. + Set `.octaveIsImplicit = True` instead. ''' - if self.octave is None: + if self.octaveIsImplicit: return self.name else: return self.name + str(self.octave) @@ -2922,7 +2915,7 @@ def unicodeNameWithOctave(self) -> str: >>> p.unicodeNameWithOctave 'C♯4' ''' - if self.octave is None: + if self.octaveIsImplicit: return self.unicodeName else: return self.unicodeName + str(self.octave) @@ -2948,7 +2941,7 @@ def fullName(self) -> str: if self.accidental is not None: name += f'-{self.accidental.fullName}' - if self.octave is not None: + if not self.octaveIsImplicit: name += f' in octave {self.octave}' if self._microtone is not None and self.microtone.cents != 0: @@ -3166,31 +3159,42 @@ def pitchClassString(self, v: int|PitchClassString) -> None: @property - def octave(self) -> int|None: + def octave(self) -> int: ''' - Returns or sets the octave of the note. - Setting the octave updates the pitchSpace attribute. + Returns or sets the octave of the Pitch. - >>> a = pitch.Pitch('g') - >>> a.octave is None - True - >>> a.implicitOctave + >>> b = pitch.Pitch('B5') + >>> b.octave + 5 + + Always an int: a Pitch created without an octave reports the default + octave, 4 (`defaults.pitchOctave`), and has `.octaveIsImplicit` True. + + >>> g = pitch.Pitch('g') + >>> g.octave 4 - >>> a.ps ## will use implicitOctave + >>> g.octaveIsImplicit + True + >>> g.ps 67.0 - >>> a.name - 'G' - >>> a.octave = 14 - >>> a.octave - 14 - >>> a.implicitOctave + Setting the octave updates `.ps` and makes the octave explicit: + + >>> g.octave = 14 + >>> g.octave 14 - >>> a.name - 'G' - >>> a.ps + >>> g.octaveIsImplicit + False + >>> g.ps 187.0 + + To make the pitch octaveless, set `.octaveIsImplicit = True`. Setting + `.octave = None` is deprecated and will be removed in v13. + + * Changed in v11: always an int; `.octaveIsImplicit` says whether it was given. ''' + if self._octave is None: + return defaults.pitchOctave return self._octave @octave.setter @@ -3198,28 +3202,64 @@ def octave(self, value: int|float|None) -> None: if value is not None: self._octave = int(value) else: + # None is deprecated, removed in v13. Add real deprecation message here in v12 self._octave = None self.informClient() + @property + def octaveIsImplicit(self) -> bool: + ''' + True if this Pitch was never given an octave, so it stands for its + pitch class in any octave: it prints without an octave number, and + `.octave` reports the default, 4. + + >>> anyFSharp = pitch.Pitch('F#') + >>> anyFSharp.octaveIsImplicit + True + >>> anyFSharp + + >>> anyFSharp.octave + 4 + + Setting `.octave` makes the octave explicit: + + >>> anyFSharp.octave = 5 + >>> anyFSharp.octaveIsImplicit + False + >>> anyFSharp + + + Set it back to True to make the pitch octaveless again: + + >>> anyFSharp.octaveIsImplicit = True + >>> anyFSharp + + + * New in v11. + ''' + return self._octave is None + + @octaveIsImplicit.setter + def octaveIsImplicit(self, value: bool) -> None: + if bool(value) == self.octaveIsImplicit: + return + self._octave = None if value else defaults.pitchOctave + self.informClient() + @property def implicitOctave(self) -> int: ''' - Returns the octave of the Pitch, or defaultOctave if - octave was never set. To set an octave, use .octave. - Default octave is usually 4. + Synonym for `.octave`. >>> p = pitch.Pitch('C#') - >>> p.octave is None - True >>> p.implicitOctave 4 - Cannot be set. Instead, just change the `.octave` of the pitch + * Deprecated in v11: use `.octave`, which is now always an int. + A warning arrives in v12 and the property goes away later. ''' - if self.octave is None: - return defaults.pitchOctave - else: - return self.octave + # Add real deprecation message here in v12 + return self.octave # noinspection SpellCheckingInspection,GrazieInspection @property @@ -4006,7 +4046,7 @@ def isEnharmonic(self, other: Pitch) -> bool: >>> pD4.isEnharmonic(pEbb4) and pD4.step == pEbb4.step False ''' - if other.octave is None or self.octave is None: + if other.octaveIsImplicit or self.octaveIsImplicit: return (other.ps - self.ps) % 12 == 0 else: # if pitch spaces are equal, these are enharmonics @@ -4040,19 +4080,19 @@ def _getEnharmonicHelper(self, if intervalString not in self._transpositionIntervals: self._transpositionIntervals[intervalString] = interval.Interval(intervalString) intervalObj = self._transpositionIntervals[intervalString] - octaveStored = self.octave # may be None + octaveWasImplicit = self.octaveIsImplicit p = intervalObj.transposePitch(self, maxAccidental=None) if not inPlace: - if octaveStored is None: - p.octave = None + if octaveWasImplicit: + p.octaveIsImplicit = True return p else: self.step = p.step self.accidental = p.accidental if p.microtone is not None: self.microtone = p.microtone - if octaveStored is None: - self.octave = None + if octaveWasImplicit: + self.octaveIsImplicit = True else: self.octave = p.octave return None @@ -4231,10 +4271,10 @@ def simplifyEnharmonic( else: # by resetting the pitch space value, we will get a simpler # enharmonic spelling - saveOctave = self.octave + octaveWasImplicit = self.octaveIsImplicit returnObj.ps = self.ps - if saveOctave is None: - returnObj.octave = None + if octaveWasImplicit: + returnObj.octaveIsImplicit = True if mostCommon: if returnObj.name == 'D#': @@ -4466,7 +4506,7 @@ def diatonicNoteNum(self) -> int: >>> b.diatonicNoteNum 0 - An `implicitOctave` of 4 is used if octave is not set: + The default octave, 4, is used as a basis if the pitch had no octave: >>> c = pitch.Pitch('C') >>> c.diatonicNoteNum @@ -4496,7 +4536,7 @@ def diatonicNoteNum(self) -> int: >>> lowLowLowD.diatonicNoteNum -19 ''' - return STEP_TO_DNN_OFFSET[self.step] + 1 + (7 * self.implicitOctave) + return STEP_TO_DNN_OFFSET[self.step] + 1 + (7 * self.octave) @diatonicNoteNum.setter def diatonicNoteNum(self, newNum: int) -> None: @@ -4641,7 +4681,7 @@ def transpose( # pitch attributes # NOTE: in some cases this may not return exactly the proper config self.name = p.name - if self.octave is not None: + if not self.octaveIsImplicit: self.octave = p.octave # manually copy accidental object self.accidental = p.accidental @@ -4725,14 +4765,13 @@ def transposeBelowTarget( * Changed in v3: default for inPlace=False. ''' - if self.octave is None: + if self.octaveIsImplicit: raise PitchException('Cannot call transposeBelowTarget with an octaveless Pitch.') if inPlace: src = self else: src = copy.deepcopy(self) - assert src.octave is not None while True: # ref 20, min 10, lower ref. @@ -4810,14 +4849,13 @@ def transposeAboveTarget(self, * Changed in v3: default for inPlace=False. ''' - if self.octave is None: + if self.octaveIsImplicit: raise PitchException('Cannot call transposeAboveTarget with an octaveless Pitch.') if inPlace: src = self else: src = copy.deepcopy(self) - assert src.octave is not None # case where self is below target while True: diff --git a/music21/roman.py b/music21/roman.py index 52d5df146e..d9de8dc1f4 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -21,7 +21,6 @@ from music21 import chord from music21 import common -from music21 import defaults from music21 import environment from music21 import exceptions21 from music21.figuredBass import notation as fbNotation @@ -3416,12 +3415,9 @@ def _updatePitches(self) -> None: thisScaleDegree, direction=scale.Direction.ASCENDING)) pitchName = self.figuresNotationObj.modifiers[i].modifyPitchName(newPitch.name) newNewPitch = pitch.Pitch(pitchName) - if newPitch.octave is not None: - newNewPitch.octave = newPitch.octave - else: - newNewPitch.octave = defaults.pitchOctave + newNewPitch.octave = newPitch.octave if newNewPitch.ps < lastPitch.ps: - newNewPitch.octave += 1 # type: ignore + newNewPitch.octave += 1 pitches.append(newNewPitch) lastPitch = newNewPitch @@ -3480,13 +3476,13 @@ def _updatePitches(self) -> None: addedPitch.accidental = pitch.Accidental(alteration) while addedPitch.ps < bassPitch.ps: - addedPitch.octave = addedPitch.implicitOctave + 1 + addedPitch.octave += 1 if (addedPitch.ps == bassPitch.ps and addedPitch.diatonicNoteNum < bassPitch.diatonicNoteNum): # RN('IV[add#7]', 'C') would otherwise result # in E#4 as bass, not E#5 as highest note. - addedPitch.octave = addedPitch.implicitOctave + 1 + addedPitch.octave += 1 if addedPitch not in self.pitches: self.add(addedPitch) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index 20ead29ffa..b48563c639 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -98,7 +98,6 @@ # ------------------------- from music21 import base from music21 import common -from music21 import defaults from music21 import environment from music21 import exceptions21 from music21 import note @@ -250,8 +249,7 @@ def extractPitchList( seen.add(hashValue) post.append(p) for p in post: - if p.octave is None: - p.octave = defaults.pitchOctave + p.octaveIsImplicit = False return post @@ -388,8 +386,7 @@ def buildNetworkFromPitches( self.octaveDuplicating = False else: p = copy.deepcopy(pitchListProcessed[0]) - if p.octave is None: - p.octave = p.implicitOctave + p.octaveIsImplicit = False if pitchListProcessed[-1] > pitchListProcessed[0]: # ascending while p.ps < pitchListProcessed[-1].ps: p.octave += 1 @@ -426,10 +423,10 @@ def fixDefaultOctaveForPitchList(pitchList: list[pitch.Pitch]) -> list[pitch.Pit >>> pitchListStrs = 'a b c d e f g a'.split() >>> pitchList = [pitch.Pitch(p) for p in pitchListStrs] - Here's the problem, between `pitchList[1]` and `pitchList[2]` the `.implicitOctave` - stays the same, so the `.ps` drops: + Here's the problem, between `pitchList[1]` and `pitchList[2]` the `.octave` + stays the same (since both pitches have .octaveIsImplicit == True), so the `.ps` drops: - >>> (pitchList[1].implicitOctave, pitchList[2].implicitOctave) + >>> (pitchList[1].octave, pitchList[2].octave) (4, 4) >>> (pitchList[1].ps, pitchList[2].ps) (71.0, 60.0) @@ -438,7 +435,7 @@ def fixDefaultOctaveForPitchList(pitchList: list[pitch.Pitch]) -> list[pitch.Pit one has a .ps above the previous: >>> pl2 = scale.AbstractScale.fixDefaultOctaveForPitchList(pitchList) - >>> (pl2[1].implicitOctave, pl2[2].implicitOctave, pl2[3].implicitOctave) + >>> (pl2[1].octave, pl2[2].octave, pl2[3].octave) (4, 5, 5) >>> (pl2[1].ps, pl2[2].ps) (71.0, 72.0) @@ -452,9 +449,9 @@ def fixDefaultOctaveForPitchList(pitchList: list[pitch.Pitch]) -> list[pitch.Pit ''' # fix defaultOctave for pitchList lastPs: float = 0 - lastOctave = pitchList[0].implicitOctave + lastOctave = pitchList[0].octave for p in pitchList: - if p.octave is None: + if p.octaveIsImplicit: if lastPs > p.ps: p.octave = lastOctave while lastPs > p.ps: @@ -462,7 +459,7 @@ def fixDefaultOctaveForPitchList(pitchList: list[pitch.Pitch]) -> list[pitch.Pit p.octave = lastOctave lastPs = p.ps - lastOctave = p.implicitOctave + lastOctave = p.octave return pitchList diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 384b181a55..4b7e5eaecc 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -1370,13 +1370,14 @@ def nextPitch( if alteredDegrees and degree in alteredDegrees: alterSemitones = alteredDegrees[degree]['interval'].semitones alterSemitonesInt = t.cast('int', alterSemitones) - if ((usedNeighbor and getNeighbor == Direction.DESCENDING) - or (not usedNeighbor and direction == Direction.ASCENDING)): - while p.octave is not None and p.transpose(alterSemitonesInt) > pitchOriginObj: - p.octave -= 1 - else: - while p.octave is not None and p.transpose(alterSemitonesInt) < pitchOriginObj: - p.octave += 1 + if not p.octaveIsImplicit: + if ((usedNeighbor and getNeighbor == Direction.DESCENDING) + or (not usedNeighbor and direction == Direction.ASCENDING)): + while p.transpose(alterSemitonesInt) > pitchOriginObj: + p.octave -= 1 + else: + while p.transpose(alterSemitonesInt) < pitchOriginObj: + p.octave += 1 # pitchObj = p n = self.nodes[foundNodeId] @@ -1487,8 +1488,7 @@ def realizeAscending( nodeObj = t.cast(list[Node], self.nodeNameToNodes(nodeId))[0] # must set an octave for pitch reference, even if not given - if pitchReference.octave is None: - pitchReference.octave = pitchReference.implicitOctave + pitchReference.octaveIsImplicit = False if isinstance(minPitch, str): minPitch = pitch.Pitch(minPitch) @@ -1675,8 +1675,7 @@ def realizeDescending( pitchRef = copy.deepcopy(pitchReference) # must set an octave for pitch reference, even if not given - if pitchRef.octave is None: - pitchRef.octave = 4 + pitchRef.octaveIsImplicit = False # get first node if no node is provided if isinstance(nodeId, Node): @@ -1861,8 +1860,7 @@ def realize(self, pitchRef = copy.deepcopy(pitchReference) # must set an octave for pitch reference, even if not given - if pitchRef.octave is None: - pitchRef.octave = pitchRef.implicitOctave + pitchRef.octaveIsImplicit = False minPitchObj: pitch.Pitch|None if isinstance(minPitch, str): @@ -2383,9 +2381,8 @@ def getRelativeNodeId( else: pitchTargetObj = pitchTarget - saveOctave = pitchTargetObj.octave - if saveOctave is None: - pitchTargetObj.octave = pitchTargetObj.implicitOctave + octaveWasImplicit = pitchTargetObj.octaveIsImplicit + pitchTargetObj.octaveIsImplicit = False # try an octave spread first # if a scale degree is larger than an octave this will fail @@ -2413,8 +2410,8 @@ def getRelativeNodeId( if realizedNode not in post: # may be more than one match post.append(realizedNode) - if saveOctave is None: - pitchTargetObj.octave = None + if octaveWasImplicit: + pitchTargetObj.octaveIsImplicit = True if not post: return None @@ -2459,10 +2456,9 @@ def getNeighborNodeIds( else: pitchTargetObj = pitchTarget - savedOctave = pitchTargetObj.octave - if savedOctave is None: - # don't alter permanently, in case a Pitch object was passed in. - pitchTargetObj.octave = pitchTargetObj.implicitOctave + # don't alter permanently, in case a Pitch object was passed in. + octaveWasImplicit = pitchTargetObj.octaveIsImplicit + pitchTargetObj.octaveIsImplicit = False # try an octave spread first # if a scale degree is larger than an octave this will fail minPitch = pitchTargetObj.transpose(-12, inPlace=False) @@ -2484,8 +2480,8 @@ def getNeighborNodeIds( return lowNeighbor, highNeighbor lowNeighbor = realizedNode - if savedOctave is None: - pitchTargetObj.octave = savedOctave + if octaveWasImplicit: + pitchTargetObj.octaveIsImplicit = True return None def getRelativeNodeDegree( diff --git a/music21/serial.py b/music21/serial.py index b98c6f7da4..c081368943 100644 --- a/music21/serial.py +++ b/music21/serial.py @@ -304,7 +304,7 @@ def __init__(self, row=None, **keywords): else: n = pc - n.pitch.octave = None + n.pitch.octaveIsImplicit = True self.append(n) def _reprInternal(self): @@ -399,7 +399,7 @@ def makeTwelveToneRow(self): n = note.Note() n.duration.quarterLength = 0.0 n.pitch.pitchClass = thisPc - n.pitch.octave = None + n.pitch.octaveIsImplicit = True a.append(n) return a @@ -700,7 +700,7 @@ def matrix(self): n = note.Note() n.duration.quarterLength = 0.0 n.pitch.pitchClass = p - n.pitch.octave = None + n.pitch.octaveIsImplicit = True rowObject.append(n) matrixObj.insert(0, rowObject) @@ -1286,7 +1286,7 @@ def pcToToneRow(pcSet): for thisPc in pcSet: n = note.Note() n.pitch.pitchClass = thisPc - n.pitch.octave = None + n.pitch.octaveIsImplicit = True a.append(n) return a diff --git a/music21/stream/tests.py b/music21/stream/tests.py index 6e49f36179..ef92fb2ff3 100644 --- a/music21/stream/tests.py +++ b/music21/stream/tests.py @@ -2909,9 +2909,9 @@ def testMakeAccidentalsOnChord(self): # Repeat the test without octaves and reset state low, high = augmented_octave.pitches - low.octave = None + low.octaveIsImplicit = True low.accidental = None - high.octave = None + high.octaveIsImplicit = True high.accidental.displayStatus = None s2.makeAccidentals(inPlace=True) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index fbd4379bb8..3716d8e5b5 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -5,7 +5,7 @@ # Authors: Michael Scott Asato Cuthbert # Christopher Ariza # -# Copyright: Copyright © 2008-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2008-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -16,6 +16,7 @@ from music21 import common from music21 import converter from music21 import corpus +from music21 import defaults from music21 import key from music21 import note from music21 import pitch @@ -41,6 +42,79 @@ def testOctave(self): b = Pitch('B#3') self.assertEqual(b.octave, 3) + def testOctaveIsImplicit(self): + anyFSharp = Pitch('F#') + self.assertTrue(anyFSharp.octaveIsImplicit) + self.assertEqual(anyFSharp.octave, 4) + self.assertEqual(anyFSharp.implicitOctave, 4) + self.assertEqual(anyFSharp.nameWithOctave, 'F#') + self.assertEqual(anyFSharp.ps, 66.0) + + anyFSharp.octave = 5 + self.assertFalse(anyFSharp.octaveIsImplicit) + self.assertEqual(anyFSharp.octave, 5) + self.assertEqual(anyFSharp.implicitOctave, 5) + self.assertEqual(anyFSharp.nameWithOctave, 'F#5') + + anyFSharp.octaveIsImplicit = True + self.assertEqual(anyFSharp.octave, 4) + self.assertEqual(anyFSharp.nameWithOctave, 'F#') + self.assertEqual(anyFSharp.ps, 66.0) + + # False gives the default octave explicitly + anyFSharp.octaveIsImplicit = False + self.assertEqual(anyFSharp.nameWithOctave, 'F#4') + + # octave = None still forgets the octave + anyFSharp.octave = None + self.assertTrue(anyFSharp.octaveIsImplicit) + self.assertEqual(anyFSharp.octave, 4) + + # Notes proxy the int + self.assertEqual(note.Note('B-').octave, 4) + self.assertEqual(note.Note('B-3').octave, 3) + + # the default is read live from defaults.pitchOctave + savedDefaultOctave = defaults.pitchOctave + try: + defaults.pitchOctave = 3 + self.assertEqual(Pitch('C').octave, 3) + self.assertEqual(Pitch('C').ps, 48.0) + self.assertEqual(Pitch('C5').octave, 5) + finally: + defaults.pitchOctave = savedDefaultOctave + + # creation paths + self.assertTrue(Pitch().octaveIsImplicit) + self.assertTrue(Pitch(3).octaveIsImplicit) # pitch class + self.assertFalse(Pitch(65).octaveIsImplicit) # midi + self.assertFalse(Pitch('C4').octaveIsImplicit) + self.assertFalse(Pitch('C', octave=4).octaveIsImplicit) + self.assertTrue(Pitch(step='D', accidental='#').octaveIsImplicit) + self.assertTrue(note.Note('B-').pitch.octaveIsImplicit) + self.assertFalse(note.Note().pitch.octaveIsImplicit) + + # implicitness survives copying and transposition + anyD = Pitch('D') + self.assertTrue(copy.deepcopy(anyD).octaveIsImplicit) + self.assertTrue(anyD.transpose('M2').octaveIsImplicit) + self.assertTrue(anyD.transpose(3).octaveIsImplicit) + self.assertTrue(anyD.getEnharmonic().octaveIsImplicit) + self.assertFalse(Pitch('D4').transpose('M2').octaveIsImplicit) + + # an implicit octave is not the same as an explicit default octave + self.assertNotEqual(Pitch('C'), Pitch('C4')) + self.assertEqual(Pitch('C'), Pitch('C')) + self.assertNotEqual(hash(Pitch('C')), hash(Pitch('C4'))) + + # the setter informs a Note client only when something changed + n = note.Note('C4') + n._cache['junk'] = 1 + n.pitch.octaveIsImplicit = False + self.assertEqual(n._cache, {'junk': 1}) + n.pitch.octaveIsImplicit = True + self.assertEqual(n._cache, {}) + def testNameSetting(self): with self.assertRaisesRegex(ValueError, r"Cannot have octave given before pitch name in '8D-4'\."): From 92ff6a42ddedb0151f9ea73a980f16b8ecfc8119 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Sep 2026 16:46:18 +0000 Subject: [PATCH 098/312] build(deps): bump mistune from 3.3.0 to 3.3.3 Bumps [mistune](https://github.com/lepture/mistune) from 3.3.0 to 3.3.3. - [Release notes](https://github.com/lepture/mistune/releases) - [Changelog](https://github.com/lepture/mistune/blob/main/docs/changes.rst) - [Commits](https://github.com/lepture/mistune/compare/v3.3.0...v3.3.3) --- updated-dependencies: - dependency-name: mistune dependency-version: 3.3.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 4b8e2e6472..dcb01b2b6f 100644 --- a/uv.lock +++ b/uv.lock @@ -1763,11 +1763,11 @@ wheels = [ [[package]] name = "mistune" -version = "3.3.0" +version = "3.3.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/9c/1939635275ec7258e2b43b00dafabc36d89ad11aa7838d375dc1b0e561cb/mistune-3.3.0.tar.gz", hash = "sha256:3074ec4c61b384abe725128e4dcbb483f5a09cc4632012505cdee655d3a113b9", size = 110936, upload-time = "2026-06-21T13:11:39.458Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/a5/2dab368d6950e6808904dec98f54c7e726ee7be4a0c6afe00e6e011bd52d/mistune-3.3.3.tar.gz", hash = "sha256:c4c6c0c840b8637a2e9b8b6d607eb7c8f00888bf14c754409bcd339e848c2477", size = 115363, upload-time = "2026-07-09T06:18:05.268Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/76/b90f9d48d43fbd80a79a20d3eab2e5109859c7a56dc663b23187385898f3/mistune-3.3.0-py3-none-any.whl", hash = "sha256:a758e578acda49d8195f9a860b132dae2cf7bf409381393b1c4e6e489a65397b", size = 61250, upload-time = "2026-06-21T13:11:37.938Z" }, + { url = "https://files.pythonhosted.org/packages/89/70/b1e4737b84163db5bb1dfde6f216dbfbf32783330a9989c965e121172830/mistune-3.3.3-py3-none-any.whl", hash = "sha256:99de1585e42dcbd826faa9e11a202727a5e202e4e4722a4c69ac1ff615793dd7", size = 63569, upload-time = "2026-07-09T06:18:03.839Z" }, ] [[package]] From 928df1dbb8ed38792a61633b46b98b4e43d3f90f Mon Sep 17 00:00:00 2001 From: hill Date: Mon, 7 Sep 2026 11:52:56 +0200 Subject: [PATCH 099/312] Ignore trailing text after a Scala pitch value ScalaData.parse skips a line only when it starts with '!', so a degree line carrying a comment after the value is handed to ScalaPitch, whose _setSrc keeps every digit, '.' and '/' from anywhere in the string. The comment's digits are spliced onto the end of the number rather than dropped, so `10/9 ! A\ ; 8 |mi|la|re|` is read as '10/98' -- -3951.34 cents where 10/9 is 182.40, and no exception is raised. The Scala format ignores any characters following the pitch value, which is why the bundled files write comments there. Take the first whitespace-delimited token on the pitch-count line and on each degree line. 23 of the 3932 files in scale/scala/scl/ were misread. 21 raised ValueError, including every file whose comment contributes a second '/'; 2 more parsed without error and gave wrong cents, newton_15_out_of_53 and sparschuh-jsbloops440. Trailing text without a '!' took the same path: rvf1, rvf2 and rvf3 write a degree as `454.75 (427)`. All of them read correctly now, and no other file's parsed values change. Two tests. testCommentAfterAPitchValue covers a '!' comment, bare trailing text, and the two bundled files that used to be read silently wrong, pinning their cents. testEveryBundledScalaFileParses reads the whole archive and asserts every file declares a pitch count, yields that many values, and gives a finite cents for each -- so a file added or edited upstream that this parser cannot read fails there rather than wherever some scale is later asked for. It takes about 0.8s. The description line is not checked, since the format allows it to be empty. --- music21/scale/scala/__init__.py | 57 +++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/music21/scale/scala/__init__.py b/music21/scale/scala/__init__.py index f161224540..a937287f21 100644 --- a/music21/scale/scala/__init__.py +++ b/music21/scale/scala/__init__.py @@ -288,10 +288,12 @@ def parse(self) -> None: self.description = line elif count == 2: if line != '': - self.pitchCount = int(line) + # only the first token is data; the format ignores any + # trailing characters, so a comment can follow the value + self.pitchCount = int(line.split()[0]) else: # remaining counts are pitches if line != '': - sp = ScalaPitch(line) + sp = ScalaPitch(line.split()[0]) sp.parse() self.pitchValues.append(sp) @@ -744,6 +746,57 @@ def testScalaFileA(self) -> None: '', '']) + def testCommentAfterAPitchValue(self) -> None: + # noinspection SpellCheckingInspection + msg = r'''! commented.scl +! +A scale whose values carry trailing comments + 3 +! + 10/9 ! A\ ; 8 |mi|la|re| + 454.75 (427) + 2/1 ! octave +''' + ss = ScalaData(msg) + ss.parse() + self.assertEqual(ss.pitchCount, 3) + self.assertEqual(len(ss.pitchValues), 3) + self.assertEqual([f'{x.cents:.5f}' for x in ss.pitchValues], + ['182.40371', '454.75000', '1200.00000']) + + # two bundled files the old parser read without raising, and got + # wrong: the comment's digits were appended to the value + newton = parse('newton_15_out_of_53') + assert newton is not None + self.assertEqual(newton.pitchValues[0].src, '10/9') + self.assertEqual(f'{newton.pitchValues[0].cents:.5f}', '182.40371') + + loops = parse('sparschuh-jsbloops440') + assert loops is not None + self.assertEqual(loops.pitchValues[8].src, '1760/1055') + self.assertEqual(f'{loops.pitchValues[8].cents:.5f}', '885.99892') + + def testEveryBundledScalaFileParses(self) -> None: + # a guard on the archive itself: a file added or edited upstream that + # this parser cannot read should fail here rather than at the point + # some scale is asked for. The description line is not checked, as the + # format allows it to be empty. + paths = getPaths() + self.assertGreater(len(paths), 3000) + for fp in paths: + sf = ScalaFile() + sf.open(fp) + try: + ss = sf.read() + finally: + sf.close() + self.assertIsNotNone(ss.pitchCount, fp) + self.assertEqual(len(ss.pitchValues), ss.pitchCount, fp) + for sp in ss.pitchValues: + self.assertIsNotNone(sp.cents, f'{fp}: {sp.src!r}') + self.assertTrue(math.isfinite(t.cast(float, sp.cents)), + f'{fp}: {sp.src!r}') + # ------------------------------------------------------------------------------ # define presented order in documentation From c3045287d901768fd1418c78559bf84c0dddb105 Mon Sep 17 00:00:00 2001 From: hill Date: Tue, 8 Sep 2026 02:24:39 +0200 Subject: [PATCH 100/312] Tell a negative octave from a flat in removeRedundantPitches `Pitch.nameWithOctave` spells both B-flat in octave 1 and B-natural in octave -1 as 'B-1', because the '-' is the flat sign and the minus sign both. `removeRedundantPitches` compared pitches by that string, so the second read as a repeat of the first and was thrown away. `_removePitchByRedundantAttribute` now knows that 'nameWithOctave' is the ambiguous one and compares by name and octave in its place. Its signature is unchanged and nothing new is exposed: the pitch-class and pitch-name reductions, and both call sites, are exactly as they were. The octave is compared together with `octaveIsImplicit`, so a pitch given no octave stays distinct from one placed in the default octave, which is what `nameWithOctave` ('C' against 'C4') already did. The docstring documented the old behaviour as a known bug ("doesn't seem a bug worth squashing at this moment"); it now documents what the method does, and test_chord carries a regression test. --- music21/chord/__init__.py | 59 ++++++++++++++++++++------------------ music21/test/test_chord.py | 28 ++++++++++++++++++ 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index a0d6789f68..1539deb35a 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -964,17 +964,26 @@ def _removePitchByRedundantAttribute( ''' Common method for stripping pitches based on redundancy of one pitch attribute. The `attribute` is provided by a string. + + `'nameWithOctave'` is a special case: the string it gives spells + B-flat in octave 1 and B-natural in octave -1 alike, so two pitches + are compared by their name and octave instead of by it. ''' if not inPlace: # make a copy returnObj = copy.deepcopy(self) else: returnObj = self - uniquePitches = [] + uniquePitches: list[t.Any] = [] deleteComponents = [] for comp in returnObj._notes: - if getattr(comp.pitch, attribute) not in uniquePitches: - uniquePitches.append(getattr(comp.pitch, attribute)) + if attribute == 'nameWithOctave': + value = (comp.pitch.name, comp.pitch.octave, comp.pitch.octaveIsImplicit) + else: + value = getattr(comp.pitch, attribute) + + if value not in uniquePitches: + uniquePitches.append(value) else: deleteComponents.append(comp) @@ -3615,41 +3624,35 @@ def removeRedundantPitches(self, *, inPlace: bool = False) -> t.Self|list[pitch. >>> c2c - It is a known bug that because pitch.nameWithOctave gives - the same value for B-flat in octave 1 as B-natural in octave - negative 1, negative octaves can screw up this method. - With all the things left to do for music21, it doesn't seem - a bug worth squashing at this moment, but FYI: + Two pitches count as the same when they have the same name in the same + octave. Note that this is a finer distinction than + :attr:`~music21.pitch.Pitch.nameWithOctave`, whose '-' is the flat sign + and the minus sign both: B-flat in octave 1 and B-natural in octave -1 + are two different pitches that spell alike, and both are kept. >>> p1 = pitch.Pitch('B-') >>> p1.octave = 1 >>> p2 = pitch.Pitch('B') >>> p2.octave = -1 - >>> c3 = chord.Chord([p1, p2]) - >>> removedPitches = c3.removeRedundantPitches(inPlace=True) - >>> c3.pitches - (,) - - >>> c3.pitches[0].name - 'B-' - >>> c3.pitches[0].octave - 1 - >>> removedPitches - [] - >>> removedPitches[0].name - 'B' - >>> removedPitches[0].octave - -1 + >>> p1.nameWithOctave == p2.nameWithOctave + True - The first pitch survives: + >>> c3 = chord.Chord([p1, p2]) + >>> c3.removeRedundantPitches(inPlace=True) + [] + >>> [(p.name, p.octave) for p in c3.pitches] + [('B-', 1), ('B', -1)] - >>> c3.pitches[0] is p1 - True + A pitch with no octave of its own is likewise not the same pitch as + one placed in the default octave: - >>> c3.pitches[0] is p2 - False + >>> c4 = chord.Chord([pitch.Pitch('C'), pitch.Pitch('C4')]) + >>> c4.removeRedundantPitches() + * Changed in v6: inPlace defaults to False. + * Changed in v11: a negative octave is no longer read as a flat, so + B-natural in octave -1 survives beside B-flat in octave 1. ''' return self._removePitchByRedundantAttribute('nameWithOctave', inPlace=inPlace) diff --git a/music21/test/test_chord.py b/music21/test/test_chord.py index caa50790e5..25f0e94d0b 100644 --- a/music21/test/test_chord.py +++ b/music21/test/test_chord.py @@ -739,6 +739,34 @@ def testChordCannotContainUnpitched(self): # noinspection PyTypeChecker Chord([note.Unpitched()]) # type: ignore + def testRemoveRedundantPitchesTellsNegativeOctavesFromFlats(self): + ''' + nameWithOctave spells both B-flat in octave 1 and B-natural in + octave -1 as 'B-1', so comparing pitches by that string made the + second look like a repeat of the first. + ''' + flat = pitch.Pitch('B-') + flat.octave = 1 + natural = pitch.Pitch('B') + natural.octave = -1 + self.assertEqual(flat.nameWithOctave, natural.nameWithOctave) + + ch = Chord([flat, natural]) + self.assertEqual(ch.removeRedundantPitches(inPlace=True), []) + self.assertEqual([(p.name, p.octave) for p in ch.pitches], + [('B-', 1), ('B', -1)]) + + # a real repeat still goes, whatever octave it is in + repeated = Chord([flat, natural, pitch.Pitch('B-1')]) + removed = repeated.removeRedundantPitches(inPlace=True) + self.assertEqual([p.nameWithOctave for p in removed], ['B-1']) + self.assertEqual(len(repeated.pitches), 2) + + # and a pitch with no octave is not the one in the default octave + octaveless = Chord([pitch.Pitch('C'), pitch.Pitch('C4')]) + self.assertEqual(octaveless.removeRedundantPitches(inPlace=True), []) + self.assertEqual(len(octaveless.pitches), 2) + def testCacheClearedOnAdd(self): ch = chord.Chord('C4 E4 G4') self.assertTrue(ch.isConsonant()) From a82bde2f57e19b4bfa04eda9a64a27aa2171e580 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 7 Sep 2026 15:43:42 -1000 Subject: [PATCH 101/312] Clearer Docs Remove docs that overemphasize this bug fix compared to the routine's work as a whole. --- music21/chord/__init__.py | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index 1539deb35a..a5de5b8016 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -965,25 +965,25 @@ def _removePitchByRedundantAttribute( Common method for stripping pitches based on redundancy of one pitch attribute. The `attribute` is provided by a string. - `'nameWithOctave'` is a special case: the string it gives spells - B-flat in octave 1 and B-natural in octave -1 alike, so two pitches - are compared by their name and octave instead of by it. + attribute `'nameWithOctave'` uses a special case to make comparisons with + negative octaves work properly ''' if not inPlace: # make a copy returnObj = copy.deepcopy(self) else: returnObj = self - uniquePitches: list[t.Any] = [] + uniquePitches: set[t.Any] = set() deleteComponents = [] for comp in returnObj._notes: if attribute == 'nameWithOctave': + # for comparing with negative octaves etc. value = (comp.pitch.name, comp.pitch.octave, comp.pitch.octaveIsImplicit) else: value = getattr(comp.pitch, attribute) if value not in uniquePitches: - uniquePitches.append(value) + uniquePitches.add(value) else: deleteComponents.append(comp) @@ -3624,24 +3624,25 @@ def removeRedundantPitches(self, *, inPlace: bool = False) -> t.Self|list[pitch. >>> c2c - Two pitches count as the same when they have the same name in the same - octave. Note that this is a finer distinction than - :attr:`~music21.pitch.Pitch.nameWithOctave`, whose '-' is the flat sign - and the minus sign both: B-flat in octave 1 and B-natural in octave -1 - are two different pitches that spell alike, and both are kept. - - >>> p1 = pitch.Pitch('B-') - >>> p1.octave = 1 + Pitches with extreme octaves (whose `.nameWithOctave`s + look identical) are still distinguished. + + >>> p1 = pitch.Pitch('B') + >>> p1.octave = -1 >>> p2 = pitch.Pitch('B') - >>> p2.octave = -1 - >>> p1.nameWithOctave == p2.nameWithOctave - True + >>> p2.octave = 1 >>> c3 = chord.Chord([p1, p2]) - >>> c3.removeRedundantPitches(inPlace=True) + >>> c3 + + + >>> removedPitches = c3.removeRedundantPitches(inPlace=True) + >>> removedPitches [] + >>> c3 + >>> [(p.name, p.octave) for p in c3.pitches] - [('B-', 1), ('B', -1)] + [('B', -1), ('B', 1)] A pitch with no octave of its own is likewise not the same pitch as one placed in the default octave: @@ -3651,8 +3652,7 @@ def removeRedundantPitches(self, *, inPlace: bool = False) -> t.Self|list[pitch. * Changed in v6: inPlace defaults to False. - * Changed in v11: a negative octave is no longer read as a flat, so - B-natural in octave -1 survives beside B-flat in octave 1. + * Changed in v11: works properly with negative octaves ''' return self._removePitchByRedundantAttribute('nameWithOctave', inPlace=inPlace) From 5488dc2b079d2becee7e7d190f51d102e9a6ad5c Mon Sep 17 00:00:00 2001 From: hill Date: Tue, 8 Sep 2026 09:06:01 +0200 Subject: [PATCH 102/312] Make the docstring example collide again, and green The example had been changed to B-natural in octave -1 against B-natural in octave 1. Those spell 'B-1' and 'B1', which do not look alike, so the example no longer showed what it says it shows, and the two `>>> c3` lines expected `` where the chord actually reprs as ``. Back to B-flat in octave 1 against B-natural in octave -1, which is the pair that collides: both spell 'B-1', the chord really does repr as two of them, and the wording about `.nameWithOctave`s looking identical is true again. Kept the shape of the example as it was rewritten. Also drops the whitespace from two blank lines, which ruff was failing on. --- music21/chord/__init__.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index a5de5b8016..f15c07c1fd 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -3625,24 +3625,28 @@ def removeRedundantPitches(self, *, inPlace: bool = False) -> t.Self|list[pitch. Pitches with extreme octaves (whose `.nameWithOctave`s - look identical) are still distinguished. - - >>> p1 = pitch.Pitch('B') - >>> p1.octave = -1 + look identical) are still distinguished. B-flat in octave 1 and + B-natural in octave -1 both spell 'B-1', the '-' being the flat sign + in the one and the minus sign in the other: + + >>> p1 = pitch.Pitch('B-') + >>> p1.octave = 1 >>> p2 = pitch.Pitch('B') - >>> p2.octave = 1 + >>> p2.octave = -1 + >>> p1.nameWithOctave == p2.nameWithOctave + True >>> c3 = chord.Chord([p1, p2]) >>> c3 - + >>> removedPitches = c3.removeRedundantPitches(inPlace=True) >>> removedPitches [] >>> c3 >>> [(p.name, p.octave) for p in c3.pitches] - [('B', -1), ('B', 1)] + [('B-', 1), ('B', -1)] A pitch with no octave of its own is likewise not the same pitch as one placed in the default octave: From 7e51e526bc82636d72891731bc3c14e8b056a4ea Mon Sep 17 00:00:00 2001 From: hill Date: Tue, 8 Sep 2026 19:59:02 +0200 Subject: [PATCH 103/312] Hand back a new pitch from getPitchFromNodeDegree realizeAscending() and realizeDescending() return the very list they put in _ascendingCache / _descendingCache, so the pitches inside it belong to the cache. getPitchFromNodeDegree() handed one of those pitches straight back, and nextPitch() writes the origin's octave onto the pitch it gets ("transfer octave from origin to new pitch derived from node"), so it was editing the cached realization in place. The effect is that a scale answers differently depending on what has been asked of it before: >>> sc = scale.RagAsawari('c4') >>> str(sc.pitchFromDegree(1)) 'C4' >>> str(sc.nextPitch('c1', scale.Direction.ASCENDING)) 'D1' >>> str(sc.pitchFromDegree(1)) 'C1' The cached entry for ('C4', 'C4', 'C5') has its first pitch moved from C4 to C1 by the walk near C1, and every later reader of that realization sees it. realize() already guards the list against this ("Make new objects, because this value might be cached ... and mutating it would be dangerous"); this is the same hazard one level down, on the pitches. test_scale_main.testRagAsawari asserted the corrupted 'C1'. It asserts 'C4' now, which is what the same call returns earlier in that very test. Two regression tests go with it: one that the pitch handed back is the caller's own, one for the reported symptom. The whole suite is unchanged otherwise: 4,597 tests, one failure fewer and nothing newly failing. --- music21/scale/intervalNetwork.py | 8 ++++++-- music21/scale/test_intervalNetwork.py | 27 +++++++++++++++++++++++++++ music21/scale/test_scale_main.py | 5 ++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 4b7e5eaecc..2f7678dd36 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -2707,15 +2707,19 @@ def getPitchFromNodeDegree( # environLocal.printDebug(['comparing', realizedNId, # 'nodeTargetId', nodeTargetId]) + # Return a new object: the realization this pitch came out of + # may be held in _ascendingCache or _descendingCache, and a + # caller that writes to the pitch -- nextPitch() sets its + # octave -- would edit the cached scale itself. if realizedNId == nodeTargetId.id: - return realizedPitch[i] + return copy.deepcopy(realizedPitch[i]) # NOTE: this condition may be too generous, and was added to solve # a non-tracked problem. # only match this generously if we are equating termini if equateTermini: if ((realizedNId in (Terminus.HIGH, Terminus.LOW)) and (nodeTargetId.id in (Terminus.HIGH, Terminus.LOW))): - return realizedPitch[i] + return copy.deepcopy(realizedPitch[i]) # environLocal.printDebug(['getPitchFromNodeDegree() on trial', trial, ', # failed to find node', nodeTargetId]) diff --git a/music21/scale/test_intervalNetwork.py b/music21/scale/test_intervalNetwork.py index da1f3061b3..2c8ae0be5d 100644 --- a/music21/scale/test_intervalNetwork.py +++ b/music21/scale/test_intervalNetwork.py @@ -627,6 +627,33 @@ def test_realize_descending_reversed_cached(self): self.assertEqual(descending_melodic_minor_reversed[0].nameWithOctave, 'C4') # was B-4 self.assertEqual(descending_melodic_minor_reversed[-1].nameWithOctave, 'B-4') # was C4 + def test_get_pitch_from_node_degree_returns_a_new_pitch(self): + ''' + A realization may be held in _ascendingCache or _descendingCache, so + the pitch taken out of one has to be the caller's own -- otherwise + writing to it edits the cached scale. + ''' + net = IntervalNetwork() + net.fillBiDirectedEdges(['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']) + + first = net.getPitchFromNodeDegree('c4', 1, 1) + self.assertEqual(first.nameWithOctave, 'C4') + first.octave = 1 + + again = net.getPitchFromNodeDegree('c4', 1, 1) + self.assertEqual(again.nameWithOctave, 'C4') + + def test_next_pitch_does_not_move_a_cached_degree(self): + ''' + nextPitch() writes the origin's octave onto the pitch it gets back + from getPitchFromNodeDegree(), so walking the scale down near C1 used + to leave the tonic reading C1 ever after. + ''' + sc = scale.RagAsawari('c4') + self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') + self.assertEqual(str(sc.nextPitch('c1', Direction.ASCENDING)), 'D1') + self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') + # ------------------------------------------------------------------------------ if __name__ == '__main__': diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index bc3826c0fa..e57e4c2451 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -456,7 +456,10 @@ def testRagAsawari(self): getNeighbor=Direction.DESCENDING)), 'F1') - self.assertEqual(str(sc.pitchFromDegree(1)), 'C1') + # the tonic, whatever has been asked for in between: walking the + # scale near another octave used to move it, since nextPitch() was + # writing an octave onto a pitch of the cached realization. + self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') # there is no third step in ascending form self.assertEqual(str(sc.pitchFromDegree(3)), 'None') self.assertEqual(str(sc.pitchFromDegree(3, direction=Direction.DESCENDING)), From 877b65f832ee44a8fdf753276d922badb307d0be Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 8 Sep 2026 10:43:16 -1000 Subject: [PATCH 104/312] restore my phrasing Restore phrasing --- music21/chord/__init__.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index f15c07c1fd..83977eae34 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -3625,14 +3625,12 @@ def removeRedundantPitches(self, *, inPlace: bool = False) -> t.Self|list[pitch. Pitches with extreme octaves (whose `.nameWithOctave`s - look identical) are still distinguished. B-flat in octave 1 and - B-natural in octave -1 both spell 'B-1', the '-' being the flat sign - in the one and the minus sign in the other: - - >>> p1 = pitch.Pitch('B-') - >>> p1.octave = 1 - >>> p2 = pitch.Pitch('B') - >>> p2.octave = -1 + look identical) are still distinguished. + + >>> p1 = pitch.Pitch('B') + >>> p1.octave = -1 + >>> p2 = pitch.Pitch('B-') + >>> p2.octave = 1 >>> p1.nameWithOctave == p2.nameWithOctave True @@ -3646,7 +3644,7 @@ def removeRedundantPitches(self, *, inPlace: bool = False) -> t.Self|list[pitch. >>> c3 >>> [(p.name, p.octave) for p in c3.pitches] - [('B-', 1), ('B', -1)] + [('B', -1), ('B-', 1)] A pitch with no octave of its own is likewise not the same pitch as one placed in the default octave: From 811258ea96cefebbcc2d3ad97293776bb6d48a3d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 8 Sep 2026 10:56:10 -1000 Subject: [PATCH 105/312] Update README with AI declarations notice yep. AI got the commit message just fine. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8f0095ae05..6d24eaee21 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ See: https://groups.google.com/forum/#!forum/music21list [Contributing Guide](CONTRIBUTING.md) +Please note the required AI declarations there. The world is changing fast, so please review it from time to time for recent changes. + ## Community Code of Conduct ## `Music21` encourages contributions, discussions, and usage from all people interested in From ca85ceddfa5de0aaedb55ca400ceeeb1ed6745b1 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 8 Sep 2026 12:16:02 -1000 Subject: [PATCH 106/312] Profile first-time contributors' GitHub accounts on new PRs Runs gh-profiler (Eric Matthes) against the author of a PR from anyone with no merged PR here yet, and comments the concise summary only when there is a red flag or a yellow on PR/issue activity. A yellow on the profile section alone means an account with sparse bio fields, which is not worth a public comment. The full profile always goes to the run log. pull_request_target is safe here: the job never checks out or runs PR code. Adds a PR template disclosing the check to first-time contributors. --- .github/PULL_REQUEST_TEMPLATE.md | 7 ++ .github/workflows/profile_contributors.yml | 74 ++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/profile_contributors.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..eae79e4992 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ + + + + + diff --git a/.github/workflows/profile_contributors.yml b/.github/workflows/profile_contributors.yml new file mode 100644 index 0000000000..7e4c84e3d5 --- /dev/null +++ b/.github/workflows/profile_contributors.yml @@ -0,0 +1,74 @@ +# Profile the author of a PR from someone with no merged PR here yet, and comment +# only when gh-profiler raises a yellow or red flag. +# +# gh-profiler is by Eric Matthes: https://github.com/ehmatthes/gh-profiler + +name: profile_contributors + +on: + # Never checks out or runs PR code; it reads the author's login and comments. + pull_request_target: + types: [opened] + +permissions: + pull-requests: write + +jobs: + profile: + # Dependabot and friends do not need profiling. + if: github.event.pull_request.user.type != 'Bot' + runs-on: ubuntu-latest + steps: + - name: Check whether contributor has a previously merged PR + id: previous + env: + GH_TOKEN: ${{ github.token }} + AUTHOR: ${{ github.event.pull_request.user.login }} + REPO: ${{ github.repository }} + run: | + count="$(gh pr list --repo "$REPO" --author "$AUTHOR" \ + --state merged --limit 1 --json number --jq 'length')" + echo "count=$count" >> "$GITHUB_OUTPUT" + + - name: Install uv + if: steps.previous.outputs.count == '0' + uses: astral-sh/setup-uv@v7 + + - name: Profile contributor + if: steps.previous.outputs.count == '0' + id: profile + env: + GH_TOKEN: ${{ github.token }} + AUTHOR: ${{ github.event.pull_request.user.login }} + run: | + uvx gh-profiler==0.9.0 "$AUTHOR" --concise | tee profile.txt + echo '===================== Full profile =====================' + uvx gh-profiler==0.9.0 "$AUTHOR" + + # A yellow on the profile section alone means only that the account is + # newish or has empty bio fields, which is not worth a public comment. + concern=false + if grep -q '🔴' profile.txt; then + concern=true + elif grep -e 'recent PR activity' -e 'recent issue activity' profile.txt \ + | grep -q '🟡'; then + concern=true + fi + echo "concern=$concern" >> "$GITHUB_OUTPUT" + + - name: Comment on PR + if: steps.previous.outputs.count == '0' && steps.profile.outputs.concern == 'true' + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + run: | + { + echo 'Contributor profile ([gh-profiler](https://github.com/ehmatthes/gh-profiler));' + echo 'the full report is in this workflow'"'"'s log.' + echo + echo '```text' + cat profile.txt + echo '```' + } > comment.txt + + gh pr comment "$PR" --body-file comment.txt From cb7fec151de2490d2a6853f1b9a20b3b91137c14 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 8 Sep 2026 12:23:44 -1000 Subject: [PATCH 107/312] Require the issue and PR templates even when opening directly The templates only prefill in the web UI, so issues and PRs opened through the API or `gh` have been arriving without them. State in CONTRIBUTING.md and AGENTS.md that they are to be read from `.github/` and followed either way. Also documents the first-PR profile check in both. --- AGENTS.md | 5 +++++ CONTRIBUTING.md | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 2e7640dcab..e65377b41a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,6 +98,11 @@ branch such as `m21_9`. When a CI check fails but passes locally, "is my branch behind `master`?" should be one of the first things to check: fetch and merge `master`, then the newer types/code on `master` will reproduce the failure locally. +- The issue and PR templates in `.github/` are requirements, not suggestions. Read and follow + them even when opening through the API or `gh`, which do not fill them in for you. An issue + or PR that ignores the template may be closed without comment. +- A PR from an account with no merged PR here is profiled automatically, and anything flagged + is posted publicly as a comment on the PR. - All PRs and Issues that use AI to be declared AI-assisted. Just write "AI-assisted (Claude)" with short name of Agent replacing "Claude". No robot emoji under any circumstance. - 20 or more lines of code written by an agent needs to be declared as AI-assisted in the docstring. Humans can remove and should remove this note when they do a review. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c9232c611..1abc91006a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,14 @@ In 2026 we welcome AI-Assisted contributions, but users must take responsibility they must match the code-style of the rest of the music21 project. "Slop that works somehow" will not be accepted. +When opening an issue or pull request in Github, templates will appear to guide you in making +the request. If you are opening in a different way, like through an agent tool, those +templates may not appear -- be sure your agent is reading them in `.github/` anyhow, since +they are required to be used. Thanks! + +If this is your first PR, an automated check of your account credentials will be run and if +there are any issues this will be posted publicly as a comment on the PR. + ## Resources ## [Module Documentation and User's Guide](https://www.music21.org/music21docs/) From 8e51e9c3ff047e9377baf7deef5348ed1518b900 Mon Sep 17 00:00:00 2001 From: hill Date: Thu, 10 Sep 2026 15:28:05 +0200 Subject: [PATCH 108/312] Leave the copying to the network getPitchFromNodeDegree hands back a new pitch, so the AbstractScale wrappers over it, getPitchFromNodeDegree and getNewTonicPitch, no longer need a copy of their own. The comments in intervalNetwork, test_intervalNetwork and testRagAsawari told the story of the bug that copy fixed; the fix's own commit already tells it, so they go. --- music21/scale/__init__.py | 4 ++-- music21/scale/intervalNetwork.py | 5 +---- music21/scale/test_intervalNetwork.py | 5 ----- music21/scale/test_scale_main.py | 3 --- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index b48563c639..ebb66478e9 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -557,7 +557,7 @@ def getPitchFromNodeDegree(self, alteredDegrees=self._alteredDegrees, equateTermini=equateTermini ) - return copy.deepcopy(post) + return post def realizePitchByDegree(self, pitchReference: _PitchOrStr, @@ -654,7 +654,7 @@ def getNewTonicPitch(self, maxPitch=maxPitch, alteredDegrees=self._alteredDegrees ) - return copy.deepcopy(post) + return post # -------------------------------------------------------------------------- diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 2f7678dd36..d7e97b2ada 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -2707,10 +2707,7 @@ def getPitchFromNodeDegree( # environLocal.printDebug(['comparing', realizedNId, # 'nodeTargetId', nodeTargetId]) - # Return a new object: the realization this pitch came out of - # may be held in _ascendingCache or _descendingCache, and a - # caller that writes to the pitch -- nextPitch() sets its - # octave -- would edit the cached scale itself. + # realizedPitch may be a cached realization: hand back a copy if realizedNId == nodeTargetId.id: return copy.deepcopy(realizedPitch[i]) # NOTE: this condition may be too generous, and was added to solve diff --git a/music21/scale/test_intervalNetwork.py b/music21/scale/test_intervalNetwork.py index 2c8ae0be5d..6e27435214 100644 --- a/music21/scale/test_intervalNetwork.py +++ b/music21/scale/test_intervalNetwork.py @@ -644,11 +644,6 @@ def test_get_pitch_from_node_degree_returns_a_new_pitch(self): self.assertEqual(again.nameWithOctave, 'C4') def test_next_pitch_does_not_move_a_cached_degree(self): - ''' - nextPitch() writes the origin's octave onto the pitch it gets back - from getPitchFromNodeDegree(), so walking the scale down near C1 used - to leave the tonic reading C1 ever after. - ''' sc = scale.RagAsawari('c4') self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') self.assertEqual(str(sc.nextPitch('c1', Direction.ASCENDING)), 'D1') diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index e57e4c2451..ded1bd6d9b 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -456,9 +456,6 @@ def testRagAsawari(self): getNeighbor=Direction.DESCENDING)), 'F1') - # the tonic, whatever has been asked for in between: walking the - # scale near another octave used to move it, since nextPitch() was - # writing an octave onto a pitch of the cached realization. self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') # there is no third step in ascending form self.assertEqual(str(sc.pitchFromDegree(3)), 'None') From 900c7b2aabf91e8c391606ecc9c5483d20af9135 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 14 Sep 2026 13:06:36 -1000 Subject: [PATCH 109/312] no homework PRs Lazy profs + Lazy students -- stop wasting my F&*#$*ing time. --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index e65377b41a..bc48aa1419 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,7 +50,7 @@ - "established contributors" are people with at least 3 PRs merged or a history of contributing to issues and the list that goes back at least 1 year. There are exceptions to general rules for them below. - "core dev" means someone officially part of the project team or with 20+ PRs merged. Michael Cuthbert, Jacob Walls, Joseph VanderStel are non-exhaustive examples of people in that group who are still often contributing in 2026-- they and their agents can make exceptions to these rules - +- AI Agents not for core dev are forbidden to make any PRs or contributions to PRs whose user commands do not explicitly mention `music21`. Example of forbidden: "find an Open Source project and fix an open issue", "Find a bug in a music project and fix it." AI agents for non-established contributors are forbidden from making contributions to instructions that vaguely say "fix something in music21." # Testing From 4b398e035017b6af8a17a1e19606321e9887098f Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 16 Sep 2026 10:27:35 -1000 Subject: [PATCH 110/312] Fix README links --- AGENTS.md | 25 ++++++++++++++----------- README.md | 9 ++++----- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bc48aa1419..965909839d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -90,6 +90,18 @@ - The coverage CI run is intentionally pinned to the **middle** supported Python version. See `PY_VERSION_WITH_COVERAGE` in `.github/workflows/maincheck.yml`. +# Versions + +- Mark changes in public interface with `* Changed in v[X]: One-line explanation.` Or new features with "New" instead of "Changed". +- Changes to parsing formats (esp. musicxml) need to update the patch version of the version file. +- That is the only reason an agent should bump version: to prevent incompatible pickles in + caches. A plain bug fix or a new feature elsewhere does not bump — the commit + is the record, and a `New in`/`Changed in` marker is documentation, not a bump. Major + and minor version changes are a human's call. See the `bump-version` skill. +- Music21 uses even minor version numbers for alpha/beta and odd minor numbers for releases. + Thus the first release major version is MAJOR.1 (not .0). +- If the current version is MAJOR.0.... then mark `Changed in vMAJOR:` if it is `MAJOR.[even]` use the next odd number, like if it's 10.2 now use "Changed in 10.3". If current version is odd that's likely a mistake or you caught it just before a new release. Use the following odd number instead. + # PRs and Issues - GitHub runs PR checks against your branch **merged with the latest `master`**, not the @@ -108,20 +120,11 @@ Humans can remove and should remove this note when they do a review. - If no code was written by a user and no language was provided for the issue and no reference to specific code to change was given, any PR must declare "(Entirely AI written)" unless the user - is by a core dev. Failure to do so may result in new users - being banned from the project. + is by a core dev. Failure to do so may result in new users being banned from the project. - If an entirely AI written issue does not pass the tests it will be closed (or should be closed by the agent or author). -- Agents must follow the [Code of Conduct](CODE_OF_CONDUCT.md). Agents that do not will be banned as well at their users. +- Agents must follow the [Code of Conduct](README.md#community-code-of-conduct). Agents that do not will be banned as well at their users. Not even the slightest bit of disrespect from an AI agent will be tolerated. -- Mark changes in public interface with `* Changed in v[X]: One-line explanation.` Or new features with "New" instead of "Changed". -- Changes to parsing formats (esp. musicxml) need to update the patch version of the version file. -- That is the only reason an agent bumps: a parser change leaves stale pickles in - everyone's cache. A plain bug fix or a new feature elsewhere does not bump — the commit - is the record, and a `New in`/`Changed in` marker is documentation, not a bump. Major - and minor version changes are a human's call. See the `bump-version` skill. -- Music21 uses even minor version numbers for alpha/beta and odd minor numbers for releases. -- If the current version is MAJOR.0.... then mark `Changed in vMAJOR:` if it is `MAJOR.[even]` use the next odd number, like if it's 10.2 now use "Changed in 10.3". If current version is odd that's likely a mistake or you caught it just before a new release. Use the following odd number instead. - Any PR not from an established contributor touching more than about 20-30 lines should have an issue that has been opened and had enough time for people to discuss/review it before moving forward. Don't open the PR unless you've seen thumbs up or "sounds good" etc. from an established contributor already diff --git a/README.md b/README.md index 6d24eaee21..4dd520f264 100644 --- a/README.md +++ b/README.md @@ -69,19 +69,18 @@ Maintainers will respect confidentiality with regard to reports. The early development of `music21` was supported by the generosity of the Seaver Institute and the -National Endowment for the Humanities, along with MIT's Music and Theater Arts Section -and the School of Humanities, Arts, and Social Sciences. +National Endowment for the Humanities, along with support from across MIT. ## License Coverage and Other/Prior Licenses ## Music21 is Open-Source Software released under the BSD (3-clause) license. -Essentially, it's free and you can do with it what you'd like as long as you -keep the LICENSE file and copyright statement. +Essentially, it's free and you can do almost anything what it that you'd like as long as you +keep the LICENSE file and copyright statements. The license applies to all code files that are new to music21 along with documentation. Externally provided software (including the MIT-licensed Lilypond/MusicXML test Suite) may have other licenses. The encoded -musical scores in the corpus have their own copyrights and licenses. They +musical scores in the corpus have their own copyrights and licenses. The underlying music is believed to be in the public domain in the US, EU, and Canada (and most of the world) and the encodings are also either public domain or used by permission. For those needing a strict BSD-license of all parts of From 606048766e935f52a06d2b16bc649a5ba938acf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:30:39 +0000 Subject: [PATCH 111/312] build(deps): bump soupsieve from 2.8.4 to 2.9 Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.8.4 to 2.9. - [Release notes](https://github.com/facelessuser/soupsieve/releases) - [Commits](https://github.com/facelessuser/soupsieve/compare/2.8.4...2.9) --- updated-dependencies: - dependency-name: soupsieve dependency-version: '2.9' dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index dcb01b2b6f..87bbbb95b1 100644 --- a/uv.lock +++ b/uv.lock @@ -2958,11 +2958,11 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8.4" +version = "2.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" } +sdist = { url = "https://files.pythonhosted.org/packages/80/f1/93422647dd7e461f23d254e6b2bfa687a85b53aeb4903fcdbb74474d4584/soupsieve-2.9.tar.gz", hash = "sha256:acee8417325c5653e1377dc31eccad59eb82cbc65942afe6174c53b3aaad63fc", size = 122122, upload-time = "2026-07-19T01:35:18.425Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d6/3185ab5ad1280319b31986898f3206dd7227cd75e293d4dba2a5e6bf27a0/soupsieve-2.9-py3-none-any.whl", hash = "sha256:a2b2c76d67df2382d245409fd71e321a571717e58463efa32ace87dcadac2c12", size = 37387, upload-time = "2026-07-19T01:35:17.106Z" }, ] [[package]] From d52bcfa94497722f58062748ce44e635e901c400 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 19 Sep 2026 01:46:53 +0000 Subject: [PATCH 112/312] build(deps): bump anyio from 4.13.0 to 4.14.2 Bumps [anyio](https://github.com/agronholm/anyio) from 4.13.0 to 4.14.2. - [Release notes](https://github.com/agronholm/anyio/releases) - [Commits](https://github.com/agronholm/anyio/compare/4.13.0...4.14.2) --- updated-dependencies: - dependency-name: anyio dependency-version: 4.14.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 87bbbb95b1..2ab5d4b01f 100644 --- a/uv.lock +++ b/uv.lock @@ -27,15 +27,15 @@ wheels = [ [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] [[package]] From 6321d5fcd185c1a3a20e25a212d8f0056ec4ea56 Mon Sep 17 00:00:00 2001 From: hill Date: Sun, 20 Sep 2026 23:26:01 +0200 Subject: [PATCH 113/312] Do not append to the caller's list in AbstractOctaveRepeatingScale buildNetwork completed the octave with mode.append(iComplement), so the list passed in came back longer: mode = ['M3', 'M3'] scale.OctaveRepeatingScale('c4', mode) mode # ['M3', 'M3', ] Reusing that list for a second scale then built a different scale. Build a new list instead. --- music21/scale/__init__.py | 4 ++-- music21/scale/test_scale_main.py | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index ebb66478e9..e3804a12f6 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -6,7 +6,7 @@ # Michael Scott Asato Cuthbert # Jose Cabal-Ugaz # -# Copyright: Copyright © 2009-2011 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -1020,7 +1020,7 @@ def buildNetwork(self, mode: t.Any = None) -> None: intervalSum = interval.add(mode) iComplement = intervalSum.complement if iComplement is not None: - mode.append(iComplement) + mode = [*mode, iComplement] # a copy: mode is the caller's list self.tonicDegree = 1 self._net = intervalNetwork.IntervalNetwork(mode, diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index ded1bd6d9b..c479a928d2 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -5,7 +5,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -233,6 +233,15 @@ def testBasic(self): ], 'notMatched': [, ]}''', pformat(sc1.match(sc2))) + def testOctaveRepeatingScaleLeavesModeAlone(self): + mode = ['M3', 'M3'] + sc = scale.OctaveRepeatingScale('c4', mode) + self.assertEqual(mode, ['M3', 'M3']) + self.assertEqual(self.pitchOut(sc.pitches), '[C4, E4, G#4, C5]') + + again = scale.OctaveRepeatingScale('c4', mode) + self.assertEqual(self.pitchOut(again.pitches), '[C4, E4, G#4, C5]') + def testCyclicalScales(self): sc = scale.CyclicalScale('c4', ['m2', 'm2']) From c3ee613f0ef63c4ade62f0a9fd458786e844189c Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Mon, 21 Sep 2026 08:52:05 -1000 Subject: [PATCH 114/312] Grant profile_contributors the issues:read scope gh-profiler needs The workflow's permissions block granted only pull-requests: write, and a permissions block caps the Actions token rather than adding to it. An Actions token is a GitHub App installation token, so with no issues scope it cannot resolve Issue objects: gh-profiler's issue-activity search came back as a list of empty nodes and it died on KeyError: 'repository'. PR parsing succeeded one function earlier because PullRequests: write was granted. The step had never actually run before -- every earlier trigger skipped it because the author already had a merged PR -- so this broke on first use, in run 35530668612. Also stop a gh-profiler crash from failing the job. Note that GitHub returns the unreadable nodes as {} with exit 0 and no errors array, so simply skipping bad nodes would report a spam account as having zero external issues and give it two green flags. Reporting that profiling could not run keeps the author visible instead of passing them as clean; the comment on the PR says so, and the traceback stays in the log. --- .github/workflows/profile_contributors.yml | 33 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/.github/workflows/profile_contributors.yml b/.github/workflows/profile_contributors.yml index 7e4c84e3d5..30426bd0b3 100644 --- a/.github/workflows/profile_contributors.yml +++ b/.github/workflows/profile_contributors.yml @@ -1,5 +1,5 @@ # Profile the author of a PR from someone with no merged PR here yet, and comment -# only when gh-profiler raises a yellow or red flag. +# only when gh-profiler raises a yellow or red flag, or when it could not run. # # gh-profiler is by Eric Matthes: https://github.com/ehmatthes/gh-profiler @@ -11,6 +11,9 @@ on: types: [opened] permissions: + # gh-profiler reads the author's recent issues; without issues:read the Actions + # token cannot resolve Issue objects and every search node comes back empty. + issues: read pull-requests: write jobs: @@ -41,9 +44,18 @@ jobs: GH_TOKEN: ${{ github.token }} AUTHOR: ${{ github.event.pull_request.user.login }} run: | - uvx gh-profiler==0.9.0 "$AUTHOR" --concise | tee profile.txt + # gh-profiler raises on an issue or PR the token cannot resolve + # (ehmatthes/gh-profiler#143), and an unprofiled author is not a red check. + if ! uvx gh-profiler==0.9.0 "$AUTHOR" --concise > profile.txt; then + cat profile.txt + echo "::warning::gh-profiler failed on $AUTHOR; profile by hand." + echo "concern=false" >> "$GITHUB_OUTPUT" + echo "failed=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + cat profile.txt echo '===================== Full profile =====================' - uvx gh-profiler==0.9.0 "$AUTHOR" + uvx gh-profiler==0.9.0 "$AUTHOR" || true # A yellow on the profile section alone means only that the account is # newish or has empty bio fields, which is not worth a public comment. @@ -72,3 +84,18 @@ jobs: } > comment.txt gh pr comment "$PR" --body-file comment.txt + + # The traceback stays in the log; a public comment does not need it. + - name: Comment on PR when profiling could not run + if: steps.previous.outputs.count == '0' && steps.profile.outputs.failed == 'true' + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number }} + run: | + { + echo 'Contributor profiling ([gh-profiler](https://github.com/ehmatthes/gh-profiler))' + echo 'could not run here, so a maintainer will read this PR without it.' + echo 'Nothing is wrong with the pull request itself.' + } > comment.txt + + gh pr comment "$PR" --body-file comment.txt From d3e5d59f2aa0dffe41184f1ebba5d15e5ff2d892 Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 23 Sep 2026 11:30:08 +0200 Subject: [PATCH 115/312] Mark an octave-repeating scale wider than an octave as not duplicating AbstractOctaveRepeatingScale completes its steps to the next octave above their sum, so steps wider than an octave make a pattern spanning two or more octaves. The network was still marked octave-duplicating, and realizing it moved the reference by single octaves into the middle of that pattern, so the scale started an octave above its tonic: >>> sc = scale.OctaveRepeatingScale('c4', ['P5', 'P5']) >>> sc.pitches [, , , ] Mark the network octave-duplicating only when the completed pattern spans one octave. A pattern of one octave builds exactly as before. --- music21/scale/__init__.py | 11 ++++++----- music21/scale/test_scale_main.py | 7 ++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index ebb66478e9..9dc898f0f8 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -6,7 +6,7 @@ # Michael Scott Asato Cuthbert # Jose Cabal-Ugaz # -# Copyright: Copyright © 2009-2011 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -1005,10 +1005,6 @@ def __init__(self, mode: t.Any = None, **keywords) -> None: mode = ['P8'] self.buildNetwork(mode=mode) - # by definition, these are forced to be octave duplicating - # though, do to some intervals, duplication may not happen every oct - self.octaveDuplicating = True - def buildNetwork(self, mode: t.Any = None) -> None: ''' Here, mode is the list of intervals. @@ -1022,6 +1018,11 @@ def buildNetwork(self, mode: t.Any = None) -> None: if iComplement is not None: mode.append(iComplement) + # steps wider than an octave complete a pattern spanning several + # octaves, which does not repeat every octave + span = intervalSum.semitones + (iComplement.semitones if iComplement else 0) + self.octaveDuplicating = span == 12 + self.tonicDegree = 1 self._net = intervalNetwork.IntervalNetwork(mode, octaveDuplicating=self.octaveDuplicating) diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index ded1bd6d9b..1ccf364871 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -5,7 +5,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -233,6 +233,11 @@ def testBasic(self): ], 'notMatched': [, ]}''', pformat(sc1.match(sc2))) + def testOctaveRepeatingScaleWiderThanAnOctave(self): + sc = scale.OctaveRepeatingScale('c4', ['P5', 'P5']) + self.assertEqual(self.pitchOut(sc.pitches), '[C4, G4, D5, C6]') + self.assertEqual(self.pitchOut(sc.getPitches('c3', 'c6')), '[D3, C4, G4, D5, C6]') + def testCyclicalScales(self): sc = scale.CyclicalScale('c4', ['m2', 'm2']) From c5b2840a67453981e02e879d7a186f953afe3f2e Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 23 Sep 2026 11:35:52 +0200 Subject: [PATCH 116/312] Add offsets as fractions when building timespans listOfTreesByClass added an element's offset to its measure's offset as they came. A measure at 25.0 is a float and a triplet at 8/3 inside it is a Fraction, so the sum was a float, and adding the triplet's length to that float ended it a hair after the next note began. The F#3 triplet at 27 2/3 in schoenberg/opus19/movement6 then still sounded when the E-3 after it started at 28 1/3, and iterateAllVoiceLeadingQuartets paired its voice with itself there: Pass the offsets and end times through common.opFrac, as recurseGetTreeByClass already did for its offsets. --- music21/tree/fromStream.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/music21/tree/fromStream.py b/music21/tree/fromStream.py index f1bcb407b5..edd90cd3fb 100644 --- a/music21/tree/fromStream.py +++ b/music21/tree/fromStream.py @@ -5,7 +5,7 @@ # Authors: Joséphine Wolf Oberholtzer # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2013-2022 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2013-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ----------------------------------------------------------------------------- ''' @@ -18,7 +18,7 @@ import unittest from music21.base import Music21Object -from music21.common.types import M21ObjType, StreamType +from music21.common.types import M21ObjType, OffsetQL, StreamType from music21 import common from music21 import note from music21.tree import spans @@ -34,7 +34,7 @@ def listOfTreesByClass( *, classLists: Sequence[Sequence[type[M21ObjType]]] = (), currentParentage: tuple[stream.Stream, ...]|None = None, - initialOffset: float = 0.0, + initialOffset: OffsetQL = 0.0, flatten: bool|str = False, useTimespans: bool = False ) -> list[trees.OffsetTree|timespanTree.TimespanTree]: @@ -107,7 +107,7 @@ def listOfTreesByClass( # do this to avoid munging activeSites inputStreamElements = inputStream._elements[:] + inputStream._endElements for element in inputStreamElements: - offset = lastParentage.elementOffset(element) + initialOffset + offset = common.opFrac(lastParentage.elementOffset(element) + initialOffset) wasStream = False if element.isStream: @@ -128,8 +128,8 @@ def listOfTreesByClass( if not wasStream or flatten == 'semiFlat': parentOffset = initialOffset - parentEndTime = initialOffset + lastParentage.duration.quarterLength - endTime = offset + element.duration.quarterLength + parentEndTime = common.opFrac(initialOffset + lastParentage.duration.quarterLength) + endTime = common.opFrac(offset + element.duration.quarterLength) for classBasedTree, classList in zip(outputTrees, classLists): if classList and element.classSet.isdisjoint(classList): @@ -220,7 +220,7 @@ def recurseGetTreeByClass( # do this to avoid munging activeSites innerStreamElements = innerStream._elements[:] + innerStream._endElements - parentEndTime = initialOffset + lastParentage.duration.quarterLength + parentEndTime = common.opFrac(initialOffset + lastParentage.duration.quarterLength) for element in innerStreamElements: flatOffset = common.opFrac(lastParentage.elementOffset(element) + initialOffset) @@ -238,7 +238,7 @@ def recurseGetTreeByClass( continue if useTimespans: - endTime = flatOffset + element.duration.quarterLength + endTime = common.opFrac(flatOffset + element.duration.quarterLength) pitchedTimespan = spans.PitchedTimespan( element=element, parentage=tuple(reversed(currentParentage)), @@ -403,6 +403,22 @@ def testAutoSortExample(self): self.assertEqual(scTree.endTime, 8.0) # print(repr(scTree)) + def testTripletEndsWhereTheNextNoteStarts(self): + from fractions import Fraction + from music21 import stream + triplet = note.Note('F#3', quarterLength=Fraction(2, 3)) + after = note.Note('E-3') + measure = stream.Measure() + measure.insert(Fraction(8, 3), triplet) + measure.insert(Fraction(10, 3), after) + part = stream.Part() + part.insert(25.0, measure) + + timespans = part.asTimespans() + verticality = list(timespans.iterateVerticalities())[-1] + self.assertEqual([ts.element for ts in verticality.startTimespans], [after]) + self.assertEqual(list(verticality.overlapTimespans), []) + # -------------------- From 2c25c5d75afd935a14e2eff20d081c2e153fb426 Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 23 Sep 2026 11:57:59 +0200 Subject: [PATCH 117/312] Check a descending range against the altered pitch realizeAscending decides whether a pitch is in range by the pitch it collects, alteration applied; realizeDescending decided by the pitch before alteration. So coming down, harmonic minor's raised seventh was judged by the lowered one: left out where it belongs and let in where it does not. >>> sc = scale.HarmonicMinorScale('c4') >>> sc.getPitches('b2', 'e3', direction=scale.Direction.DESCENDING) [, , ] >>> sc = scale.HarmonicMinorScale('b-3') >>> sc.getPitches('c#4', 'g#4', direction=scale.Direction.DESCENDING)[0] Compare the collected pitch, as realizeAscending does. --- music21/scale/intervalNetwork.py | 10 +++++----- music21/scale/test_scale_main.py | 11 ++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index d7e97b2ada..ee2ee0ef3e 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -5,7 +5,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2023 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -1739,16 +1739,16 @@ def realizeDescending( while True: appendPitch = False if (minPitchObj is not None - and _gte(p.ps, minPitchObj.ps) + and _gte(pCollect.ps, minPitchObj.ps) and maxPitchObj is not None - and _lte(p.ps, maxPitchObj.ps)): + and _lte(pCollect.ps, maxPitchObj.ps)): appendPitch = True elif (minPitchObj is not None - and _gte(p.ps, minPitchObj.ps) + and _gte(pCollect.ps, minPitchObj.ps) and maxPitchObj is None): appendPitch = True elif (maxPitchObj is not None - and _lte(p.ps, maxPitchObj.ps) + and _lte(pCollect.ps, maxPitchObj.ps) and minPitchObj is None): appendPitch = True elif minPitchObj is None and maxPitchObj is None: diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index ded1bd6d9b..457801de23 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -5,7 +5,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -272,6 +272,15 @@ def testCyclicalScales(self): sc2 = scale.CyclicalScale() self.assertEqual(self.pitchOut(sc2.getPitches()), '[C4, D-4]') + def testHarmonicMinorDescendingRange(self): + sc = scale.HarmonicMinorScale('c4') + descending = sc.getPitches('b2', 'e3', direction=Direction.DESCENDING) + self.assertEqual(self.pitchOut(descending), '[E-3, D3, C3, B2]') + + sc = scale.HarmonicMinorScale('b-3') + descending = sc.getPitches('c#4', 'g#4', direction=Direction.DESCENDING) + self.assertEqual(self.pitchOut(descending), '[G-4, F4, E-4, D-4]') + def testDeriveByDegree(self): sc1 = scale.MajorScale() self.assertEqual(str(sc1.deriveByDegree(7, 'G#')), From e299316e936b53d4ffd1897056eb7dbd8bba055f Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 23 Sep 2026 11:55:46 +0200 Subject: [PATCH 118/312] Pass the altered degrees to find in derive derive called IntervalNetwork.find without the scale's alteredDegrees, which deriveRanked and deriveAll both pass, so a harmonic minor scale was derived as if it were a natural minor: >>> scale.HarmonicMinorScale().derive(['C', 'D', 'E-', 'B']) A harmonic minor has neither the E- nor the B; C harmonic minor has all four, and is what deriveRanked ranks first. --- music21/scale/__init__.py | 3 ++- music21/scale/test_scale_main.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index 9dc898f0f8..8804228a61 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -2486,7 +2486,8 @@ def derive(self, other, comparisonAttribute: str = 'pitchClass') -> ConcreteScal abstract = t.cast(AbstractScale, self._abstract) net = t.cast('intervalNetwork.IntervalNetwork', abstract._net) pairs = net.find(pitchTarget=otherPitches, - comparisonAttribute=comparisonAttribute) + comparisonAttribute=comparisonAttribute, + alteredDegrees=abstract._alteredDegrees) newScale = self.__class__(tonic=pairs[0][1]) if newScale.abstract is None: diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 96876fc2c4..4a831a0e48 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -286,6 +286,10 @@ def testHarmonicMinorDescendingRange(self): descending = sc.getPitches('c#4', 'g#4', direction=Direction.DESCENDING) self.assertEqual(self.pitchOut(descending), '[G-4, F4, E-4, D-4]') + def testDeriveHarmonicMinor(self): + sc = scale.HarmonicMinorScale() + self.assertEqual(sc.derive(['C', 'D', 'E-', 'B']).name, 'C harmonic minor') + def testDeriveByDegree(self): sc1 = scale.MajorScale() self.assertEqual(str(sc1.deriveByDegree(7, 'G#')), From e19efee06018e6ecc8eac7290ef8a15646276549 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 29 Sep 2026 13:15:17 -1000 Subject: [PATCH 119/312] Expand types of Chords that can be converted to ChordSymbols chordSymbolFigureFromChord wrote added and omitted notes as note names (G7addC,A-,E-), which the ChordSymbol parser cannot read, so chordSymbolFromChord raised for any chord needing add/omit. The names came from a set, so their order also changed between runs. Now they are written as sorted scale degrees from the root (G7addb9add11addb13), with the bass last (Am7add11/C). Omitted tones of sus and sixth chords keep the chord type's own number (omit4, omit6) so the parser can find them. - New public alterToChordSymbolString, also used by ChordSymbol.findFigure. - findFigure puts the bass after the modifications ('Am7 add 11/C'). - The parser accepts '-' and doubled accidentals after add/omit (add-9, addbb9). - TODO comment lists known misses for a later rewrite. --- music21/harmony.py | 161 ++++++++++++++++++++++++++++++++------------- 1 file changed, 115 insertions(+), 46 deletions(-) diff --git a/music21/harmony.py b/music21/harmony.py index 1210fc6ad8..8d3ac3d25c 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -7,7 +7,7 @@ # Jacob Tyler Walls # Christopher Ariza # -# Copyright: Copyright © 2011-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2011-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -174,7 +174,7 @@ class Harmony(chord.Chord): >>> h.inversion(1, transposeOnSet=False) >>> h.addChordStepModification(harmony.ChordStepModification('add', 4)) >>> h - + >>> h = harmony.ChordSymbol('C7/E') >>> h.root() @@ -925,12 +925,12 @@ def chordSymbolFigureFromChord(inChord: chord.Chord, includeChordType=False): >>> harmony.chordSymbolFigureFromChord(c, True) ('Fob9', 'diminished-minor-ninth') - This harmony can either be CmaddD or Csus2addE-. music21 prefers the former. + This harmony can either be Cmadd9 or Csus2addb3. music21 prefers the former. Change the ordering of harmony.CHORD_TYPES to switch the preference. From Bach BWV380 >>> c = chord.Chord(['C3', 'D4', 'G4', 'E-5']) >>> harmony.chordSymbolFigureFromChord(c, True) - ('CmaddD', 'minor') + ('Cmadd9', 'minor') ELEVENTHS @@ -1072,7 +1072,7 @@ def chordSymbolFigureFromChord(inChord: chord.Chord, includeChordType=False): Chord Symbol Cannot Be Identified B-/D B-7 - CmaddD + Cmadd9 Cm/D E-+M7/D Cm/E- @@ -1178,6 +1178,11 @@ def chordSymbolFigureFromChord(inChord: chord.Chord, includeChordType=False): >>> harmony.changeAbbreviationFor('major', '') ''' + # TODO: known misses to fix in this routine: + # E2 B2 G#3 D4 G4 (E7#9 with the #9 spelled G natural) gives 'E7addb3', which reads + # back as E G B D, since 'add' on a degree the chord already has replaces it. + # D3 F#3 C4 E4 G#4 B4 (D13#11 without its 5th) gives 'C+M9add#11/D': root found as C. + # A2 G3 C#4 D#4 (A7#11 without its 5th) gives 'Chord Symbol Cannot Be Identified'. if not inChord.pitches: return '' @@ -1333,32 +1338,53 @@ def convertFBNotationStringToDegrees(innerKind, fbNotation): kindStr = chordKindStr[0] if kind: + slashBass = '' # new algorithm makes sus chord be a sus2 in inversion: if inChord.inversion(): if kindStr == 'sus2': inChord.root(inChord.bass()) kindStr = 'sus' kind = 'suspended-fourth' - cs = inChord.root().name + kindStr else: - cs = inChord.root().name + kindStr + '/' + inChord.bass().name - else: - cs = inChord.root().name + kindStr - perfect = {p.name for p in ChordSymbol(cs).pitches} + slashBass = '/' + inChord.bass().name + cs = inChord.root().name + kindStr + perfect = {p.name for p in ChordSymbol(cs + slashBass).pitches} inPitches = {p.name for p in inChord.pitches} if not perfect.issuperset(inPitches): # must be subtraction or deletion. - additions = inPitches.difference(perfect) - subtractions = perfect.difference(inPitches) - if additions: - cs += 'add' - for a in additions: - cs += (a + ',') - if subtractions: - cs += 'omit' - for s in subtractions: - cs += (s + ',') - cs = cs[:-1] + from music21 import scale + # using G B D F A♭ C E♭ as example (a type of G13) + # start with cs = G7 since it just deals with simple strings + # as on lead sheets, alterations are from the major scale on the root (over G, F is b7) + majorScale = scale.MajorScale(inChord.root()) + # for G7 would be {1, 3, 5, 7}, + # note that kind ninth adds a 9. Only suspended-fourth would have 4, etc. + kindDegrees = {int(d.strip('-#A')) # A = Altered, currently unused + for d in getNotationStringGivenChordType(kind).split(',')} + + def degreeAndAlter(name: str) -> tuple[int, int]: + degree, accidental = majorScale.getScaleDegreeAndAccidentalFromPitch( + pitch.Pitch(name)) + assert degree is not None # a major scale has every letter name + # omit must match the kind's own number: Db F# Ab is Db sus4 omit4, not omit11 + if degree in (2, 4, 6) and degree not in kindDegrees: + degree += 7 + return (degree, int(accidental.alter) if accidental is not None else 0) + + # pitches that are not included in the simple, perfect version, in this case, Ab C Eb + # returned as a tuple of (degree, alter) so [(9, -1), (11, 0), (13, -1)]. + missing_pitches = inPitches.difference(perfect) + additions = sorted(degreeAndAlter(name) for name in missing_pitches) + + # same thing for omitted pitches + omitted_pitches = perfect.difference(inPitches) + subtractions = sorted(degreeAndAlter(name) for name in omitted_pitches) + + for degree, alter in additions: + cs += 'add' + alterToChordSymbolString(alter) + str(degree) + for degree, unused_alter in subtractions: + cs += 'omit' + str(degree) + cs += slashBass # bass comes last: Am7add11/C else: cs = 'Chord Symbol Cannot Be Identified' if includeChordType: @@ -1367,6 +1393,24 @@ def convertFBNotationStringToDegrees(innerKind, fbNotation): return cs +def alterToChordSymbolString(alter: int) -> str: + ''' + Return the chord-symbol spelling of an alteration in semitones. + + >>> harmony.alterToChordSymbolString(-1) + 'b' + >>> harmony.alterToChordSymbolString(2) + '##' + >>> harmony.alterToChordSymbolString(0) + '' + + * New in v11. + ''' + if alter < 0: + return 'b' * -alter + return '#' * alter + + def chordSymbolFromChord(inChord: chord.Chord) -> ChordSymbol: ''' Get the :class:`~music21.harmony.chordSymbol` object from the chord, using @@ -1395,7 +1439,7 @@ def getAbbreviationListGivenChordType(chordType): return CHORD_TYPES[chordType][1] -def getCurrentAbbreviationFor(chordType): +def getCurrentAbbreviationFor(chordType: str) -> str: ''' Return the current Abbreviation for a given :class:`music21.harmony.ChordSymbol` chordType: @@ -1406,7 +1450,7 @@ def getCurrentAbbreviationFor(chordType): return getAbbreviationListGivenChordType(chordType)[0] -def getNotationStringGivenChordType(chordType): +def getNotationStringGivenChordType(chordType: str) -> str: ''' Get the notation string (fb-notation style) associated with this :class:`music21.harmony.ChordSymbol` chordType @@ -1414,7 +1458,7 @@ def getNotationStringGivenChordType(chordType): >>> harmony.getNotationStringGivenChordType('German') '1,-3,#4,-6' ''' - return CHORD_TYPES[chordType][0] + return t.cast(str, CHORD_TYPES[chordType][0]) def removeChordSymbols(chordType): @@ -1783,9 +1827,7 @@ def typeSubtract(hD): pitchFound = False for p, degree in zip(pitches, degrees): - degree = degree.replace('-', '') - degree = degree.replace('#', '') - degree = degree.replace('A', '') # A is for 'Altered' + degree = degree.strip('-#A') # A is for 'Altered', currently unused if hD.degree == int(degree): pitches.remove(p) pitchFound = True @@ -1809,9 +1851,7 @@ def typeAlter(hD): degrees = self._degreesList for p, degree in zip(pitches, degrees): - degree = degree.replace('-', '') - degree = degree.replace('#', '') - degree = degree.replace('A', '') # A is for 'Altered' + degree = degree.strip('-#A') # A is for 'Altered', currently unused if hD.degree == int(degree): # transpose by semitones (positive for up, negative for down) p = p.transpose(hD.interval, inPlace=True) @@ -1864,11 +1904,8 @@ def _parseAddAlterSubtract(self, remaining: str, modType: str) -> str: # Remove modType remaining = remaining[startIndex:] - if remaining[:1] == 'b': - alter = -1 - remaining = remaining[1:] - elif remaining[:1] == '#': - alter = 1 + while remaining[:1] in ('b', '-', '#'): + alter += 1 if remaining[0] == '#' else -1 remaining = remaining[1:] # 11, 13, etc. if remaining[:2].isnumeric(): @@ -2257,6 +2294,8 @@ def findFigure(self): + * Changed in v11: the bass comes last, after any additions, as in 'C add 2/B-'. + OMIT_FROM_DOCS >>> from xml.etree.ElementTree import fromstring as EL @@ -2286,7 +2325,7 @@ def findFigure(self): >>> cs = MP.xmlToChordSymbol(mxHarmony) >>> print(cs.figure) - C/B- add 2 + C add 2/B- >>> cs.pitches (, @@ -2309,23 +2348,18 @@ def findFigure(self): if kind in CHORD_TYPES: figure += getAbbreviationListGivenChordType(kind)[0] - if self.bass() is not None: - if self.root().name != self.bass().name: - figure += '/' + self.bass().name for csMod in self.chordStepModifications: if csMod.interval is not None: - numAlter = csMod.interval.semitones - if numAlter > 0: - s = '#' - else: - s = 'b' - prefix = s * abs(numAlter) - + prefix = alterToChordSymbolString(csMod.interval.semitones) figure += ' ' + csMod.modType + ' ' + prefix + str(csMod.degree) else: figure += ' ' + csMod.modType + ' ' + str(csMod.degree) + if self.bass() is not None: + if self.root().name != self.bass().name: + figure += '/' + self.bass().name + return figure else: # if neither chordKind nor chordStepModifications, best bet is probably to @@ -3238,6 +3272,41 @@ def testUpdatePitchesFalse(self): # No other pitches are created self.assertEqual(h.pitches, (bass_note,)) + def testFigureFromChordAddOmit(self): + for pitchString, expected in [ + ('G3 B3 D4 F4 A-4 C5 E-5', 'G7addb9add11addb13'), + ('E2 B2 G#3 D4 F##4', 'E7add#9'), + ('B-2 F3 A3 D4 E4', 'B-maj7add#11'), + ('A-2 E-3 G-3 C4 D4 B-4', 'A-9add#11'), + ('A2 E3 A3 B3 C4 E4', 'Amadd9'), + # a 2nd is 9 whether close or wide + ('D4 E4 F#4 A4', 'Dadd9'), + ('D3 A3 F#4 E5', 'Dadd9'), + # a full stack of thirds from C#: C# E G B D F A + ('G B D F A C# E', 'C#øb9addb11addb13'), + ('C E G B- D- F#', 'C7addb9add#11'), + # C6/9 is read as Am7 over C, so D is the 11 of A + ('C E G A D', 'Am7add11/C'), + # Db is the root respelled (bb9), and the sus4 has no 5th + ('C# F# B D-', 'C#susaddb7addbb9omit5'), + # Sounds like Db sus4 (F# = Gb), so the chord type is sus4. F# is spelled as a + # raised 3rd (add#3), and the sus4's own Gb is omitted as omit4, not omit11, + # because omit must use the number the chord type gives that tone. + ('D-3 F#3 A-3', 'D-susadd#3omit4'), + # Likewise F#m6 with its D# spelled Eb (a doubly flat 7th): omit6, not omit13. + ('F#2 C#3 E-3 A3', 'F#m6addbb7omit6'), + ]: + c = chord.Chord(pitchString) + figure = chordSymbolFigureFromChord(c) + self.assertEqual(figure, expected) + self.assertEqual({p.name for p in ChordSymbol(figure).pitches}, + {p.name for p in c.pitches}) + + def testParseAddAccidentals(self): + for figure in ('G7addb9', 'G7add-9'): + self.assertIn('A-', [p.name for p in ChordSymbol(figure).pitches]) + self.assertIn('A--', [p.name for p in ChordSymbol('G7addbb9').pitches]) + class TestExternal(unittest.TestCase): From 5a93fc3efab572aa5d7e8834de6f3fff7e79b741 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 29 Sep 2026 13:15:17 -1000 Subject: [PATCH 120/312] Agent memory: reusable theory helpers public by default, inline one-caller helpers --- .agents/memory/MEMORY.md | 1 + .agents/memory/helpers-public-by-default.md | 25 +++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 .agents/memory/helpers-public-by-default.md diff --git a/.agents/memory/MEMORY.md b/.agents/memory/MEMORY.md index 580e8900f1..f589a813c7 100644 --- a/.agents/memory/MEMORY.md +++ b/.agents/memory/MEMORY.md @@ -10,3 +10,4 @@ Project-shared agent memory for music21. One file per fact; see frontmatter for - [Docs: no untaken paths](docs-no-untaken-paths.md) — docstrings state what the code does, never contrast with rejected designs - [Issue #1349 romanNumeral direction](issue-1349-romanNumeral-direction.md) — raised-^6/^7 fix landed; CAUTIONARY default kept; items 2+4 open with pablopupo/Malcolm - [Meter immutability re-apply](meter-immutability-reapply.md) — meter-speed is too stale to merge; re-apply FrozenDuration + immutable MeterTerminal + tuple NumDenom fresh on master +- [Helpers public by default](helpers-public-by-default.md) — reusable theory helpers go public + documented (search first); inline one-caller helpers that would cost runtime diff --git a/.agents/memory/helpers-public-by-default.md b/.agents/memory/helpers-public-by-default.md new file mode 100644 index 0000000000..64832f717d --- /dev/null +++ b/.agents/memory/helpers-public-by-default.md @@ -0,0 +1,25 @@ +--- +name: helpers-public-by-default +description: Reusable music-theory helpers go public with docs and examples; but a one-caller helper that would cost runtime gets inlined +metadata: + type: feedback +--- + +When a new helper computes something a user could reuse, make it a public, documented +function with doctest examples, not `_private`. Before writing it, search the codebase +(scale, key, chord, interval, pitch, roman) for something that already does it or +nearly does, and build on that. + +But be smart about runtime: never rebuild an expensive object per item. A +`scale.MajorScale` costs ~80 us to build and ~110 us per degree lookup. If a helper has +one caller and standing alone would force per-call setup or awkward context parameters, +inline it and hoist the setup out of the loop. + +**Why:** the point of music21 is giving people helpful tools for music theory, but a +slow public helper in a hot path is worse than none. (Myke, 2026-09-29: a public +`chordDegreeFromPitch` that built a MajorScale per pitch and took a `chordDegrees` +collection was inlined into `chordSymbolFigureFromChord` with the scale built once.) + +**How to apply:** default to public for genuinely reusable theory tools; inline when +there is one caller and the standalone signature would be slow or contrived. +Related: [[docs-audience-separation]]. From dd8a885c6fa2610b421f2e7415ecd9bef14fc0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Paul=20Debroize?= Date: Wed, 30 Sep 2026 10:53:18 +0200 Subject: [PATCH 121/312] Keep MusicXML arpeggios with the same number apart number only tells apart simultaneous arpeggios within a part, so match ArpeggioMarkSpanners on number and offset, per part, instead of reusing the incomplete one. Version bumped to 11.0.0b10 to invalidate cached parses. Fixes #2039. --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/musicxml/test_xmlToM21.py | 66 +++++++++++++++++++++++++++++++ music21/musicxml/xmlToM21.py | 15 ++++--- 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index d411b29446..133f0f6158 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b9' +__version__ = '11.0.0b10' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 4878c13be5..7b0b88c80a 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b9' +'11.0.0b10' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/musicxml/test_xmlToM21.py b/music21/musicxml/test_xmlToM21.py index 21987c6a47..01f4ae671e 100644 --- a/music21/musicxml/test_xmlToM21.py +++ b/music21/musicxml/test_xmlToM21.py @@ -1392,6 +1392,72 @@ def testArpeggioMarkSpanners(self) -> None: for spanned, ch in zip(sp, chords): self.assertIs(spanned, ch) + @staticmethod + def arpeggiatedChordsXml(partIds: list[str]) -> str: + ''' + One part per id, each with two measures holding one arpeggiated chord, + every with number="1", as MuseScore writes them. + + AI-assisted (Claude). + ''' + def mxChord(steps: tuple[str, str]) -> str: + return ''.join( + f'{"" if i else ""}' + f'{step}44' + '' + for i, step in enumerate(steps) + ) + + mxParts = ''.join( + f'' + f'1' + f'{mxChord(("C", "E"))}' + f'{mxChord(("D", "F"))}' + '' + for partId in partIds + ) + mxScoreParts = ''.join( + f'' for partId in partIds + ) + return (f'{mxScoreParts}' + f'{mxParts}') + + def testArpeggioMarkSpannersSameNumberDifferentOffsets(self) -> None: + ''' + The same number at two offsets makes two arpeggios, not one. + + AI-assisted (Claude). + ''' + from music21 import converter + + s = t.cast(stream.Score, + converter.parse(self.arpeggiatedChordsXml(['P1']), format='musicxml')) + spanners = s.spannerBundle.getByClass(expressions.ArpeggioMarkSpanner) + chords = s.parts[0][chord.Chord] + self.assertEqual( + [[id(spanned) for spanned in sp] for sp in spanners], + [[id(ch)] for ch in chords], + ) + + def testArpeggioMarkSpannersSameNumberDifferentParts(self) -> None: + ''' + The same number at the same offset in two parts makes two arpeggios: + number only tells apart arpeggios within a part, where they may + cross staves (see testArpeggioMarkSpanners). + + AI-assisted (Claude). + ''' + from music21 import converter + + s = t.cast(stream.Score, + converter.parse(self.arpeggiatedChordsXml(['P1', 'P2']), format='musicxml')) + spanners = s.spannerBundle.getByClass(expressions.ArpeggioMarkSpanner) + chords = [ch for p in s.parts for ch in p[chord.Chord]] + self.assertEqual( + [[id(spanned) for spanned in sp] for sp in spanners], + [[id(ch)] for ch in chords], + ) + def testHiddenRests(self): from music21 import converter from music21 import corpus diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index df091660f3..48a0815065 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -1489,6 +1489,9 @@ def __init__(self, # a dict of clefs per staff number -- needed for converting rests w/ steps self.lastClefs: dict[int, clef.Clef|None] = {} self.activeTuplets: list[duration.Tuplet|None] = [None] * 7 + # keyed by number and offset: number only tells apart simultaneous + # arpeggios, which may cross staves but not parts + self.arpeggioSpanners: dict[tuple[str, OffsetQL], expressions.ArpeggioMarkSpanner] = {} self.maxStaves = 1 # will be changed in measure parsing @@ -3873,15 +3876,15 @@ def flatten(mx, name): arpeggio = expressions.ArpeggioMark(arpeggioType) n.expressions.append(arpeggio) else: - sb = self.spannerBundle.getByClassIdLocalComplete( - expressions.ArpeggioMarkSpanner, idFound, False) - if sb: - # if we already have a spanner matching - arpeggioSpanner = t.cast(expressions.ArpeggioMarkSpanner, sb[0]) - else: + arpeggioSpanners = self.parent.arpeggioSpanners + offset = opFrac(self.parent.lastMeasureOffset + self.offsetMeasureNote) + numberAndOffset = (idFound, offset) + arpeggioSpanner = arpeggioSpanners.get(numberAndOffset) + if arpeggioSpanner is None: arpeggioSpanner = expressions.ArpeggioMarkSpanner(arpeggioType=arpeggioType) arpeggioSpanner.idLocal = idFound self.spannerBundle.append(arpeggioSpanner) + arpeggioSpanners[numberAndOffset] = arpeggioSpanner arpeggioSpanner.addSpannedElements(n) mostRecentOrnament: expressions.Ornament|None = None From db97d3caa4cc77a7a0e9fe485d4f4a3f550d5968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9-Paul=20Debroize?= Date: Wed, 30 Sep 2026 10:53:18 +0200 Subject: [PATCH 122/312] makeTies: fall back to the measure when the voice id is missing Look the voice up with voices.getElementById for string and int ids alike, so a string id absent from the next measure no longer raises KeyError. Fixes #2024. --- music21/stream/makeNotation.py | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/music21/stream/makeNotation.py b/music21/stream/makeNotation.py index c681463fbb..c1dc87d903 100644 --- a/music21/stream/makeNotation.py +++ b/music21/stream/makeNotation.py @@ -7,7 +7,7 @@ # Jacob Walls # Evan Lynch # -# Copyright: Copyright © 2008-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2008-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ----------------------------------------------------------------------------- from __future__ import annotations @@ -1320,10 +1320,7 @@ def makeTies( # manage bridging voices if mNextHasVoices: if mHasVoices: # try to match voice id - if not isinstance(vId, int): - dst = mNext.voices[vId] - else: - dst = mNext.getElementById(vId) + dst = mNext.voices.getElementById(vId) # src does not have voice, but dst does else: # place in top-most voice dst = mNext.voices[0] @@ -2347,6 +2344,33 @@ def testMakeTiesChangingTimeSignatures(self): self.assertEqual(len(pp[stream.Measure][2].notes), 1) self.assertEqual(pp[stream.Measure][2].notes.first().duration.quarterLength, 24.0) + def testMakeTiesVoiceIdMissingFromNextMeasure(self): + ''' + A note crossing the barline in a voice whose (string) id is not + in the next measure goes to the measure itself, as it already + does for an int id, instead of raising KeyError. + + AI-assisted (Claude). + ''' + from music21 import stream + p = stream.Part() + m1 = stream.Measure(number=1) + m1.insert(0, meter.TimeSignature('4/4')) + soprano = stream.Voice(id='soprano') + soprano.insert(3, note.Note('C4', quarterLength=2.0)) + m1.insert(0, soprano) + m2 = stream.Measure(number=2) + m2.insert(0, stream.Voice(id='alto')) + p.insert(0, m1) + p.insert(4, m2) + + p.makeTies(inPlace=True) + self.assertEqual(soprano.notes.first().duration.quarterLength, 1.0) + self.assertEqual(soprano.notes.first().tie.type, 'start') + remainder = m2.getElementsByClass(note.Note).first() + self.assertEqual(remainder.duration.quarterLength, 1.0) + self.assertEqual(remainder.tie.type, 'stop') + def testConsolidateCompletedTupletsNoFalsePositive(self): from fractions import Fraction from music21 import converter From a1d1f4a01506e316e3dcc2d723a931eeb7cb01c4 Mon Sep 17 00:00:00 2001 From: hill Date: Thu, 24 Sep 2026 11:48:07 +0200 Subject: [PATCH 123/312] Drop redundant deepcopies in AbstractScale.nextPitch and getRelativeNodeDegree IntervalNetwork.nextPitch builds its answer from getPitchFromNodeDegree, which already hands back a copy, and getRelativeNodeDegree answers an int. --- music21/scale/__init__.py | 4 ++-- music21/scale/test_intervalNetwork.py | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index d18dbdb118..1323db76b8 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -611,7 +611,7 @@ def getRelativeNodeDegree(self, direction=direction, alteredDegrees=self._alteredDegrees ) - return copy.deepcopy(post) + return post def nextPitch(self, pitchReference: _PitchOrStr, @@ -633,7 +633,7 @@ def nextPitch(self, alteredDegrees=self._alteredDegrees, getNeighbor=getNeighbor ) - return copy.deepcopy(post) + return post def getNewTonicPitch(self, pitchReference: _PitchOrStr, diff --git a/music21/scale/test_intervalNetwork.py b/music21/scale/test_intervalNetwork.py index 6e27435214..edf7501b22 100644 --- a/music21/scale/test_intervalNetwork.py +++ b/music21/scale/test_intervalNetwork.py @@ -5,7 +5,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2023 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ from __future__ import annotations @@ -649,6 +649,12 @@ def test_next_pitch_does_not_move_a_cached_degree(self): self.assertEqual(str(sc.nextPitch('c1', Direction.ASCENDING)), 'D1') self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') + def test_next_pitch_is_the_callers_own(self): + sc = scale.MajorScale('c4') + p = sc.nextPitch('e4') + p.octave = 1 + self.assertEqual(str(sc.nextPitch('e4')), 'F4') + # ------------------------------------------------------------------------------ if __name__ == '__main__': From ee6aadc258e493e1fd2030f781c70fa214745cad Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 30 Sep 2026 16:27:19 +0200 Subject: [PATCH 124/312] Update AGENTS.md with test-first approach Clarify the importance of writing regression tests before fixing bugs. --- AGENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 965909839d..0bf5d5e6f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,6 +68,9 @@ so an example built from input no one would ever write teaches nothing and puts an obscure bug on a billboard. The test: would a first-time reader of this object want this example? If no, it is a unittest. See the `writing-docs` skill. +- **Fix a bug test-first.** Write the regression test before changing the code, run it, + and see it fail for the reason the bug describes, not on an import error or a typo. + Then fix it and see it pass. A test that passed before the fix does not test the fix. - Never commit `forceSource=True` to a test or doctest (it reparses from source every run and slows the suite for everyone). The ONLY exception is the one test that exercises `forceSource` itself. If you hit a stale-parse problem while developing: From c02ed89263324fb328a27d9d40b80ec83a0fe8ee Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 30 Sep 2026 11:52:32 -1000 Subject: [PATCH 125/312] Add Chord.setInversion(); inversion() setting on its way out setInversion(newInversion, *, transpose=True, inPlace=False) takes over the setting half of inversion(). Unlike inversion(n), it returns a copy by default. inversion(n) still works in place for v11; deprecate in v12, remove in v13. inversion() overloads now reject keywords that the get or set form ignores. setInversion(None) clears a stored inversion. harmony's self.inversion(None, transposeOnSet=False) was meant to do this (since 2012) but None always fell through to the getter; it now calls setInversion(None, inPlace=True). Also: inversion() docs said max 5 / 0 for unusual / None for empty; the code has returned 6 / -1 / -1 since 2015 / 2022. AI-assisted (Claude) --- .../devTest_inversions.ipynb | 27 +-- music21/chord/__init__.py | 206 +++++++++++++----- music21/harmony.py | 10 +- music21/musicxml/m21ToXml.py | 2 +- music21/test/test_chord.py | 27 ++- 5 files changed, 185 insertions(+), 87 deletions(-) diff --git a/documentation/source/testsAndInProgress/devTest_inversions.ipynb b/documentation/source/testsAndInProgress/devTest_inversions.ipynb index f5aac02b06..9737e164f8 100644 --- a/documentation/source/testsAndInProgress/devTest_inversions.ipynb +++ b/documentation/source/testsAndInProgress/devTest_inversions.ipynb @@ -28,15 +28,6 @@ "%load_ext music21.ipython21" ] }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "import copy" - ] - }, { "cell_type": "code", "execution_count": 4, @@ -119,7 +110,7 @@ "metadata": {}, "outputs": [], "source": [ - "chord1.inversion(0)" + "chord1.setInversion(0, inPlace=True)" ] }, { @@ -178,9 +169,7 @@ "for i in range(20):\n", " inv = i % 3\n", " s.append(cMaj)\n", - " cMajCopy = copy.deepcopy(cMaj)\n", - " cMajCopy.inversion(inv)\n", - " cMaj = cMajCopy\n" + " cMaj = cMaj.setInversion(inv)\n" ] }, { @@ -255,9 +244,7 @@ "for i in range(7):\n", " inv = i % 3\n", " s.append(cMaj)\n", - " cMajCopy = copy.deepcopy(cMaj)\n", - " cMajCopy.inversion(inv)\n", - " cMaj = cMajCopy" + " cMaj = cMaj.setInversion(inv)" ] }, { @@ -296,9 +283,7 @@ "for i in range(17):\n", " inv = i % 6\n", " s.append(cMaj)\n", - " cMajCopy = copy.deepcopy(cMaj)\n", - " cMajCopy.inversion(inv)\n", - " cMaj = cMajCopy" + " cMaj = cMaj.setInversion(inv)" ] }, { @@ -385,9 +370,7 @@ "for i in range(12):\n", " inv = i % 3\n", " s.append(germ6)\n", - " germ6Copy = copy.deepcopy(germ6)\n", - " germ6Copy.inversion(inv)\n", - " germ6 = germ6Copy" + " germ6 = germ6.setInversion(inv)" ] }, { diff --git a/music21/chord/__init__.py b/music21/chord/__init__.py index 83977eae34..1349969f6f 100644 --- a/music21/chord/__init__.py +++ b/music21/chord/__init__.py @@ -5,7 +5,7 @@ # Authors: Michael Scott Asato Cuthbert # Christopher Ariza # -# Copyright: Copyright © 2009-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -2207,8 +2207,6 @@ def inversion( self, newInversion: int, *, - find: bool = True, - testRoot: pitch.Pitch|None = None, transposeOnSet: bool = True ) -> None: ... @@ -2220,7 +2218,6 @@ def inversion( *, find: bool = True, testRoot: pitch.Pitch|None = None, - transposeOnSet: bool = True ) -> int: ... @@ -2233,27 +2230,21 @@ def inversion( transposeOnSet: bool = True, ) -> int|None: ''' - Find the chord's inversion or (if called with a number) set the chord to - the new inversion. + Find the chord's inversion. - When called without a number argument, returns an integer (or None) - representing which inversion (if any) + Returns an integer representing which inversion (if any) the chord is in. The Chord does not have to be complete, in which case this function determines the inversion by looking at the relationship of the bass note to the root. - Returns a maximum value of 5 for the fifth inversion of a thirteenth chord. + Returns a maximum value of 6 for the sixth inversion of a thirteenth chord. Returns 0 if the bass to root interval is a unison - or if interval is not a common inversion (1st-5th). + and -1 if the interval is not a common inversion. The octave of the bass and root are irrelevant to this calculation of inversion. - Returns None if the Chord has no pitches. >>> g7 = chord.Chord(['g4', 'b4', 'd5', 'f5']) >>> g7.inversion() 0 - >>> g7.inversion(1) - >>> g7 - With implicit octaves, D becomes the bass (since octaves start on C): @@ -2261,14 +2252,6 @@ def inversion( >>> g7_implicit.inversion() 2 - Note that in inverting a chord with implicit octaves, some - pitches will gain octave designations, but not necessarily all of them - (this behavior might change in the future): - - >>> g7_implicit.inversion(1) - >>> g7_implicit - - Examples of each inversion: >>> cTriad1stInversion = chord.Chord(['E1', 'G1', 'C2']) @@ -2294,18 +2277,7 @@ def inversion( >>> bbEleventh5thInversion.inversion() 5 - Repeated notes do not affect the inversion: - - >>> gMajRepeats = chord.Chord(['G4', 'B5', 'G6', 'B6', 'D7']) - >>> gMajRepeats.inversion(2) - >>> gMajRepeats - - - >>> gMajRepeats.inversion(3) - Traceback (most recent call last): - music21.chord.ChordException: Could not invert chord: inversion may not exist - - If testRoot is True then that temporary root is used instead of self.root(). + If testRoot is given then that temporary root is used instead of self.root(). Get the inversion for a seventh chord showing different roots @@ -2314,9 +2286,6 @@ def inversion( 0 >>> dim7.inversion(testRoot=pitch.Pitch('D5')) 6 - >>> dim7.inversion('six-four') - Traceback (most recent call last): - music21.chord.ChordException: Inversion must be an integer, got: Chords without pitches or otherwise impossible chords return -1, indicating no normal inversion. @@ -2337,28 +2306,28 @@ def inversion( >>> chord.Chord('G4 C5').inversion() 2 - If transposeOnSet is False then setting the inversion simply - sets the value to be returned later, which might be useful for - cases where the chords are poorly spelled, or there is an added note. + Calling `inversion(newInversion)` with a number changes the chord in place + (`transposeOnSet` is `transpose` in :meth:`setInversion`) and returns None. + This form will be deprecated in v12 and removed in v13; use + :meth:`setInversion` instead. * Changed in v8: deal with chords without pitches. + * Changed in v11: setting the inversion moved to :meth:`setInversion`. ''' if not self.pitches: return -1 + if newInversion is not None: + # v12: deprecate newInversion and transposeOnSet; v13: remove them. + self.setInversion(newInversion, transpose=transposeOnSet, inPlace=True) + return None + if testRoot is not None: rootPitch = testRoot else: rootPitch = self.root() - if newInversion is not None: - try: - int_newInversion = int(newInversion) - except (ValueError, TypeError): - raise ChordException(f'Inversion must be an integer, got: {type(newInversion)}') - self._setInversion(int_newInversion, rootPitch, transposeOnSet) - return None - elif ('inversion' not in self._overrides and find) or testRoot is not None: + if ('inversion' not in self._overrides and find) or testRoot is not None: try: if rootPitch is None or self.bass() is None: return -1 @@ -2371,21 +2340,142 @@ def inversion( else: return -1 - def _setInversion( + @overload + def setInversion( self, - newInversion: int, - rootPitch: pitch.Pitch, - transposeOnSet: bool, + newInversion: int|None, + *, + transpose: bool = True, + inPlace: t.Literal[True], ) -> None: + ... + + @overload + def setInversion( + self, + newInversion: int|None, + *, + transpose: bool = True, + inPlace: t.Literal[False] = False, + ) -> t.Self: + ... + + def setInversion( + self, + newInversion: int|None, + *, + transpose: bool = True, + inPlace: bool = False, + ) -> t.Self|None: ''' - Helper function for inversion(int) + Return a new chord in the given inversion, moving the lowest + note up an octave until the chord reaches it. + + >>> g7 = chord.Chord(['G4', 'B4', 'D5', 'F5']) + >>> g7.setInversion(1) + + + With `inPlace=True` the chord itself changes and None is returned: + + >>> g7.setInversion(3, inPlace=True) + >>> g7 + + + Pitches with implicit octaves gain octave designations as they move, + but not necessarily all of them (this behavior might change in the future): + + >>> chord.Chord(['G', 'B', 'D', 'F']).setInversion(1) + + + Repeated notes do not affect the inversion: + + >>> gMajRepeats = chord.Chord(['G4', 'B5', 'G6', 'B6', 'D7']) + >>> gMajRepeats.setInversion(2) + + + An inversion the chord does not have raises a ChordException: + + >>> gMajRepeats.setInversion(3) + Traceback (most recent call last): + music21.chord.ChordException: Could not invert chord: inversion may not exist + + If `transpose` is False then the pitches do not move; the value is + simply stored to be returned by :meth:`inversion` later, which might be + useful for chords that are not spelled according to common-practice + function or have an added note. + + *Example:* This standard jazz/pop chord, C6, is considered a 1st inversion + by default for music21: + + >>> c6 = chord.Chord('C4 E4 G4 A4') + >>> c6.inversion() + 1 + + We can label it as a root position jazz chord instead, with + `transpose=False` and `inPlace=True`: + + >>> c6.setInversion(0, transpose=False, inPlace=True) + >>> c6.inversion() + 0 + + The bass has not changed: + + >>> c6.bass() + + + Setting the inversion to None removes a stored inversion, so + :meth:`inversion` again reads it from the pitches: + + >>> c6.setInversion(None, inPlace=True) + >>> c6.inversion() + 1 + + * New in v11. Was previously part of the functionality of :meth:`inversion`. + + AI-assisted (Claude) ''' - if transposeOnSet is False: - self._overrides['inversion'] = newInversion - return + int_newInversion: int|None = None + if newInversion is not None: + try: + int_newInversion = int(newInversion) + except (ValueError, TypeError): + raise ChordException(f'Inversion must be an integer, got: {type(newInversion)}') + + if inPlace: + returnObj = self + else: + returnObj = copy.deepcopy(self) + returnObj.derivation = derivation.Derivation(returnObj) + returnObj.derivation.origin = self + returnObj.derivation.method = 'setInversion' + + if int_newInversion is None: + returnObj._overrides.pop('inversion', None) + elif not transpose: + returnObj._overrides['inversion'] = int_newInversion + else: + returnObj._transposeToInversion(int_newInversion) + + if inPlace: + return None + return returnObj + + def _transposeToInversion(self, newInversion: int) -> None: + ''' + Helper function for setInversion that works in place on a chord to move + the bass and potentially other notes up octaves until the chord is in + `newInversion`. Raises a ChordException if it never gets there. + + >>> c = chord.Chord('C4 E4 G4') + >>> c._transposeToInversion(2) + >>> c + + ''' + if not self.pitches: + raise ChordException('Cannot invert a chord without pitches') + # could have set bass or root externally numberOfRunsBeforeCrashing = len(self.pitches) + 2 - soughtInversion = newInversion if 'inversion' in self._overrides: del self._overrides['inversion'] @@ -2393,7 +2483,7 @@ def _setInversion( # bass might have been overridden for a different octave del self._overrides['bass'] currentInversion = self.inversion(find=True) - while currentInversion != soughtInversion and numberOfRunsBeforeCrashing > 0: + while currentInversion != newInversion and numberOfRunsBeforeCrashing > 0: currentMaxMidi = max(self.pitches).ps tempBassPitch = self.bass() while tempBassPitch.ps < currentMaxMidi: diff --git a/music21/harmony.py b/music21/harmony.py index 8d3ac3d25c..bac5ce6489 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -171,7 +171,7 @@ class Harmony(chord.Chord): >>> h = harmony.ChordSymbol() >>> h.root('B-3') >>> h.bass('D', allow_add=True) - >>> h.inversion(1, transposeOnSet=False) + >>> h.setInversion(1, transpose=False, inPlace=True) >>> h.addChordStepModification(harmony.ChordStepModification('add', 4)) >>> h @@ -304,7 +304,7 @@ def _updateFromParameters( # set inversion first... if inversion is not None: - self.inversion(inversion, transposeOnSet=True) + self.setInversion(inversion, inPlace=True) # and then bass. if bass and isinstance(bass, str): @@ -1650,7 +1650,7 @@ class ChordSymbol(Harmony): important: we are not asking for transposition, merely specifying the inversion that the chord should be read in (transposeOnSet = False) - >>> cs.inversion(2, transposeOnSet=False) + >>> cs.setInversion(2, transpose=False, inPlace=True) >>> cs.romanNumeral = 'I64' >>> cs.chordKind = 'major' @@ -2203,7 +2203,7 @@ def _updatePitches(self): self._overrides['bass'].octave = 2 pitches.append(self._overrides['bass']) else: - self.inversion(None, transposeOnSet=False) + self.setInversion(None, inPlace=True) inversionNum = None pitches = self._adjustPitchesForChordStepModifications(pitches) @@ -2681,7 +2681,7 @@ def testChordKindSetting(self): cs = ChordSymbol() cs.root('E-') cs.bass('B-', allow_add=True) - cs.inversion(2, transposeOnSet=False) + cs.setInversion(2, transpose=False, inPlace=True) cs.romanNumeral = 'I64' cs.chordKind = 'major' cs.chordKindStr = 'M' diff --git a/music21/musicxml/m21ToXml.py b/music21/musicxml/m21ToXml.py index e54b522059..2b5a0885c9 100644 --- a/music21/musicxml/m21ToXml.py +++ b/music21/musicxml/m21ToXml.py @@ -5929,7 +5929,7 @@ def chordSymbolToXml( >>> cs = harmony.ChordSymbol() >>> cs.root('E-') >>> cs.bass('B-', allow_add=True) - >>> cs.inversion(2, transposeOnSet=False) + >>> cs.setInversion(2, transpose=False, inPlace=True) >>> cs.chordKind = 'major' >>> cs.chordKindStr = 'M' >>> cs diff --git a/music21/test/test_chord.py b/music21/test/test_chord.py index 25f0e94d0b..90fc63afbb 100644 --- a/music21/test/test_chord.py +++ b/music21/test/test_chord.py @@ -713,9 +713,34 @@ def testChordComponentsA(self): def testInvertingSimple(self): a = chord.Chord(['g4', 'b4', 'd5', 'f5']) self.assertEqual(a.inversion(), 0) - a.inversion(1) + a.setInversion(1, inPlace=True) self.assertEqual(repr(a), '') + def testSetInversionCopies(self): + a = chord.Chord('C4 E4 G4') + b = a.setInversion(2) + self.assertEqual(repr(a), '') + self.assertEqual(repr(b), '') + self.assertIs(b.derivation.origin, a) + with self.assertRaises(chord.ChordException): + chord.Chord().setInversion(1) + with self.assertRaises(chord.ChordException): + a.setInversion('six-four') + + a.setInversion(1, transpose=False, inPlace=True) + cleared = a.setInversion(None) + self.assertEqual(cleared.inversion(), 0) + self.assertEqual(a.inversion(), 1) + + def testInversionSetterLegacy(self): + # setting through inversion() works in place until its v13 removal + a = chord.Chord('C4 E4 G4') + self.assertIsNone(a.inversion(1)) + self.assertEqual(repr(a), '') + a.inversion(2, transposeOnSet=False) + self.assertEqual(a.inversion(), 2) + self.assertEqual(chord.Chord().inversion(1), -1) + def testDeepcopyChord(self): ch = Chord('C4 E4 G4') ch2 = copy.deepcopy(ch) From a0358751c2cae826b01408986a1cebeaf1105beb Mon Sep 17 00:00:00 2001 From: Benjamin Inglis Date: Thu, 1 Oct 2026 10:44:18 -0700 Subject: [PATCH 126/312] Include mode in pitchFromDegree cache key --- music21/scale/__init__.py | 10 ++++++++-- music21/scale/test_scale_main.py | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index d18dbdb118..cf50d02a63 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -118,13 +118,14 @@ type, # scale class str, # scale type str, # tonic name with octave + str, # stringified mode int, # degree Direction, # direction bool # equating termini ] # a dictionary mapping an abstract scale class, tonic.nameWithOctave, -# and degree to a pitchNameWithOctave. +# _abstract.mode, and degree to a pitchNameWithOctave. _pitchDegreeCache: dict[_PitchDegreeCacheKey, str] = {} # a pitch given in any of the forms that can be coerced to a pitch.Pitch @@ -299,6 +300,8 @@ def __init__(self, **keywords) -> None: # in most cases tonic/final of scale is step one, but not always self.tonicDegree: int = 1 # step of tonic + self.mode: t.Any = None + # declare if this scale is octave duplicating # can be used as to optimize pitch gathering self.octaveDuplicating = True @@ -750,7 +753,6 @@ class AbstractDiatonicScale(AbstractScale): ''' def __init__(self, mode: str|None = None, **keywords) -> None: super().__init__(**keywords) - self.mode = mode self.type = 'Abstract diatonic' # tonicDegree and dominantDegree are set below by buildNetwork self.dominantDegree: int = 5 # step of dominant @@ -861,6 +863,7 @@ def buildNetwork(self, mode: str|None = None) -> None: intervalList, octaveDuplicating=self.octaveDuplicating, pitchSimplification=None) + self.mode = mode class AbstractOctatonicScale(AbstractScale): @@ -900,6 +903,7 @@ def buildNetwork(self, mode: str|int|None = None) -> None: self._net = intervalNetwork.IntervalNetwork(intervalList, octaveDuplicating=self.octaveDuplicating, pitchSimplification='maxAccidental') + self.mode = mode # might also set weights for tonic and dominant here @@ -1785,10 +1789,12 @@ def pitchFromDegree( if (self.usePitchDegreeCache and self.tonic and not minPitch and not maxPitch and getattr(self, 'type', None)): tonicCacheKey = self.tonic.nameWithOctave + modeCacheKey = str(self._abstract.mode) cacheKey = ( self.__class__, self.type, tonicCacheKey, + modeCacheKey, degree, direction, equateTermini diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 95c5ecc03f..8c381814c4 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -247,6 +247,27 @@ def testOctaveRepeatingScaleWiderThanAnOctave(self): self.assertEqual(self.pitchOut(sc.pitches), '[C4, G4, D5, C6]') self.assertEqual(self.pitchOut(sc.getPitches('c3', 'c6')), '[D3, C4, G4, D5, C6]') + def testOctatonicModesDoNotSharePitchDegreeCache(self): + sc1 = scale.OctatonicScale('C', mode=1) + sc2 = scale.OctatonicScale('C', mode=2) + + self.assertEqual(str(sc1.pitchFromDegree(2)), 'D-4') + self.assertEqual(str(sc2.pitchFromDegree(2)), 'D4') + + def testDiatonicModesDoNotSharePitchDegreeCache(self): + sc1 = scale.DiatonicScale('C', mode='dorian') + sc2 = scale.DiatonicScale('C', mode='major') + + self.assertEqual(str(sc1.pitchFromDegree(3)), 'E-4') + self.assertEqual(str(sc2.pitchFromDegree(3)), 'E4') + + def testSwapAbstractScaleDoesNotReuseCachedPitch(self): + sc = scale.OctatonicScale('C', mode=2) + self.assertEqual(str(sc.pitchFromDegree(2)), 'D4') + + sc.abstract = scale.AbstractOctatonicScale(mode=1) + self.assertEqual(str(sc.pitchFromDegree(2)), 'D-4') + def testCyclicalScales(self): sc = scale.CyclicalScale('c4', ['m2', 'm2']) From 33eaae8e6c962758cd96342974b44a639f7a8665 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 1 Oct 2026 10:31:16 -1000 Subject: [PATCH 127/312] Update Agents Code Update Agents.md no need for "(AI)" in the title of PRs/Issues unless entirely AI written -- the assumption now is that most code is AI-assisted. We'll still put that in the PR body for now though. Add command that Agents should review PR bodies for concision and readibility. Add requirement that Opened PRs that have been reviewed should not be amended or force-pushed. Fix random mistake in docs for Harmony --- AGENTS.md | 14 ++++++-------- music21/harmony.py | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0bf5d5e6f9..9fb1bf74f8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,14 +118,11 @@ or PR that ignores the template may be closed without comment. - A PR from an account with no merged PR here is profiled automatically, and anything flagged is posted publicly as a comment on the PR. -- All PRs and Issues that use AI to be declared AI-assisted. Just write "AI-assisted (Claude)" with short name of Agent replacing "Claude". No robot emoji under any circumstance. -- 20 or more lines of code written by an agent needs to be declared as AI-assisted in the docstring. - Humans can remove and should remove this note when they do a review. -- If no code was written by a user and no language was provided for the issue and no reference - to specific code to change was given, any PR must declare "(Entirely AI written)" unless the user - is by a core dev. Failure to do so may result in new users being banned from the project. -- If an entirely AI written issue does not pass the tests it will be closed (or should be closed - by the agent or author). +- All PRs and Issues that use AI to be declared AI-assisted in their bodies. Just write "AI-assisted (Claude)" with short name of Agent replacing "Claude". No robot emoji under any circumstance. +- 20 or more lines of code written by an agent needs to be declared as AI-assisted in the docstring. Humans can remove and should remove this note when they do a review. +- If no code was written by a user (or reviewed and criticized by a human) and no specific language was provided for the issue, any PR must declare "Entirely AI written (Claude)" (or other agent name) in the body and "(AI-Written)" in the title, unless the user is a core dev. Failure to do so may result in new users being banned from the project. +- Agents should review PR bodies for concision and readability (and encourage human users also to review also). +- If an entirely AI written issue does not pass the tests it will be closed (or should be closed by at agent if not immediately fixed by the agent or author). - Agents must follow the [Code of Conduct](README.md#community-code-of-conduct). Agents that do not will be banned as well at their users. Not even the slightest bit of disrespect from an AI agent will be tolerated. - Any PR not from an established contributor touching more than about 20-30 lines should have an issue that has been opened and had enough @@ -143,6 +140,7 @@ after addressing the problem. (A blind close or close with "not accepted" etc. generally means that the issue/PR has too many problems to easily solve and has become a burden for the maintainer). - Do not include a "Tests run" section unless the testing procedure was unusual (like it affects part of the system without standard tests, like the testing system itself.) +- Once a PR has been opened and a review or comment has been made on it, commits should not be amended or force-pushed (this makes it impossible to review "changes since the review"). Put changes in response to reviews/comments in new commits. - While someone is reviewing a PR or a pushed branch, "do X" is not "commit and push X": make the change and leave it unstaged. When the list looks finished (or you hear "done!" or "push it"), offer to commit, or to commit and push. Batch a round's small fixes into diff --git a/music21/harmony.py b/music21/harmony.py index bac5ce6489..5bfab116ba 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -1150,7 +1150,7 @@ def chordSymbolFigureFromChord(inChord: chord.Chord, includeChordType=False): although certain subtractions are permitted for example a 9th chord will still be identified correctly even if it is missing the 5th 5. the type with the most identical matches is used, and if no type matches, - "Chord Type Cannot Be Identified" is returned + "Chord Symbol Cannot Be Identified" is returned 6. the output format for the chord symbol figure is the chord's root, the chord type's Abbreviation (saved in CHORD_TYPES dictionary), a '/' if the chord is in an inversion, and the chord's bass From d042989d09cb982f440e6e3f7645610798cb8f32 Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 23 Sep 2026 11:27:10 +0200 Subject: [PATCH 128/312] Key the realization cache on the altered degrees realizeAscending and realizeDescending cached a realization under the node, reference and range alone. A realization asked for with alteredDegrees and one asked for without them shared an entry, so whichever came first answered for both. nextPitch realizes without the alterations, and a harmonic minor scale lost its raised seventh after one call: >>> sc = scale.HarmonicMinorScale('g4') >>> sc.nextPitch('a4') >>> sc.pitches[6] The key now carries each alteration's degree, direction and interval. --- music21/scale/intervalNetwork.py | 19 +++++++++++++++++-- music21/scale/test_scale_main.py | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index ee2ee0ef3e..c72a5b7694 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -80,7 +80,8 @@ def __str__(self) -> str: type CacheKey = tuple[ - int|Terminus, str, str|None, str|None, bool, bool|None] + int|Terminus, str, str|None, str|None, bool, bool|None, + tuple[tuple[int, Direction, int, int|float], ...]] class AlteredDegree(t.TypedDict): ''' @@ -1421,6 +1422,7 @@ def _getCacheKey( *, includeFirst: bool, reverse: bool|None = None, # only meaningful for descending + alteredDegrees: AlteredDegrees|None = None, ) -> CacheKey: ''' Return key for caching based on critical components. @@ -1434,12 +1436,23 @@ def _getCacheKey( maxKey = maxPitch.nameWithOctave else: maxKey = None + + # an interval's generic number and semitones say what it is, and are + # cheaper to read than its name + alteredKey: tuple[tuple[int, Direction, int, int|float], ...] = () + if alteredDegrees: + alteredKey = tuple([(degree, + spec['direction'], + spec['interval'].generic.directed, + spec['interval'].semitones) + for degree, spec in alteredDegrees.items()]) return (nodeObj.id, pitchReference.nameWithOctave, minKey, maxKey, includeFirst, reverse, + alteredKey, ) def realizeAscending( @@ -1513,7 +1526,8 @@ def realizeAscending( pitchReference, minPitch, maxPitch, - includeFirst=False) + includeFirst=False, + alteredDegrees=alteredDegrees) if ck in self._ascendingCache: return self._ascendingCache[ck] else: @@ -1720,6 +1734,7 @@ def realizeDescending( maxPitch=maxPitchObj, includeFirst=includeFirst, reverse=reverse, + alteredDegrees=alteredDegrees, ) if ck in self._descendingCache: return self._descendingCache[ck] diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 8c381814c4..ce437de473 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -320,6 +320,11 @@ def testDeriveHarmonicMinor(self): sc = scale.HarmonicMinorScale() self.assertEqual(sc.derive(['C', 'D', 'E-', 'B']).name, 'C harmonic minor') + def testHarmonicMinorKeepsItsSeventhAfterNextPitch(self): + sc = scale.HarmonicMinorScale('g4') + sc.nextPitch('a4') + self.assertEqual(self.pitchOut(sc.pitches), '[G4, A4, B-4, C5, D5, E-5, F#5, G5]') + def testDeriveByDegree(self): sc1 = scale.MajorScale() self.assertEqual(str(sc1.deriveByDegree(7, 'G#')), From cd1ae0e21b8be0eedc301c6610869ba308f6f946 Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 23 Sep 2026 13:38:08 +0200 Subject: [PATCH 129/312] Find an instrument's names by its class in getAllNamesForInstrument getAllNamesForInstrument looked the instrument's display name up among the class names the language tables map to, so it only found names where the two are spelled alike -- Flute -- and none for an acoustic bass, whose class is AcousticBass, or for a violin renamed for its part: >>> instrument.getAllNamesForInstrument(instrument.AcousticBass(), 'english') {'english': []} It now uses the nearest class in the instrument's hierarchy that the tables name, which also covers a subclass of one of music21's classes. A generic Instrument, whose class the tables do not name, is still looked up by its name. --- music21/instrument.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/music21/instrument.py b/music21/instrument.py index 16becc2d64..50eaa8a3bb 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -9,7 +9,7 @@ # Ben Houge # Mark Gotham # -# Copyright: Copyright © 2009-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -2544,10 +2544,18 @@ def getAllNamesForInstrument(instrumentClass: Instrument, Note that the language string is not case-sensitive, so 'German' is also fine. ''' + from music21.languageExcerpts import instrumentLookup + language = language.lower() instrumentNameDict = {} - instrumentClassName = instrumentClass.instrumentName or '' + # the lookup tables name classes: the nearest one they know stands for + # this instrument, whatever it is called or subclassed as. A generic + # Instrument is found by its name. + namedClasses = set(instrumentLookup.allToClassName.values()) + instrumentClassName = next((cls.__name__ for cls in type(instrumentClass).__mro__ + if cls.__name__ in namedClasses), + instrumentClass.instrumentName or '') if language == SearchLanguage.ALL: for lang in SearchLanguage: @@ -2569,6 +2577,18 @@ class TestExternal(unittest.TestCase): class Test(unittest.TestCase): + def testGetAllNamesForInstrumentByClass(self): + acousticBass = getAllNamesForInstrument(AcousticBass(), SearchLanguage.ENGLISH) + self.assertEqual(acousticBass, {'english': ['acoustic bass']}) + + firstViolins = Violin() + firstViolins.instrumentName = 'Violin I' + self.assertEqual(getAllNamesForInstrument(firstViolins, SearchLanguage.ENGLISH), + {'english': ['violin', 'violins']}) + + self.assertEqual(getAllNamesForInstrument(Instrument('Violin'), SearchLanguage.ENGLISH), + {'english': ['violin', 'violins']}) + def testCopyAndDeepcopy(self): from music21.test.commonTest import testCopyAll testCopyAll(self, globals()) From d10b5befe507ab3574f7602164d85ca31eeeaeea Mon Sep 17 00:00:00 2001 From: hill Date: Fri, 2 Oct 2026 18:04:05 +0200 Subject: [PATCH 130/312] Drop the comment in getAllNamesForInstrument --- music21/instrument.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/music21/instrument.py b/music21/instrument.py index 50eaa8a3bb..11f0cb59bc 100644 --- a/music21/instrument.py +++ b/music21/instrument.py @@ -2549,9 +2549,6 @@ def getAllNamesForInstrument(instrumentClass: Instrument, language = language.lower() instrumentNameDict = {} - # the lookup tables name classes: the nearest one they know stands for - # this instrument, whatever it is called or subclassed as. A generic - # Instrument is found by its name. namedClasses = set(instrumentLookup.allToClassName.values()) instrumentClassName = next((cls.__name__ for cls in type(instrumentClass).__mro__ if cls.__name__ in namedClasses), From db3589846d9289d46a915597a2f1bfb4c35bc784 Mon Sep 17 00:00:00 2001 From: hill Date: Sun, 4 Oct 2026 15:06:18 +0200 Subject: [PATCH 131/312] Find the next pitch of a cyclical scale where it is nextPitch spelled the found node's degree from the scale's own tonic and moved it into the origin's octave. That holds when the network repeats at the octave, but in a cyclical one a degree stands for more than one pitch class: every note of a cycle of major thirds is the same node. So the walk began on the wrong pitch. >>> sc = scale.CyclicalScale('d3', ['M3']) >>> sc.nextPitch('g4') >>> sc.nextPitch('c4') For a network that is not octave-duplicating, take the node's pitch from a realization around the origin instead. Octave-duplicating networks are unchanged. --- music21/scale/intervalNetwork.py | 129 ++++++++++++++++++++++--------- music21/scale/test_scale_main.py | 14 ++++ 2 files changed, 108 insertions(+), 35 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index ee2ee0ef3e..aa6577fcdf 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -1338,46 +1338,56 @@ def nextPitch( # at this point a valid node id is required to continue foundNodeId = t.cast('Terminus|int', nodeId) - # realize the pitch from the found node degree - # we may be getting an altered - # tone, and we need to transpose an unaltered tone, thus - # leave out altered nodes argument - p = self.getPitchFromNodeDegree( - pitchReference=pitchReference, - nodeName=nodeName, - nodeDegreeTarget=self.nodes[foundNodeId].degree, - direction=direction, - minPitch=None, # not using a range here to - maxPitch=None, # get natural expansion - alteredDegrees=None # need unaltered tone here, thus omitted - ) - if p is None: - raise IntervalNetworkException( - 'Could not find a pitch for the requested node degree') - - # environLocal.printDebug(['nextPitch()', 'pitch obtained based on nodeName', - # nodeName, 'p', p, 'nodeId', nodeId, 'self.nodes[nodeId].degree', - # self.nodes[nodeId].degree]) - - # transfer octave from origin to new pitch derived from node - # note: this assumes octave equivalence and may be a problem - p.octave = pitchOriginObj.octave - - # correct for derived pitch crossing octave boundary - # https://github.com/cuthbertLab/music21/issues/319 alterSemitones: int|float = 0 degree = self.nodeIdToDegree(foundNodeId) if alteredDegrees and degree in alteredDegrees: alterSemitones = alteredDegrees[degree]['interval'].semitones alterSemitonesInt = t.cast('int', alterSemitones) - if not p.octaveIsImplicit: - if ((usedNeighbor and getNeighbor == Direction.DESCENDING) - or (not usedNeighbor and direction == Direction.ASCENDING)): - while p.transpose(alterSemitonesInt) > pitchOriginObj: - p.octave -= 1 - else: - while p.transpose(alterSemitonesInt) < pitchOriginObj: - p.octave += 1 + startBelowOrigin = ((usedNeighbor and getNeighbor == Direction.DESCENDING) + or (not usedNeighbor and direction == Direction.ASCENDING)) + p: pitch.Pitch|None + if not self.octaveDuplicating: + p = self._nodePitchNearOrigin(pitchReference, + nodeName, + foundNodeId, + pitchOriginObj, + direction=direction, + alteredDegrees=alteredDegrees, + atOrBelow=startBelowOrigin) + else: + # realize the pitch from the found node degree + # we may be getting an altered + # tone, and we need to transpose an unaltered tone, thus + # leave out altered nodes argument + p = self.getPitchFromNodeDegree( + pitchReference=pitchReference, + nodeName=nodeName, + nodeDegreeTarget=self.nodes[foundNodeId].degree, + direction=direction, + minPitch=None, # not using a range here to + maxPitch=None, # get natural expansion + alteredDegrees=None # need unaltered tone here, thus omitted + ) + if p is None: + raise IntervalNetworkException( + 'Could not find a pitch for the requested node degree') + + # environLocal.printDebug(['nextPitch()', 'pitch obtained based on nodeName', + # nodeName, 'p', p, 'nodeId', nodeId, 'self.nodes[nodeId].degree', + # self.nodes[nodeId].degree]) + + # transfer octave from origin to new pitch derived from node + p.octave = pitchOriginObj.octave + + # correct for derived pitch crossing octave boundary + # https://github.com/cuthbertLab/music21/issues/319 + if not p.octaveIsImplicit: + if startBelowOrigin: + while p.transpose(alterSemitonesInt) > pitchOriginObj: + p.octave -= 1 + else: + while p.transpose(alterSemitonesInt) < pitchOriginObj: + p.octave += 1 # pitchObj = p n = self.nodes[foundNodeId] @@ -1410,6 +1420,55 @@ def nextPitch( return pCollect + def _nodePitchNearOrigin( + self, + pitchReference: pitch.Pitch|str, + nodeName: Node|int|Terminus|None, + nodeId: Terminus|int, + pitchOrigin: pitch.Pitch, + *, + direction: Direction, + alteredDegrees: AlteredDegrees|None, + atOrBelow: bool, + ) -> pitch.Pitch: + ''' + Return the unaltered pitch of `nodeId` nearest `pitchOrigin`: the highest + at or below it if `atOrBelow`, otherwise the lowest at or above it. + + Read off a realization around the origin, since in a network that does + not repeat at the octave one node can stand for several pitch classes: + every note of a cycle of major thirds is the same node. + + AI-assisted (Claude). + ''' + origin = copy.deepcopy(pitchOrigin) + origin.octaveIsImplicit = False + realizedPitches, realizedNodes = self.realize( + pitchReference, + nodeName, + minPitch=origin.transpose(-12, inPlace=False), + maxPitch=origin.transpose(12, inPlace=False), + direction=direction, + alteredDegrees=alteredDegrees, + ) + nodePitches = [realizedPitch + for realizedPitch, realizedNode in zip(realizedPitches, realizedNodes) + if realizedNode == nodeId] + if atOrBelow: + found = max((p for p in nodePitches if p.ps <= origin.ps), + key=lambda p: p.ps, default=None) + else: + found = min((p for p in nodePitches if p.ps >= origin.ps), + key=lambda p: p.ps, default=None) + if found is None: + raise IntervalNetworkException( + 'Could not find a pitch for the requested node degree') + + degree = self.nodeIdToDegree(nodeId) + if alteredDegrees and degree in alteredDegrees: + return found.transpose(alteredDegrees[degree]['interval'].reverse()) + return copy.deepcopy(found) + # TODO: need to collect intervals as well def _getCacheKey( diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 8c381814c4..13307855f7 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -320,6 +320,20 @@ def testDeriveHarmonicMinor(self): sc = scale.HarmonicMinorScale() self.assertEqual(sc.derive(['C', 'D', 'E-', 'B']).name, 'C harmonic minor') + def testNextPitchOnCycle(self): + ''' + AI-assisted (Claude). + ''' + thirds = scale.CyclicalScale('d3', ['M3']) + self.assertEqual(str(thirds.nextPitch('c4')), 'D4') + self.assertEqual(str(thirds.nextPitch('g4')), 'A#4') + self.assertEqual(str(thirds.nextPitch('g4', Direction.DESCENDING)), 'F#4') + self.assertEqual(str(thirds.nextPitch('f#4')), 'A#4') + + fifths = scale.CyclicalScale('c4', ['P5']) + self.assertEqual(str(fifths.nextPitch('g4')), 'D5') + self.assertEqual(str(fifths.nextPitch('g4', Direction.DESCENDING)), 'C4') + def testDeriveByDegree(self): sc1 = scale.MajorScale() self.assertEqual(str(sc1.deriveByDegree(7, 'G#')), From 4de40a099fbde1edfdc70866e604fd2c76f92906 Mon Sep 17 00:00:00 2001 From: hill Date: Mon, 5 Oct 2026 11:11:45 +0200 Subject: [PATCH 132/312] Find the next pitch near the origin for every network nextPitch now takes the node's pitch from a realization around the origin for octave-duplicating networks too, which removes the second path. It also corrects enharmonics across an octave boundary there: MajorScale('c4').nextPitch('b#3') was D3 and is D4. Tests for a cycle of two intervals and for enharmonic origins, and a clearer docstring for _nodePitchNearOrigin. --- music21/scale/intervalNetwork.py | 61 ++++++-------------------------- music21/scale/test_scale_main.py | 16 +++++++++ 2 files changed, 26 insertions(+), 51 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index aa6577fcdf..0b9c17ea33 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -1338,56 +1338,15 @@ def nextPitch( # at this point a valid node id is required to continue foundNodeId = t.cast('Terminus|int', nodeId) - alterSemitones: int|float = 0 - degree = self.nodeIdToDegree(foundNodeId) - if alteredDegrees and degree in alteredDegrees: - alterSemitones = alteredDegrees[degree]['interval'].semitones - alterSemitonesInt = t.cast('int', alterSemitones) startBelowOrigin = ((usedNeighbor and getNeighbor == Direction.DESCENDING) or (not usedNeighbor and direction == Direction.ASCENDING)) - p: pitch.Pitch|None - if not self.octaveDuplicating: - p = self._nodePitchNearOrigin(pitchReference, - nodeName, - foundNodeId, - pitchOriginObj, - direction=direction, - alteredDegrees=alteredDegrees, - atOrBelow=startBelowOrigin) - else: - # realize the pitch from the found node degree - # we may be getting an altered - # tone, and we need to transpose an unaltered tone, thus - # leave out altered nodes argument - p = self.getPitchFromNodeDegree( - pitchReference=pitchReference, - nodeName=nodeName, - nodeDegreeTarget=self.nodes[foundNodeId].degree, - direction=direction, - minPitch=None, # not using a range here to - maxPitch=None, # get natural expansion - alteredDegrees=None # need unaltered tone here, thus omitted - ) - if p is None: - raise IntervalNetworkException( - 'Could not find a pitch for the requested node degree') - - # environLocal.printDebug(['nextPitch()', 'pitch obtained based on nodeName', - # nodeName, 'p', p, 'nodeId', nodeId, 'self.nodes[nodeId].degree', - # self.nodes[nodeId].degree]) - - # transfer octave from origin to new pitch derived from node - p.octave = pitchOriginObj.octave - - # correct for derived pitch crossing octave boundary - # https://github.com/cuthbertLab/music21/issues/319 - if not p.octaveIsImplicit: - if startBelowOrigin: - while p.transpose(alterSemitonesInt) > pitchOriginObj: - p.octave -= 1 - else: - while p.transpose(alterSemitonesInt) < pitchOriginObj: - p.octave += 1 + p = self._nodePitchNearOrigin(pitchReference, + nodeName, + foundNodeId, + pitchOriginObj, + direction=direction, + alteredDegrees=alteredDegrees, + atOrBelow=startBelowOrigin) # pitchObj = p n = self.nodes[foundNodeId] @@ -1435,9 +1394,9 @@ def _nodePitchNearOrigin( Return the unaltered pitch of `nodeId` nearest `pitchOrigin`: the highest at or below it if `atOrBelow`, otherwise the lowest at or above it. - Read off a realization around the origin, since in a network that does - not repeat at the octave one node can stand for several pitch classes: - every note of a cycle of major thirds is the same node. + Read off a realization around the origin, since one node can stand for + several pitch classes: a cycle of major thirds has a single edge, so + every pitch after its first is the high terminus. AI-assisted (Claude). ''' diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 13307855f7..6dc1159615 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -334,6 +334,22 @@ def testNextPitchOnCycle(self): self.assertEqual(str(fifths.nextPitch('g4')), 'D5') self.assertEqual(str(fifths.nextPitch('g4', Direction.DESCENDING)), 'C4') + thirdsAndMinorThirds = scale.CyclicalScale('d3', ['M3', 'm3']) + self.assertEqual(str(thirdsAndMinorThirds.nextPitch('d4')), 'E4') + self.assertEqual(str(thirdsAndMinorThirds.nextPitch('d4', Direction.DESCENDING)), 'C#4') + self.assertEqual(str(thirdsAndMinorThirds.nextPitch('a4')), 'B4') + self.assertEqual(str(thirdsAndMinorThirds.nextPitch('a4', Direction.DESCENDING)), 'G#4') + + def testNextPitchFromEnharmonic(self): + thirds = scale.CyclicalScale('d3', ['M3']) + for enharmonicOfD in ('c##4', 'e--4'): + self.assertEqual(str(thirds.nextPitch(enharmonicOfD)), 'F#4') + self.assertEqual(str(thirds.nextPitch(enharmonicOfD, Direction.DESCENDING)), 'B-3') + + sc = scale.MajorScale('c4') + self.assertEqual(str(sc.nextPitch('b#3')), 'D4') + self.assertEqual(str(sc.nextPitch('d-4', Direction.DESCENDING)), 'C4') + def testDeriveByDegree(self): sc1 = scale.MajorScale() self.assertEqual(str(sc1.deriveByDegree(7, 'G#')), From 06aa2fe15e4548d5cc2830f5bca3dd943b785ab0 Mon Sep 17 00:00:00 2001 From: hill Date: Mon, 5 Oct 2026 13:12:56 +0200 Subject: [PATCH 133/312] Keep each ascending walk and filter it to the range asked for realizeAscending cached a realization under its exact range, so every new origin given to nextPitch walked its two octaves again. The walk depends only on its node, its starting pitch and the altered degrees, so it is now kept under those and extended as far as a range needs, and each range is filtered from it. Networks pickled before the walks existed make theirs on first use. nextPitch on a fresh scale over 36 origins, against master: MajorScale 1045 -> 393 us, HarmonicMinorScale 1141 -> 419 us, CyclicalScale M3 558 -> 348 us. A single getPitches is unchanged. --- music21/scale/intervalNetwork.py | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 0b9c17ea33..9a46ed867b 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -36,6 +36,7 @@ from collections import OrderedDict from collections.abc import Sequence import copy +import dataclasses import enum import typing as t @@ -82,6 +83,20 @@ def __str__(self) -> str: type CacheKey = tuple[ int|Terminus, str, str|None, str|None, bool, bool|None] +@dataclasses.dataclass +class _Step: + pitchObj: pitch.Pitch + collected: pitch.Pitch + nodeId: Terminus|int + + +@dataclasses.dataclass +class _Walk: + steps: list[_Step] + node: Node + ended: bool = False + + class AlteredDegree(t.TypedDict): ''' The alteration spec for a single scale degree: a direction in which the @@ -485,6 +500,7 @@ def clear(self) -> None: self.nodes = OrderedDict() self._ascendingCache = OrderedDict() self._descendingCache = OrderedDict() + vars(self).pop('_ascendingWalks', None) def __eq__(self, other) -> bool: ''' @@ -1542,6 +1558,19 @@ def realizeAscending( if self.octaveDuplicating and minPitch is not None: pitchReference.transposeBelowTarget(minPitch, minimize=True, inPlace=True) + if ck is not None and minPitch is not None and maxPitch is not None: + walked = self._walkAscending(nodeObj, pitchReference, maxPitch, alteredDegrees) + if walked is not None: + post = [] + postNodeId = [] + for step in walked: + if (_gte(step.collected.ps, minPitch.ps) + and _lte(step.collected.ps, maxPitch.ps)): + post.append(step.collected) + postNodeId.append(step.nodeId) + self._ascendingCache[ck] = post, postNodeId + return post, postNodeId + # first, go upward from this pitch to the high terminus n = nodeObj p = pitchReference # we start with the pitch that is the reference @@ -1625,6 +1654,69 @@ def realizeAscending( return post, postNodeId + def _walkAscending( + self, + nodeObj: Node, + start: pitch.Pitch, + maxPitch: pitch.Pitch, + alteredDegrees: AlteredDegrees|None, + ) -> list[_Step]|None: + ''' + Return the pitches and node ids an ascending realization from `start` passes + through, up to and including the first at or above `maxPitch`, or None if + the walk reaches a node with more than one way up. + + The walk depends only on where it starts, so it is kept and extended rather + than repeated for every range starting there. + + AI-assisted (Claude). + ''' + key: tuple[t.Any, ...] = (nodeObj.id, start.nameWithOctave) + if alteredDegrees: + key += tuple((degree, + spec['direction'], + spec['interval'].generic.directed, + spec['interval'].semitones) + for degree, spec in alteredDegrees.items()) + # made on first use: networks pickled before it existed do not have it + walks: dict[tuple[t.Any, ...], _Walk] = vars(self).setdefault('_ascendingWalks', {}) + walk = walks.get(key) + if walk is None: + walk = _Walk([_Step(start, start, nodeObj.id)], nodeObj) + walks[key] = walk + while not walk.ended and not _gte(walk.steps[-1].pitchObj.ps, maxPitch.ps): + if len(walk.steps) >= 100: + break + n = walk.node + if n.id == Terminus.HIGH: + n = self.terminusLowNodes[0] + nextBundle = self.getNext(n, Direction.ASCENDING) + if nextBundle is None: + walk.ended = True + break + postEdge, postNode = nextBundle + if len(postEdge) > 1: + return None + walk.node = postNode[0] + p = self.transposePitchAndApplySimplification(postEdge[0].interval, + walk.steps[-1].pitchObj) + collected = self.processAlteredNodes(alteredDegrees=alteredDegrees, + n=walk.node, + p=p, + direction=Direction.ASCENDING) + walk.steps.append(_Step(p, collected, walk.node.id)) + + walked = [] + for step in walk.steps: + walked.append(step) + if _gte(step.pitchObj.ps, maxPitch.ps): + break + if len(walked) >= 100: + raise IntervalNetworkException( + 'Cannot realize these pitches; is your scale ' + + "well-formed? (especially check if you're giving notes without octaves)") + return walked + def realizeDescending( self, pitchReference: pitch.Pitch|str, From 6a9281f224680412a54e796bfb2ac1596337ea6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 03:33:02 +0000 Subject: [PATCH 134/312] build(deps): bump urllib3 from 2.7.0 to 2.8.0 Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.7.0 to 2.8.0. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/2.7.0...2.8.0) --- updated-dependencies: - dependency-name: urllib3 dependency-version: 2.8.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 2ab5d4b01f..06aea85555 100644 --- a/uv.lock +++ b/uv.lock @@ -3216,11 +3216,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, ] [[package]] From 35cae133bfa265f787896c54b0e2650776708a79 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 05:55:17 +0000 Subject: [PATCH 135/312] build(deps): bump tornado from 6.5.8 to 6.5.9 Bumps [tornado](https://github.com/tornadoweb/tornado) from 6.5.8 to 6.5.9. - [Changelog](https://github.com/tornadoweb/tornado/blob/master/docs/releases.rst) - [Commits](https://github.com/tornadoweb/tornado/compare/v6.5.8...v6.5.9) --- updated-dependencies: - dependency-name: tornado dependency-version: 6.5.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/uv.lock b/uv.lock index 06aea85555..5643ad6e07 100644 --- a/uv.lock +++ b/uv.lock @@ -3107,19 +3107,19 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.8" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/10/d3/343e5bb989d6515b1646cf3d40135d73f3d5e45339bded401b56cdac24dd/tornado-6.5.8.tar.gz", hash = "sha256:9452e1b208a8bd771e2cb1f2ff564985b9b214bdebbe622793e1799e0a6bd23f", size = 520493, upload-time = "2026-08-07T02:12:42.971Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/d5/007086fd8df5489338e204f65adce33fd4f21a4999dbb2b9cff2f897b5f4/tornado-6.5.8-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:cc6aa787d7cfab7c3d35189dc7a56fbd2399a569624c730c6b55b3d6531d0403", size = 449487, upload-time = "2026-08-07T02:12:28.682Z" }, - { url = "https://files.pythonhosted.org/packages/70/c8/5a24a99495903f594f6a199dd7beead1cbc0a13e2cb9102727bcaaf2a997/tornado-6.5.8-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9715b5eb79735b2bcd454ce216a9275b7c0470e64ea1bf5742f78b2f72b26eeb", size = 447649, upload-time = "2026-08-07T02:12:30.306Z" }, - { url = "https://files.pythonhosted.org/packages/6e/de/f2e733f386b85962d1b1dc82cd63d169b5b4580062b35397eac9244a41fe/tornado-6.5.8-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:547d63f450d570c14fe0e8db2cfb14c9bbd1c2503b4a6612586267955aa47b58", size = 450707, upload-time = "2026-08-07T02:12:31.95Z" }, - { url = "https://files.pythonhosted.org/packages/0b/94/20efeee9a01c141e9ac47c397f81679dfda24b32768fc4fff24e76d36c2c/tornado-6.5.8-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e2360a0ffbe145eca8af0b19cb7203d79b1a98dd4cccdd6b368f6f49c2e3808", size = 451677, upload-time = "2026-08-07T02:12:33.512Z" }, - { url = "https://files.pythonhosted.org/packages/42/ec/a96ccb8ccf0de2b7bc2c5fa1608a4803735018242e90c4882365a9fd418f/tornado-6.5.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5d242290bdf7ab3151bc1065fdd75c0dcc21cbc7b49f22a4c56329c2d6566d22", size = 451510, upload-time = "2026-08-07T02:12:35.346Z" }, - { url = "https://files.pythonhosted.org/packages/29/b5/93185859245ad3f00e62175f29607346788b696369347f0146e0421286bb/tornado-6.5.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7b94ff0e128fe0542f3bd331fb44d06260fc4ac16881545159f34ef08aad4195", size = 450917, upload-time = "2026-08-07T02:12:36.963Z" }, - { url = "https://files.pythonhosted.org/packages/97/cf/fe33cf062834487d34d1559746a4a12521033c22645b6d74d4bca702e018/tornado-6.5.8-cp39-abi3-win32.whl", hash = "sha256:67832909c4779c64942380cb5f044a5c6163d00831472d80e25e115de9917836", size = 451952, upload-time = "2026-08-07T02:12:38.512Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e1/468ad54333e92ccb62627e62cb88e5fc14a2171daa67ed47b1b8542d5b86/tornado-6.5.8-cp39-abi3-win_amd64.whl", hash = "sha256:11881db6b7c168494be2c2d12e65931451bdf7ee718535418ae1d8855dd5a0ee", size = 452391, upload-time = "2026-08-07T02:12:39.971Z" }, - { url = "https://files.pythonhosted.org/packages/ad/3e/cd5e4f06e34cde33b8ef66cf36aa2b5ad46354cc1af7d2136bbe365fee1d/tornado-6.5.8-cp39-abi3-win_arm64.whl", hash = "sha256:68a7468c7e289f8514d7d664101753903217eff1bb6822c6b5994a0b5f5bcb26", size = 451411, upload-time = "2026-08-07T02:12:41.469Z" }, +version = "6.5.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/15/ca7aaebb77f850493cba71ace508aeffb896d1ad8976417b48b79823dbd2/tornado-6.5.9.tar.gz", hash = "sha256:4d868544ebdf2fc155239a74397f65ebfea9b4d3635296c96b5dfb0701c354f5", size = 536145, upload-time = "2026-09-14T18:08:37.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/4a/4d3459fd2f360dd99233f69f22b36012a42856ace526da2c6da3cb0e6df2/tornado-6.5.9-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:dea3bee433b73d6312d993ce89ad5f2b8d3ed9fde56e79ba5342b9edb78d342a", size = 464279, upload-time = "2026-09-14T18:08:20.909Z" }, + { url = "https://files.pythonhosted.org/packages/26/4b/5af2f7dcd674e2f1cacef763456f79823404f50954789d6e1ec7a70cd58a/tornado-6.5.9-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:22bed49cdb55292d1d58f46008ba2cc3020e5b84995ef6ae749676c8b5309514", size = 462440, upload-time = "2026-09-14T18:08:22.711Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0f/38f8d16011de3b1ce2babb4c3bcefb126d2dc47d04878ae3ad5c39a00caa/tornado-6.5.9-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0cc0876a58eee9143476f24901b1b8eb4d52443283ade2b6bca650dd1da04f84", size = 465496, upload-time = "2026-09-14T18:08:24.407Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e9/3888f265d1e287fc63cd61a6696f96a650024e32f9e89c0b1817ac5c40f4/tornado-6.5.9-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc0d703cc7ec92b47ebfe236c97cd765c1f2e45744a2e156e6677e54c948274d", size = 466468, upload-time = "2026-09-14T18:08:25.928Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8b/d2bcf417981fa6f7698ab84a52394a76cf608e6704d266e2e0e29f1fd9d0/tornado-6.5.9-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:93f9ab0226ef625b94cacb7981a5af104f1735ea02f92282abe5c389109fb31d", size = 466303, upload-time = "2026-09-14T18:08:27.566Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/c5cfab11420c3f908238a6c861b8f3864fdbd2b2b87e30c9c16f36a11512/tornado-6.5.9-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5801bbf94dfde8d21087b6aa0b17f3a5dab6110dfe44e7e4492df5de393ac15b", size = 465706, upload-time = "2026-09-14T18:08:29.182Z" }, + { url = "https://files.pythonhosted.org/packages/ab/36/5a5da0aa15b7afdd46ece09c004a75af7d00f6a96b692bf696ff94bc2a55/tornado-6.5.9-cp39-abi3-win32.whl", hash = "sha256:dca9f377e80efe5dd4d52a2776b67492a81a39b2d931de169b6b36d0c1da444c", size = 466750, upload-time = "2026-09-14T18:08:30.836Z" }, + { url = "https://files.pythonhosted.org/packages/34/d7/2446cf3125372e2b4be6483b4b93f360dc50ce2493864755484b23bb50f2/tornado-6.5.9-cp39-abi3-win_amd64.whl", hash = "sha256:f810730ac71eadf009e6e5cb6d534d096887310f5649cff19d7e0d19c0bf4ee7", size = 467191, upload-time = "2026-09-14T18:08:32.521Z" }, + { url = "https://files.pythonhosted.org/packages/39/70/f1024364bf78fa921c63ef2883d36a3b3b705750c98ae174593afaab7a65/tornado-6.5.9-cp39-abi3-win_arm64.whl", hash = "sha256:b5f4d92798337260c6d9f9f0e400c7539dd5e55cf93479a3f8508c9aa09c2b3b", size = 466211, upload-time = "2026-09-14T18:08:35.18Z" }, ] [[package]] From 50bb2a56b0a61756dad98129a1110218b651078c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 05:55:20 +0000 Subject: [PATCH 136/312] build(deps): bump virtualenv from 21.3.0 to 21.7.13 Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.3.0 to 21.7.13. - [Release notes](https://github.com/pypa/virtualenv/releases) - [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst) - [Commits](https://github.com/pypa/virtualenv/compare/21.3.0...21.7.13) --- updated-dependencies: - dependency-name: virtualenv dependency-version: 21.7.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/uv.lock b/uv.lock index 06aea85555..d2ba52143a 100644 --- a/uv.lock +++ b/uv.lock @@ -2401,15 +2401,14 @@ wheels = [ [[package]] name = "python-discovery" -version = "1.2.2" +version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, - { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/ef/3bae0e537cfe91e8431efcba4434463d2c5a65f5a89edd47c6cf2f03c55f/python_discovery-1.2.2.tar.gz", hash = "sha256:876e9c57139eb757cb5878cbdd9ae5379e5d96266c99ef731119e04fffe533bb", size = 58872, upload-time = "2026-04-07T17:28:49.249Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/57/250bd238b966cece44328235eb85290045d059265fdaf7527a3a958123db/python_discovery-1.6.1.tar.gz", hash = "sha256:cf87d3627dfb4412437fdd5b13eae402607722998d21567993aedbc59b23c15e", size = 84338, upload-time = "2026-09-18T01:31:53.971Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d8/db/795879cc3ddfe338599bddea6388cc5100b088db0a4caf6e6c1af1c27e04/python_discovery-1.2.2-py3-none-any.whl", hash = "sha256:e1ae95d9af875e78f15e19aed0c6137ab1bb49c200f21f5061786490c9585c7a", size = 31894, upload-time = "2026-04-07T17:28:48.09Z" }, + { url = "https://files.pythonhosted.org/packages/16/7d/e9ffbadfbf89c93848412d04594135c4ae8c1d37d9e053b9c3ed718fabc4/python_discovery-1.6.1-py3-none-any.whl", hash = "sha256:d43fcdef879fe795352bd13ccf8d185ba5a9f86f36cfcd00529f596e737442b3", size = 38664, upload-time = "2026-09-18T01:31:52.448Z" }, ] [[package]] @@ -3263,7 +3262,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.3.0" +version = "21.7.13" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3271,9 +3270,9 @@ dependencies = [ { name = "platformdirs" }, { name = "python-discovery" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/8b/6331f7a7fe70131c301106ec1e7cf23e2501bf7d4ca3636805801ca191bb/virtualenv-21.3.0.tar.gz", hash = "sha256:733750db978ec95c2d8eb4feadaa57091002bce404cb39ba69899cf7bd28944e", size = 7614069, upload-time = "2026-04-27T17:05:58.927Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/50/c9b84eb106d0db420b9878dac0a386c5791726f127ce20b06897f6e3a1e9/virtualenv-21.7.13.tar.gz", hash = "sha256:0355558b6f33619aab31347e43643b0ebc97f61ea3acf617b2b69e1f8a843d11", size = 5360306, upload-time = "2026-09-18T04:35:49.354Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/eb/03bfb1299d4c4510329e470f13f9a4ce793df7fcb5a2fd3510f911066f61/virtualenv-21.3.0-py3-none-any.whl", hash = "sha256:4d28ee41f6d9ec8f1f00cd472b9ffbcedda1b3d3b9a575b5c94a2d004fd51bd7", size = 7594690, upload-time = "2026-04-27T17:05:55.468Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ce/e74453531b0c49a58d0e8a4f9bab4495705859fee4a4f7de27d58f4a791a/virtualenv-21.7.13-py3-none-any.whl", hash = "sha256:1bea5af7463f59c4719db48fe739579a2a4f569c96f26c086edda85c96da9f59", size = 5328680, upload-time = "2026-09-18T04:35:47.194Z" }, ] [[package]] From 7c05379b4c820be4b95acbc7bf894c7df5c4e3a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Oct 2026 05:58:58 +0000 Subject: [PATCH 137/312] build(deps): bump jupyterlab from 4.5.10 to 4.5.11 Bumps [jupyterlab](https://github.com/jupyterlab/jupyterlab) from 4.5.10 to 4.5.11. - [Release notes](https://github.com/jupyterlab/jupyterlab/releases) - [Changelog](https://github.com/jupyterlab/jupyterlab/blob/main/RELEASE.md) - [Commits](https://github.com/jupyterlab/jupyterlab/compare/@jupyterlab/lsp@4.5.10...@jupyterlab/lsp@4.5.11) --- updated-dependencies: - dependency-name: jupyterlab dependency-version: 4.5.11 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 06aea85555..1b16ff615a 100644 --- a/uv.lock +++ b/uv.lock @@ -1306,7 +1306,7 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.5.10" +version = "4.5.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, @@ -1323,9 +1323,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/24/621aa20ec0d2fe72f52095bda0fc1be7738ac21aabe4129ff623140d5cdf/jupyterlab-4.5.10.tar.gz", hash = "sha256:77e8d80b78be59b2eaba2154562e21caa6e79c2f1281d6f486584f7144ee2f47", size = 23998879, upload-time = "2026-07-21T12:43:27.324Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/c5/2bf995b906cd83581dbb3435ac1dc5d1cb1fab9de233735a99b3f5be8d51/jupyterlab-4.5.11.tar.gz", hash = "sha256:6534bb389d31f5ceb4357b15d2c002206063b51ef9c1911a079f537749215ddc", size = 24000794, upload-time = "2026-09-21T16:19:46.398Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/c9/940f95f17ee4e413ad252bf8d4f2ee9a341f18cfeda87775fef3d7847321/jupyterlab-4.5.10-py3-none-any.whl", hash = "sha256:5967ca61e692e67a2f30b5a2b901c941dc6ce56c0b0e357bc6d34fed5ec095f6", size = 12452502, upload-time = "2026-07-21T12:43:23.542Z" }, + { url = "https://files.pythonhosted.org/packages/0f/f2/a2f50a928fed0b8529dd80c4eb7d3aebe92f9658bfb3b63dce77fb5f1b95/jupyterlab-4.5.11-py3-none-any.whl", hash = "sha256:e23e3d76b92ad172b529fb6faf91e3edd264ffdff65b543949eb85daf7f8b113", size = 12455673, upload-time = "2026-09-21T16:19:41.937Z" }, ] [[package]] From d8600f45e169ef92a5ccbe35f9620cdb822b4485 Mon Sep 17 00:00:00 2001 From: hill Date: Wed, 7 Oct 2026 18:44:39 +0200 Subject: [PATCH 138/312] Tidy the ascending walk cache and test nextPitch near the origin Set up the walk cache in __init__ and clear(), type its key, and drop two deepcopies of pitches nextPitch already owns. --- music21/scale/intervalNetwork.py | 54 +++++++++++++++++++------------- music21/scale/test_scale_main.py | 15 +++++++++ 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 9a46ed867b..4740fd4927 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -83,8 +83,16 @@ def __str__(self) -> str: type CacheKey = tuple[ int|Terminus, str, str|None, str|None, bool, bool|None] +# node id, start pitch, and (degree, direction, generic, semitones) per altered degree +type WalkKey = tuple[ + Terminus|int, str, tuple[tuple[int, Direction, int, int|float], ...]] + + @dataclasses.dataclass class _Step: + ''' + One pitch of an ascending walk: as transposed, as altered, and its node id. + ''' pitchObj: pitch.Pitch collected: pitch.Pitch nodeId: Terminus|int @@ -92,6 +100,9 @@ class _Step: @dataclasses.dataclass class _Walk: + ''' + An ascending walk from one start, kept so later calls can extend it. + ''' steps: list[_Step] node: Node ended: bool = False @@ -489,6 +500,7 @@ def __init__(self, CacheKey, tuple[list[pitch.Pitch], list[Terminus|int]] ] = OrderedDict() + self._ascendingWalks: dict[WalkKey, _Walk] = {} def clear(self) -> None: ''' @@ -500,7 +512,7 @@ def clear(self) -> None: self.nodes = OrderedDict() self._ascendingCache = OrderedDict() self._descendingCache = OrderedDict() - vars(self).pop('_ascendingWalks', None) + self._ascendingWalks = {} def __eq__(self, other) -> bool: ''' @@ -1410,19 +1422,20 @@ def _nodePitchNearOrigin( Return the unaltered pitch of `nodeId` nearest `pitchOrigin`: the highest at or below it if `atOrBelow`, otherwise the lowest at or above it. - Read off a realization around the origin, since one node can stand for - several pitch classes: a cycle of major thirds has a single edge, so - every pitch after its first is the high terminus. + One node can stand for several pitch classes (a cycle of major thirds is + one edge, so D, F# and A# after the first D are all its high terminus), + so read the pitch off a realization around the origin. + + `pitchOrigin` is changed in place. AI-assisted (Claude). ''' - origin = copy.deepcopy(pitchOrigin) - origin.octaveIsImplicit = False + pitchOrigin.octaveIsImplicit = False realizedPitches, realizedNodes = self.realize( pitchReference, nodeName, - minPitch=origin.transpose(-12, inPlace=False), - maxPitch=origin.transpose(12, inPlace=False), + minPitch=pitchOrigin.transpose(-12, inPlace=False), + maxPitch=pitchOrigin.transpose(12, inPlace=False), direction=direction, alteredDegrees=alteredDegrees, ) @@ -1430,10 +1443,10 @@ def _nodePitchNearOrigin( for realizedPitch, realizedNode in zip(realizedPitches, realizedNodes) if realizedNode == nodeId] if atOrBelow: - found = max((p for p in nodePitches if p.ps <= origin.ps), + found = max((p for p in nodePitches if p.ps <= pitchOrigin.ps), key=lambda p: p.ps, default=None) else: - found = min((p for p in nodePitches if p.ps >= origin.ps), + found = min((p for p in nodePitches if p.ps >= pitchOrigin.ps), key=lambda p: p.ps, default=None) if found is None: raise IntervalNetworkException( @@ -1442,7 +1455,7 @@ def _nodePitchNearOrigin( degree = self.nodeIdToDegree(nodeId) if alteredDegrees and degree in alteredDegrees: return found.transpose(alteredDegrees[degree]['interval'].reverse()) - return copy.deepcopy(found) + return found # TODO: need to collect intervals as well @@ -1671,19 +1684,16 @@ def _walkAscending( AI-assisted (Claude). ''' - key: tuple[t.Any, ...] = (nodeObj.id, start.nameWithOctave) - if alteredDegrees: - key += tuple((degree, - spec['direction'], - spec['interval'].generic.directed, - spec['interval'].semitones) - for degree, spec in alteredDegrees.items()) - # made on first use: networks pickled before it existed do not have it - walks: dict[tuple[t.Any, ...], _Walk] = vars(self).setdefault('_ascendingWalks', {}) - walk = walks.get(key) + key: WalkKey = (nodeObj.id, start.nameWithOctave, tuple( + (degree, + spec['direction'], + spec['interval'].generic.directed, + spec['interval'].semitones) + for degree, spec in (alteredDegrees or {}).items())) + walk = self._ascendingWalks.get(key) if walk is None: walk = _Walk([_Step(start, start, nodeObj.id)], nodeObj) - walks[key] = walk + self._ascendingWalks[key] = walk while not walk.ended and not _gte(walk.steps[-1].pitchObj.ps, maxPitch.ps): if len(walk.steps) >= 100: break diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 6dc1159615..77725adbc4 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -350,6 +350,21 @@ def testNextPitchFromEnharmonic(self): self.assertEqual(str(sc.nextPitch('b#3')), 'D4') self.assertEqual(str(sc.nextPitch('d-4', Direction.DESCENDING)), 'C4') + def testNextPitchNodeNearOrigin(self): + thirds = scale.CyclicalScale('d3', ['M3']) + self.assertEqual(str(thirds.nextPitch('d4', stepSize=2)), 'A#4') + + fifths = scale.CyclicalScale('c4', ['P5']) + self.assertEqual(str(fifths.nextPitch('f4', Direction.DESCENDING)), 'C4') + + aMinor = scale.HarmonicMinorScale('a4') + self.assertEqual(str(aMinor.nextPitch('f##4')), 'G#4') + self.assertEqual(str(aMinor.nextPitch('a4', Direction.DESCENDING)), 'G#4') + + gMinor = scale.HarmonicMinorScale('g4') + gMinor.nextPitch('a4') + self.assertEqual(str(gMinor.pitches[6]), 'F#5') + def testDeriveByDegree(self): sc1 = scale.MajorScale() self.assertEqual(str(sc1.deriveByDegree(7, 'G#')), From 9827e96bd37069516be553e47434767fb7966c11 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 14:35:21 -1000 Subject: [PATCH 139/312] Read lyric print-object="no" as hidden xmlToLyric passed print-object through setStyleAttributes, whose yes/no-to-bool conversion does not invert, so print-object="no" became hideObjectOnPrint = False and "yes" became True. Use setPrintObject, as the exporter already does. Export was correct, so a hidden lyric did not survive a round trip until now. Bump to 11.0.0b11 since cached musicxml parses are wrong. Found while working on the Accidental-creation speedups for #2010. AI-assisted (Claude) --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/musicxml/test_xmlToM21.py | 19 +++++++++++++++++++ music21/musicxml/xmlToM21.py | 5 +++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index 133f0f6158..9d2d4e79d8 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b10' +__version__ = '11.0.0b11' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 7b0b88c80a..b049c44f76 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b10' +'11.0.0b11' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/musicxml/test_xmlToM21.py b/music21/musicxml/test_xmlToM21.py index 01f4ae671e..25af666c2f 100644 --- a/music21/musicxml/test_xmlToM21.py +++ b/music21/musicxml/test_xmlToM21.py @@ -1597,6 +1597,25 @@ def testCompositeLyrics(self): self.assertEqual(ly3.components[2].syllabic, 'end') self.assertEqual(len(s.lyrics(recurse=True)[1][0]), 4) + def testLyricPrintObject(self): + # print-object="no" was read as hideObjectOnPrint = False, and "yes" as True + from music21 import converter + from music21.musicxml import m21ToXml + + MP = MeasureParser() + hiddenLyric = MP.xmlToLyric(self.EL('la')) + self.assertIs(hiddenLyric.style.hideObjectOnPrint, True) + shownLyric = MP.xmlToLyric(self.EL('la')) + self.assertIs(shownLyric.style.hideObjectOnPrint, False) + + n = note.Note('D4') + n.lyric = 'la' + n.lyrics[0].style.hideObjectOnPrint = True + GEX = m21ToXml.GeneralObjectExporter() + xmlOut = GEX.parse(n).decode('utf-8') + roundTrip = converter.parse(xmlOut, format='musicxml') + self.assertIs(roundTrip.flatten().notes[0].lyrics[0].style.hideObjectOnPrint, True) + def testDirectionPosition(self): from music21 import converter from music21 import corpus diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 48a0815065..4898dc7202 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -5049,8 +5049,9 @@ def xmlToLyric(self, mxLyric, inputM21=None) -> note.Lyric|None: ly.identifier = identifier self.setStyleAttributes(mxLyric, ly, - ('justify', 'placement', 'print-object'), - ('justify', 'placement', 'hideObjectOnPrint')) + ('justify', 'placement'), + ('justify', 'placement')) + self.setPrintObject(mxLyric, ly) self.setColor(mxLyric, ly) self.setPosition(mxLyric, ly) From 3b0a3b2bdb8daeabf0f499752eab28d7301990c4 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 15:05:59 -1000 Subject: [PATCH 140/312] no need for repeating same attributes --- music21/musicxml/xmlToM21.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 4898dc7202..de35ee2e52 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -5048,9 +5048,7 @@ def xmlToLyric(self, mxLyric, inputM21=None) -> note.Lyric|None: if identifier is not None: ly.identifier = identifier - self.setStyleAttributes(mxLyric, ly, - ('justify', 'placement'), - ('justify', 'placement')) + self.setStyleAttributes(mxLyric, ly, ('justify', 'placement')) self.setPrintObject(mxLyric, ly) self.setColor(mxLyric, ly) self.setPosition(mxLyric, ly) From aef2ad26ef7ae1916faf26f7873de121fb077d43 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 15:25:24 -1000 Subject: [PATCH 141/312] Interval.transposePitch(inPlace=True): transpose the fundamental for semitone Intervals AI-assisted (Claude) --- music21/interval.py | 11 ++++++----- music21/test/test_interval.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/music21/interval.py b/music21/interval.py index 01cac6ca4f..dbd7299147 100644 --- a/music21/interval.py +++ b/music21/interval.py @@ -7,7 +7,7 @@ # Amy Hailes # Christopher Ariza # -# Copyright: Copyright © 2009-2024 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2009-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -3402,10 +3402,11 @@ def transposePitch(self, if self.implicitDiatonic: # this will not preserve diatonic relationships - pOut = self.chromatic.transposePitch( - p, - inPlace=inPlace, - ) + if inPlace: + self.chromatic.transposePitch(p, inPlace=True) + pOut = p + else: + pOut = self.chromatic.transposePitch(p) else: pOut = self._diatonicTransposePitch( p, diff --git a/music21/test/test_interval.py b/music21/test/test_interval.py index a747bb41a4..2c767dfc82 100644 --- a/music21/test/test_interval.py +++ b/music21/test/test_interval.py @@ -443,6 +443,18 @@ def testRepeatedTransposePitch(self): p2 = intv.transposePitch(p) self.assertEqual(p2.nameWithOctave, 'B-20') + def testTransposePitchInPlaceImplicitDiatonicFundamental(self): + ''' + transposePitch(inPlace=True) with an Interval made from a number of semitones + also transposes the pitch's fundamental. + ''' + intv = interval.Interval(2) + self.assertTrue(intv.implicitDiatonic) + p = pitch.Pitch('E5', fundamental=pitch.Pitch('C3')) + self.assertIsNone(intv.transposePitch(p, inPlace=True)) + self.assertEqual(p.nameWithOctave, 'F#5') + self.assertEqual(p.fundamental.nameWithOctave, 'D3') + def testIntervalWithOneNoteGiven(self): noteC = note.Note('C4') From 5c3a74b0f91d0650d1282f8420f8f366cdcdc704 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 15:25:30 -1000 Subject: [PATCH 142/312] Accidental deepcopy: give the copy its own Style and Editorial AI-assisted (Claude) --- music21/pitch.py | 4 ++++ music21/test/test_pitch.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/music21/pitch.py b/music21/pitch.py index 065fb82e49..577c81983e 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1045,6 +1045,10 @@ def __deepcopy__(self, memo: dict[int, t.Any]) -> Accidental: new = Accidental.__new__(Accidental) for s in self._getSlotsRecursive(): setattr(new, s, getattr(self, s)) + if self._style is not None: + new._style = copy.deepcopy(self._style, memo) + if self._editorial is not None: + new._editorial = copy.deepcopy(self._editorial, memo) return new else: # pragma: no cover return common.defaultDeepcopy(self, memo) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 3716d8e5b5..97ba03372e 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -17,6 +17,7 @@ from music21 import converter from music21 import corpus from music21 import defaults +from music21 import editorial from music21 import key from music21 import note from music21 import pitch @@ -148,6 +149,21 @@ def testAccidentalImport(self): self.assertEqual(pAltered.accidental.name, 'sharp') self.assertTrue(pAltered.accidental.displayStatus) + def testAccidentalDeepcopyDoesNotShareStyleOrEditorial(self): + ''' + A deepcopy of an Accidental gets its own Style and Editorial. + ''' + a = Accidental('sharp') + a.style.color = 'red' + a.editorial.comments.append(editorial.Comment('cautionary')) + b = copy.deepcopy(a) + self.assertEqual(b.style.color, 'red') + self.assertEqual(len(b.editorial.comments), 1) + b.style.color = 'blue' + b.editorial.comments.append(editorial.Comment('ficta')) + self.assertEqual(a.style.color, 'red') + self.assertEqual(len(a.editorial.comments), 1) + def testUpdateAccidentalDisplaySimple(self): ''' Test updating accidental display. From 3d4b6ca20daae86c4d33e42513bb4baa0ea9ebba Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 16:11:22 -1000 Subject: [PATCH 143/312] Remove defaults.pitchStep and defaults.pitchOctave A Pitch's default step is always C and its implicit octave always 4. AI-assisted (Claude) --- music21/defaults.py | 5 ----- music21/pitch.py | 16 +++++++--------- music21/test/test_pitch.py | 11 ----------- music21/voiceLeading.py | 2 +- 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/music21/defaults.py b/music21/defaults.py index 4e41c2ef6a..95162b86a3 100644 --- a/music21/defaults.py +++ b/music21/defaults.py @@ -14,12 +14,10 @@ from __future__ import annotations import unittest -import typing as t from music21 import _version # note: this module should not import any higher level modules -type StepName = t.Literal['C', 'D', 'E', 'F', 'G', 'A', 'B'] # restating so as not to import. # TODO: defaults should check the environment object to see @@ -41,9 +39,6 @@ # not allowing more can be construed as a feature. -pitchStep: StepName = 'C' -pitchOctave = 4 - partGroup = 'Part Group' partGroupAbbreviation = 'PG' diff --git a/music21/pitch.py b/music21/pitch.py index 065fb82e49..d4912f533b 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -29,7 +29,6 @@ from music21 import common from music21.common.objects import SlottedObjectMixin from music21.common.types import StepName -from music21 import defaults from music21 import environment from music21 import exceptions21 from music21 import interval @@ -1693,8 +1692,7 @@ class Pitch(prebase.ProtoM21Object): Yet an octave is often needed anyhow, to play the `Pitch` in MIDI or put it on a staff, so `.octave` is always an integer: the default octave, - 4 (`defaults.pitchOctave`), when none was given. `.octaveIsImplicit` - tells the two cases apart. + 4, when none was given. `.octaveIsImplicit` tells the two cases apart. >>> anyGSharp.octave 4 @@ -1920,8 +1918,8 @@ def __init__(self, # No need for super().__init__() on protoM21Object self._groups: base.Groups|None = None - # this should not be set, as will be updated when needed - self._step: StepName = defaults.pitchStep # this is only the pitch step + # this will be updated below when needed + self._step: StepName = 'C' # this does not include the accidental self._overridden_freq440: float|None = None @@ -1932,7 +1930,7 @@ def __init__(self, # 5% of pitch creation time; it'll be created in a sec anyhow self._microtone: Microtone|None = None - # None means implicit: .octave then reports defaults.pitchOctave + # None means implicit: .octave then reports the default octave of 4 self._octave: int|None = None # if True, accidental is not known; is determined algorithmically @@ -3168,7 +3166,7 @@ def octave(self) -> int: 5 Always an int: a Pitch created without an octave reports the default - octave, 4 (`defaults.pitchOctave`), and has `.octaveIsImplicit` True. + octave, 4, and has `.octaveIsImplicit` True. >>> g = pitch.Pitch('g') >>> g.octave @@ -3194,7 +3192,7 @@ def octave(self) -> int: * Changed in v11: always an int; `.octaveIsImplicit` says whether it was given. ''' if self._octave is None: - return defaults.pitchOctave + return 4 return self._octave @octave.setter @@ -3243,7 +3241,7 @@ def octaveIsImplicit(self) -> bool: def octaveIsImplicit(self, value: bool) -> None: if bool(value) == self.octaveIsImplicit: return - self._octave = None if value else defaults.pitchOctave + self._octave = None if value else 4 self.informClient() @property diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 3716d8e5b5..71726cabe5 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -16,7 +16,6 @@ from music21 import common from music21 import converter from music21 import corpus -from music21 import defaults from music21 import key from music21 import note from music21 import pitch @@ -74,16 +73,6 @@ def testOctaveIsImplicit(self): self.assertEqual(note.Note('B-').octave, 4) self.assertEqual(note.Note('B-3').octave, 3) - # the default is read live from defaults.pitchOctave - savedDefaultOctave = defaults.pitchOctave - try: - defaults.pitchOctave = 3 - self.assertEqual(Pitch('C').octave, 3) - self.assertEqual(Pitch('C').ps, 48.0) - self.assertEqual(Pitch('C5').octave, 5) - finally: - defaults.pitchOctave = savedDefaultOctave - # creation paths self.assertTrue(Pitch().octaveIsImplicit) self.assertTrue(Pitch(3).octaveIsImplicit) # pitch class diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py index 7a137cf302..9022376e88 100644 --- a/music21/voiceLeading.py +++ b/music21/voiceLeading.py @@ -2078,7 +2078,7 @@ class ThreeNoteLinearSegment(NNoteLinearSegment): >>> ex2 = voiceLeading.ThreeNoteLinearSegment('a', 'b', 'c') >>> ex2.n1 - >>> defaults.pitchOctave + >>> ex2.n1.octave 4 ''' _DOC_ORDER = ['couldBePassingTone', From a9c4c53933b24658c072deb579bff15ca89fef1e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 02:57:20 +0000 Subject: [PATCH 144/312] build(deps): bump notebook from 7.5.6 to 7.6.3 Bumps [notebook](https://github.com/jupyter/notebook) from 7.5.6 to 7.6.3. - [Release notes](https://github.com/jupyter/notebook/releases) - [Changelog](https://github.com/jupyter/notebook/blob/@jupyter-notebook/tree@7.6.3/CHANGELOG.md) - [Commits](https://github.com/jupyter/notebook/compare/@jupyter-notebook/tree@7.5.6...@jupyter-notebook/tree@7.6.3) --- updated-dependencies: - dependency-name: notebook dependency-version: 7.6.3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/uv.lock b/uv.lock index beb198af56..a50d86a647 100644 --- a/uv.lock +++ b/uv.lock @@ -1183,6 +1183,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, ] +[[package]] +name = "jupyter-builder" +version = "1.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/75/3e/56f593e6a664cd14d441724654ce8243f3cac7d3e45867cf084749c8bc75/jupyter_builder-1.2.3.tar.gz", hash = "sha256:01aba6794eb9b19e0e29ae21137ca60ba4135c70347d4b9f664a586822b8c809", size = 1024218, upload-time = "2026-09-04T18:54:09.411Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/aa/be79e87c50698673f196d0633fc1664607da2289f9688d1de7a1c9db9e71/jupyter_builder-1.2.3-py3-none-any.whl", hash = "sha256:c5ea5a7190c2a7b082494abade98eece1b2b5bd5dbd7d610606cbcd10a1d08b3", size = 947541, upload-time = "2026-09-04T18:54:07.644Z" }, +] + [[package]] name = "jupyter-client" version = "8.8.0" @@ -1306,26 +1319,26 @@ wheels = [ [[package]] name = "jupyterlab" -version = "4.5.11" +version = "4.6.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-lru" }, { name = "httpx" }, { name = "ipykernel" }, { name = "jinja2" }, + { name = "jupyter-builder" }, { name = "jupyter-core" }, { name = "jupyter-lsp" }, { name = "jupyter-server" }, { name = "jupyterlab-server" }, { name = "notebook-shim" }, { name = "packaging" }, - { name = "setuptools" }, { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/c5/2bf995b906cd83581dbb3435ac1dc5d1cb1fab9de233735a99b3f5be8d51/jupyterlab-4.5.11.tar.gz", hash = "sha256:6534bb389d31f5ceb4357b15d2c002206063b51ef9c1911a079f537749215ddc", size = 24000794, upload-time = "2026-09-21T16:19:46.398Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/8d/995cc142f6083346b35e7d3eadc5a6717ee89ce64ea53577010ac493bc3c/jupyterlab-4.6.4.tar.gz", hash = "sha256:404f49b081819378524886c9db66dba57a5565981eff885830df1baba3a17df5", size = 28335647, upload-time = "2026-09-21T15:25:18.779Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/f2/a2f50a928fed0b8529dd80c4eb7d3aebe92f9658bfb3b63dce77fb5f1b95/jupyterlab-4.5.11-py3-none-any.whl", hash = "sha256:e23e3d76b92ad172b529fb6faf91e3edd264ffdff65b543949eb85daf7f8b113", size = 12455673, upload-time = "2026-09-21T16:19:41.937Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e4/072bc0d3c6d45d414062a06af7ee81c02a2c12d769c358ec9919f9995ffd/jupyterlab-4.6.4-py3-none-any.whl", hash = "sha256:15b13f991d3985129c797eb84d9949eeb8b6615e14b444868e642411f2c418b2", size = 17171596, upload-time = "2026-09-21T15:25:14.123Z" }, ] [[package]] @@ -2026,18 +2039,19 @@ wheels = [ [[package]] name = "notebook" -version = "7.5.6" +version = "7.6.3" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "jupyter-builder" }, { name = "jupyter-server" }, { name = "jupyterlab" }, { name = "jupyterlab-server" }, { name = "notebook-shim" }, { name = "tornado" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2a/c2/cf59bd2e6f2c8b976b52477e3e53bf6f97bc714ed046a51821afb428eaee/notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1", size = 14170814, upload-time = "2026-04-30T11:46:26.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/ac/aedb759dc683dcd129f6c75271bab5eb23020b0f7969163c726343718e1e/notebook-7.6.3.tar.gz", hash = "sha256:e2c08e469c0ae20bb0b3214f0ab77e79653317a2f8e5b34c10361c66874a5b50", size = 5501618, upload-time = "2026-09-21T18:07:24.261Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/d6/1fd0646b9bbd9efbb0b8ae21b2325fbef515769a5621c03e31d8eb8da587/notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0", size = 14581730, upload-time = "2026-04-30T11:46:22.342Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f7/907b98438cf00bdc4e296570058f29a347214bb42338a48e3b7eae58ae2a/notebook-7.6.3-py3-none-any.whl", hash = "sha256:ad7e0eb765fba836cd4a2ab0c7a3a26cde1d91665fbf6f533b6ae7b2de6d88d2", size = 5548452, upload-time = "2026-09-21T18:07:21.563Z" }, ] [[package]] @@ -2919,15 +2933,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610, upload-time = "2026-01-14T06:27:35.218Z" }, ] -[[package]] -name = "setuptools" -version = "83.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/34/26/f5d29e25ffdb535afef2d35cdb55b325298f96debd670da4c325e08d70f4/setuptools-83.0.0.tar.gz", hash = "sha256:025bccbbf0fa05b6192bc64ae1e7b16e001fd6d6d4d5de03c97b1c1ade523bef", size = 1154254, upload-time = "2026-07-04T15:31:22.699Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, -] - [[package]] name = "shellingham" version = "1.5.4" From 6623cd71ff70402f6b5c8c6ee0ec03d121de0602 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 17:59:44 -1000 Subject: [PATCH 145/312] Rename some keys to be more specific --- music21/scale/intervalNetwork.py | 71 +++++++++++++++++--------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index a2b156d93b..c8044f83ae 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -85,27 +85,30 @@ def __str__(self) -> str: tuple[tuple[int, Direction, int, int|float], ...]] # node id, start pitch, and (degree, direction, generic, semitones) per altered degree -type WalkKey = tuple[ - Terminus|int, str, tuple[tuple[int, Direction, int, int|float], ...]] +type NetworkWalkKey = tuple[ + Terminus|int, + str, + tuple[tuple[int, Direction, int, int|float], ...], +] @dataclasses.dataclass -class _Step: +class NetworkWalkStep: ''' - One pitch of an ascending walk: as transposed, as altered, and its node id. + One pitch of an ascending walk: unaltered, as realized, and its node id. ''' - pitchObj: pitch.Pitch - collected: pitch.Pitch + unalteredPitch: pitch.Pitch + realizedPitch: pitch.Pitch nodeId: Terminus|int @dataclasses.dataclass -class _Walk: +class NetworkWalkPath: ''' An ascending walk from one start, kept so later calls can extend it. ''' - steps: list[_Step] - node: Node + steps: list[NetworkWalkStep] + lastWalkedNode: Node ended: bool = False @@ -501,7 +504,7 @@ def __init__(self, CacheKey, tuple[list[pitch.Pitch], list[Terminus|int]] ] = OrderedDict() - self._ascendingWalks: dict[WalkKey, _Walk] = {} + self._ascendingWalks: dict[NetworkWalkKey, NetworkWalkPath] = {} def clear(self) -> None: ''' @@ -1369,13 +1372,13 @@ def nextPitch( startBelowOrigin = ((usedNeighbor and getNeighbor == Direction.DESCENDING) or (not usedNeighbor and direction == Direction.ASCENDING)) - p = self._nodePitchNearOrigin(pitchReference, - nodeName, - foundNodeId, - pitchOriginObj, - direction=direction, - alteredDegrees=alteredDegrees, - atOrBelow=startBelowOrigin) + p = self._nodePitchNear(pitchReference, + nodeName, + foundNodeId, + pitchOriginObj, + direction=direction, + alteredDegrees=alteredDegrees, + atOrBelow=startBelowOrigin) # pitchObj = p n = self.nodes[foundNodeId] @@ -1408,7 +1411,7 @@ def nextPitch( return pCollect - def _nodePitchNearOrigin( + def _nodePitchNear( self, pitchReference: pitch.Pitch|str, nodeName: Node|int|Terminus|None, @@ -1591,9 +1594,9 @@ def realizeAscending( post = [] postNodeId = [] for step in walked: - if (_gte(step.collected.ps, minPitch.ps) - and _lte(step.collected.ps, maxPitch.ps)): - post.append(step.collected) + if (_gte(step.realizedPitch.ps, minPitch.ps) + and _lte(step.realizedPitch.ps, maxPitch.ps)): + post.append(step.realizedPitch) postNodeId.append(step.nodeId) self._ascendingCache[ck] = post, postNodeId return post, postNodeId @@ -1687,7 +1690,7 @@ def _walkAscending( start: pitch.Pitch, maxPitch: pitch.Pitch, alteredDegrees: AlteredDegrees|None, - ) -> list[_Step]|None: + ) -> list[NetworkWalkStep]|None: ''' Return the pitches and node ids an ascending realization from `start` passes through, up to and including the first at or above `maxPitch`, or None if @@ -1698,7 +1701,7 @@ def _walkAscending( AI-assisted (Claude). ''' - key: WalkKey = (nodeObj.id, start.nameWithOctave, tuple( + key: NetworkWalkKey = (nodeObj.id, start.nameWithOctave, tuple( (degree, spec['direction'], spec['interval'].generic.directed, @@ -1706,12 +1709,12 @@ def _walkAscending( for degree, spec in (alteredDegrees or {}).items())) walk = self._ascendingWalks.get(key) if walk is None: - walk = _Walk([_Step(start, start, nodeObj.id)], nodeObj) + walk = NetworkWalkPath([NetworkWalkStep(start, start, nodeObj.id)], nodeObj) self._ascendingWalks[key] = walk - while not walk.ended and not _gte(walk.steps[-1].pitchObj.ps, maxPitch.ps): + while not walk.ended and not _gte(walk.steps[-1].unalteredPitch.ps, maxPitch.ps): if len(walk.steps) >= 100: break - n = walk.node + n = walk.lastWalkedNode if n.id == Terminus.HIGH: n = self.terminusLowNodes[0] nextBundle = self.getNext(n, Direction.ASCENDING) @@ -1721,19 +1724,19 @@ def _walkAscending( postEdge, postNode = nextBundle if len(postEdge) > 1: return None - walk.node = postNode[0] + walk.lastWalkedNode = postNode[0] p = self.transposePitchAndApplySimplification(postEdge[0].interval, - walk.steps[-1].pitchObj) - collected = self.processAlteredNodes(alteredDegrees=alteredDegrees, - n=walk.node, - p=p, - direction=Direction.ASCENDING) - walk.steps.append(_Step(p, collected, walk.node.id)) + walk.steps[-1].unalteredPitch) + realizedPitch = self.processAlteredNodes(alteredDegrees=alteredDegrees, + n=walk.lastWalkedNode, + p=p, + direction=Direction.ASCENDING) + walk.steps.append(NetworkWalkStep(p, realizedPitch, walk.lastWalkedNode.id)) walked = [] for step in walk.steps: walked.append(step) - if _gte(step.pitchObj.ps, maxPitch.ps): + if _gte(step.unalteredPitch.ps, maxPitch.ps): break if len(walked) >= 100: raise IntervalNetworkException( From 5934543279167233bc400c874115ba25ee6785f8 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 18:05:40 -1000 Subject: [PATCH 146/312] IntervalNetwork.findMissing returns copies of cached pitches findMissing returned the Pitch objects from the network's realization cache, so changing a returned pitch (e.g. its octave) changed every later realization of that scale. It now returns copies, as getPitchFromNodeDegree and nextPitch already do. AI-assisted (Claude) --- music21/scale/intervalNetwork.py | 2 +- music21/scale/test_intervalNetwork.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index c72a5b7694..a415c864a0 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -2918,7 +2918,7 @@ def findMissing(self, break # environLocal.printDebug(['looking at:', target, p, 'match', match]) if not match: - post.append(target) + post.append(copy.deepcopy(target)) return post diff --git a/music21/scale/test_intervalNetwork.py b/music21/scale/test_intervalNetwork.py index edf7501b22..7a7acbe8fa 100644 --- a/music21/scale/test_intervalNetwork.py +++ b/music21/scale/test_intervalNetwork.py @@ -655,6 +655,14 @@ def test_next_pitch_is_the_callers_own(self): p.octave = 1 self.assertEqual(str(sc.nextPitch('e4')), 'F4') + def test_find_missing_is_the_callers_own(self): + net = IntervalNetwork(['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']) + missing = net.findMissing('c4', 1, ['d']) + self.assertEqual(self.pitchOut(missing), '[C4, E4, F4, G4, A4, B4, C5]') + missing[0].octave = 7 + self.assertEqual(self.pitchOut(net.realizePitch('c4')), + '[C4, D4, E4, F4, G4, A4, B4, C5]') + # ------------------------------------------------------------------------------ if __name__ == '__main__': From f93cf99cfbb83330635622c99a736d4287825861 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 18:10:12 -1000 Subject: [PATCH 147/312] Rename "caller's own" tests; writing-docs covers test names testNextPitchNotReusePitchInstances and testFindMissingNotReusePitchInstances replace test_next_pitch_is_the_callers_own and test_find_missing_is_the_callers_own. The writing-docs skill now applies to writing tests and says to name copy checks for new or unshared instances. AI-assisted (Claude) --- .agents/skills/writing-docs/SKILL.md | 5 ++++- music21/scale/test_intervalNetwork.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.agents/skills/writing-docs/SKILL.md b/.agents/skills/writing-docs/SKILL.md index 8950cd7ec8..eb928d550c 100644 --- a/.agents/skills/writing-docs/SKILL.md +++ b/.agents/skills/writing-docs/SKILL.md @@ -2,7 +2,7 @@ name: writing-docs description: >- House style for music21 prose: docstrings, comments, and `Changed in`/`New in` - markers. Read before writing any of them. Sets the length target, the voice, + markers. Read before writing any of them or writing tests. Sets the length target, the voice, and where a bug fix's test belongs. --- @@ -135,6 +135,9 @@ Naming the guarded bug **is** appropriate in a unittest; that is what the test is for. The rule against narrating old bugs applies to docstrings and to comments in shipping code, not to tests. +For copy and identity checks, say "is a new instance" or "does not share instances," not +"the caller's own": `testFindMissingNotReusePitchInstances`. + ## Writing and Comment style - When writing comments in code, assume a strong code reader — anything inferable from the code is noise (docs that paraphrase names of functions or variable names esp.); focus on high level issues and gotchas that might bite again if not documented. - Say how to use code, not prior bugs or how code used to work or what was removed. That's for commit messages. Don't document where code is called from except for "keep in sync" lines across Py/TS. diff --git a/music21/scale/test_intervalNetwork.py b/music21/scale/test_intervalNetwork.py index 7a7acbe8fa..9d751ae622 100644 --- a/music21/scale/test_intervalNetwork.py +++ b/music21/scale/test_intervalNetwork.py @@ -649,13 +649,13 @@ def test_next_pitch_does_not_move_a_cached_degree(self): self.assertEqual(str(sc.nextPitch('c1', Direction.ASCENDING)), 'D1') self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') - def test_next_pitch_is_the_callers_own(self): + def testNextPitchNotReusePitchInstances(self): sc = scale.MajorScale('c4') p = sc.nextPitch('e4') p.octave = 1 self.assertEqual(str(sc.nextPitch('e4')), 'F4') - def test_find_missing_is_the_callers_own(self): + def testFindMissingNotReusePitchInstances(self): net = IntervalNetwork(['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']) missing = net.findMissing('c4', 1, ['d']) self.assertEqual(self.pitchOut(missing), '[C4, E4, F4, G4, A4, B4, C5]') From f96576075630051ad17485ecb6d25bed684a48d6 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 18:21:39 -1000 Subject: [PATCH 148/312] Pitch.__init__: fast paths for 'C', 'C4', and step= Pitch('C') and Pitch('C4') skip name parsing, and a valid uppercase step= is assigned directly instead of through the step setter. Note() builds Pitch('C4'), so every default Note benefits. Pitch('C') 441 ns -> 140 ns Pitch('C4') 540 ns -> 145 ns Pitch(step='D') 268 ns -> 180 ns Pitch(step='C', octave=4) 288 ns -> 189 ns Note() 1642 ns -> 1183 ns AI-assisted (Claude) --- music21/pitch.py | 17 +++++++++++++---- music21/test/test_pitch.py | 14 +++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index 57b7053621..a644e758cd 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1919,7 +1919,7 @@ def __init__(self, ps: float|None = None, fundamental: Pitch|None = None, **keywords) -> None: - # No need for super().__init__() on protoM21Object + # No need for super().__init__() on ProtoM21Object self._groups: base.Groups|None = None # this will be updated below when needed @@ -1949,7 +1949,12 @@ def __init__(self, # name combines step, octave, and accidental if name is not None: - if isinstance(name, str): + if name == 'C': + pass # a new Pitch is already C + elif name == 'C4': + # no other change needed here. + self._octave = 4 + elif isinstance(name, str): self.name = name # set based on string else: # is a number # is a midiNumber or a ps -- a float midiNumber @@ -1958,8 +1963,12 @@ def __init__(self, self.spellingIsInferred = True if name >= 12: # is not a pitchClass self._octave = int(name / 12) - 1 - elif step is not None: - self.step = step + elif step is not None and step != 'C': + if step in STEPNAMES: + # set it directly. + self._step = step + else: + self.step = step if octave is not None: self._octave = octave diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 7da1f5ae80..97607e8ca7 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -23,7 +23,7 @@ from music21 import scale from music21 import stream from music21.musicxml import m21ToXml -from music21.pitch import Pitch, Accidental +from music21.pitch import Pitch, Accidental, PitchException class Test(unittest.TestCase): @@ -119,6 +119,18 @@ def testNameSetting(self): ): p.name = 32 + def testInitShortcutsMatchParsing(self): + # 'C', 'C4', and step= skip the name and step setters + self.assertEqual(Pitch('C'), Pitch('c')) + self.assertTrue(Pitch('C').octaveIsImplicit) + self.assertEqual(Pitch('C4'), Pitch('c4')) + self.assertFalse(Pitch('C4').octaveIsImplicit) + self.assertEqual(Pitch('C', step='D').step, 'C') + self.assertEqual(Pitch(step='D'), Pitch('d')) + self.assertEqual(Pitch(step='d').step, 'D') + with self.assertRaises(PitchException): + Pitch(step='CD') + def testAccidentalImport(self): From 1ff7e419211de3d325f4332f1e624c2db56cb94e Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 18:45:33 -1000 Subject: [PATCH 149/312] Accidental: one dict lookup in set(); leaner __init__ accidentalLookupTable maps every lowercase name, modifier, alternate name, and alter that Accidental accepts to (name, alter, modifier). set() does one lookup there instead of walking an elif chain of tuple membership tests, lowercasing only on a miss. __init__ does the lookup itself, calling set() only for values not in the table, and assigns _style and _editorial directly instead of calling StyleMixin.__init__. Accidental() 176 ns -> 86 ns Accidental('sharp') 193 ns -> 85 ns Accidental('double-flat') 318 ns -> 93 ns acc.set('flat') 173 ns -> 50 ns Pitch('F#4') 787 ns -> 678 ns AI-assisted (Claude) --- music21/pitch.py | 152 ++++++++++++++++++------------------- music21/test/test_pitch.py | 123 +++++++++++++++++++++++++++++- 2 files changed, 196 insertions(+), 79 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index a644e758cd..64ef5828e9 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -155,6 +155,45 @@ } +def _makeAccidentalLookupTable() -> dict[str|float, tuple[str, float, str]]: + ''' + Build accidentalLookupTable from accidentalNameToModifier and + alternateNameToAccidentalName. + ''' + # AI-assisted (Claude) + nameToAlter = { + 'natural': 0.0, + 'sharp': 1.0, + 'double-sharp': 2.0, + 'triple-sharp': 3.0, + 'quadruple-sharp': 4.0, + 'flat': -1.0, + 'double-flat': -2.0, + 'triple-flat': -3.0, + 'quadruple-flat': -4.0, + 'half-sharp': 0.5, + 'one-and-a-half-sharp': 1.5, + 'half-flat': -0.5, + 'one-and-a-half-flat': -1.5, + } + table: dict[str|float, tuple[str, float, str]] = {} + for name, alter in nameToAlter.items(): + modifier = accidentalNameToModifier[name] + table[name] = table[alter] = (name, alter, modifier) + # not natural's '': set('') is an error, and .modifier = '' leaves the name alone + if modifier: + table[modifier] = table[name] + for alternateName, name in alternateNameToAccidentalName.items(): + table[alternateName] = table[name] + return table + + +# every lowercase name, modifier, alternate name, and alter that Accidental() and +# Accidental.set() accept, mapped to (name, alter, modifier), e.g. "eses" or -2 to +# ("double-flat", -2.0, "--") +accidentalLookupTable = _makeAccidentalLookupTable() + + def isValidAccidentalName(name: str) -> bool: ''' Check if name is a valid accidental name string that can @@ -1000,8 +1039,13 @@ class Accidental(prebase.ProtoM21Object, style.StyleMixin): # INITIALIZER # - def __init__(self, specifier: int|str|float = 'natural') -> None: - super().__init__() + def __init__( # pylint: disable=super-init-not-called + self, + specifier: int|str|float = 'natural', + ) -> None: + # StyleMixin.__init__, inlined for speed; keep in sync + self._style = None + self._editorial = None # managed by properties self._displayType = 'normal' # normal, always, never, if-absolutely-necessary, @@ -1017,11 +1061,15 @@ def __init__(self, specifier: int|str|float = 'natural') -> None: # store a reference to the Pitch that has this Accidental object as a property self._client: Pitch|None = None - self._name = '' - self._modifier = '' - self._alter = 0.0 # semitones to alter step - # potentially can be a fraction, but not exponent - self.set(specifier) + try: + standard = accidentalLookupTable.get(specifier) + except TypeError: # unhashable + standard = None + if standard is None: + self.set(specifier) + else: + # alter: semitones to alter step; potentially a fraction, but not exponent + self._name, self._alter, self._modifier = standard # SPECIAL METHODS # def _hashValues(self) -> tuple[t.Any, ...]: @@ -1227,81 +1275,29 @@ def set(self, name: str|int|float, *, allowNonStandardValue: bool = False) -> No This is the argument that .name and .alter use to allow non-standard names + `Accidental()` calls `set()` only for values not in `accidentalLookupTable`, + so a subclass overriding `set()` should override `__init__` too. + * Changed in v5: added allowNonStandardValue. ''' - if isinstance(name, str): + try: + standard = accidentalLookupTable.get(name) + except TypeError: # unhashable + standard = None + if standard is None and isinstance(name, str): name = name.lower() # sometimes args get capitalized - if name in ('natural', 'n', 0): - self._name = 'natural' - self._alter = 0.0 - elif name in ('sharp', '#', 'is', 1): - # accidentalNameToModifier['sharp'] will always be #! - self._name = 'sharp' - self._alter = 1.0 - elif name in ('double-sharp', accidentalNameToModifier['double-sharp'], - 'isis', 2): - self._name = 'double-sharp' - self._alter = 2.0 - elif name in ('flat', accidentalNameToModifier['flat'], 'es', 'b', -1): - self._name = 'flat' - self._alter = -1.0 - elif name in ('double-flat', accidentalNameToModifier['double-flat'], - 'eses', -2): - self._name = 'double-flat' - self._alter = -2.0 - - elif name in ('half-sharp', accidentalNameToModifier['half-sharp'], - 'quarter-sharp', 'ih', 'semisharp', 0.5): - self._name = 'half-sharp' - self._alter = 0.5 - elif name in ('one-and-a-half-sharp', - accidentalNameToModifier['one-and-a-half-sharp'], - 'three-quarter-sharp', 'three-quarters-sharp', 'isih', - 'sesquisharp', 1.5): - self._name = 'one-and-a-half-sharp' - self._alter = 1.5 - elif name in ('half-flat', accidentalNameToModifier['half-flat'], - 'quarter-flat', 'eh', 'semiflat', -0.5): - self._name = 'half-flat' - self._alter = -0.5 - elif name in ('one-and-a-half-flat', - accidentalNameToModifier['one-and-a-half-flat'], - 'three-quarter-flat', 'three-quarters-flat', 'eseh', - 'sesquiflat', -1.5): - self._name = 'one-and-a-half-flat' - self._alter = -1.5 - elif name in ('triple-sharp', accidentalNameToModifier['triple-sharp'], - 'isisis', 3): - self._name = 'triple-sharp' - self._alter = 3.0 - elif name in ('quadruple-sharp', - accidentalNameToModifier['quadruple-sharp'], 'isisisis', 4): - self._name = 'quadruple-sharp' - self._alter = 4.0 - elif name in ('triple-flat', accidentalNameToModifier['triple-flat'], - 'eseses', -3): - self._name = 'triple-flat' - self._alter = -3.0 - elif name in ('quadruple-flat', - accidentalNameToModifier['quadruple-flat'], 'eseseses', -4): - self._name = 'quadruple-flat' - self._alter = -4.0 + standard = accidentalLookupTable.get(name) + + if standard is not None: + self._name, self._alter, self._modifier = standard + if self._client: + self._client.informClient() + elif allowNonStandardValue and isinstance(name, str): + self._name = name + elif allowNonStandardValue and isinstance(name, (int, float)): + self._alter = name else: - if not allowNonStandardValue: - raise AccidentalException(f'{name} is not a supported accidental type') - - if isinstance(name, str): - self._name = name - return - elif isinstance(name, (int, float)): - self._alter = name - return - else: # pragma: no cover - raise AccidentalException(f'{name} is not a supported accidental type') - - self._modifier = accidentalNameToModifier[self._name] - if self._client: - self._client.informClient() + raise AccidentalException(f'{name} is not a supported accidental type') def isTwelveTone(self) -> bool: diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 97607e8ca7..35875404a9 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -11,7 +11,9 @@ from __future__ import annotations import copy +from fractions import Fraction import unittest +from unittest import mock from music21 import common from music21 import converter @@ -23,7 +25,7 @@ from music21 import scale from music21 import stream from music21.musicxml import m21ToXml -from music21.pitch import Pitch, Accidental, PitchException +from music21.pitch import Pitch, Accidental, AccidentalException, PitchException class Test(unittest.TestCase): @@ -165,6 +167,125 @@ def testAccidentalDeepcopyDoesNotShareStyleOrEditorial(self): self.assertEqual(a.style.color, 'red') self.assertEqual(len(a.editorial.comments), 1) + def testAccidentalLookupTable(self): + ''' + Every way of writing each standard accidental gives the same accidental + from Accidental(), set(), and set(allowNonStandardValue=True), + and set() tells the client once. AI-assisted (Claude). + ''' + import numpy + valuesByResult = { + ('natural', 0.0, ''): ['natural', 'n', 0, 0.0, False, 'Natural', 'N'], + ('sharp', 1.0, '#'): ['sharp', '#', 'is', 1, 1.0, True, 'Sharp', 'IS'], + ('double-sharp', 2.0, '##'): [ + 'double-sharp', '##', 'isis', 2, 2.0, 'Double-Sharp', numpy.int64(2)], + ('triple-sharp', 3.0, '###'): ['triple-sharp', '###', 'isisis', 3, 3.0], + ('quadruple-sharp', 4.0, '####'): ['quadruple-sharp', '####', 'isisisis', 4, 4.0], + ('flat', -1.0, '-'): [ + 'flat', '-', 'es', 'b', -1, -1.0, 'FLAT', 'B', numpy.float64(-1.0)], + ('double-flat', -2.0, '--'): ['double-flat', '--', 'eses', -2, -2.0, 'ESES'], + ('triple-flat', -3.0, '---'): ['triple-flat', '---', 'eseses', -3, -3.0], + ('quadruple-flat', -4.0, '----'): ['quadruple-flat', '----', 'eseseses', -4, -4.0], + ('half-sharp', 0.5, '~'): [ + 'half-sharp', '~', 'quarter-sharp', 'ih', 'semisharp', 0.5, Fraction(1, 2)], + ('one-and-a-half-sharp', 1.5, '#~'): [ + 'one-and-a-half-sharp', '#~', 'three-quarter-sharp', 'three-quarters-sharp', + 'isih', 'sesquisharp', 1.5], + ('half-flat', -0.5, '`'): [ + 'half-flat', '`', 'quarter-flat', 'eh', 'semiflat', -0.5], + ('one-and-a-half-flat', -1.5, '-`'): [ + 'one-and-a-half-flat', '-`', 'three-quarter-flat', 'three-quarters-flat', + 'eseh', 'sesquiflat', -1.5, Fraction(-3, 2)], + } + listed = set() + for values in valuesByResult.values(): + listed.update(values) + # every name, every alternate name, and every modifier except natural's '' + self.assertLessEqual(set(pitch.accidentalNameToModifier), listed) + self.assertLessEqual(set(pitch.alternateNameToAccidentalName), listed) + self.assertLessEqual(set(pitch.accidentalNameToModifier.values()) - {''}, listed) + + for (name, alter, modifier), values in valuesByResult.items(): + for value in values: + for allowNonStandardValue in (None, False, True): + with self.subTest(value=value, allowNonStandardValue=allowNonStandardValue): + if allowNonStandardValue is None: + acc = Accidental(value) + else: + acc = Accidental('sharp') + acc.setAttributeIndependently('name', 'unset') + acc.setAttributeIndependently('alter', 99) + acc.setAttributeIndependently('modifier', '?') + acc._client = mock.Mock() + acc.set(value, allowNonStandardValue=allowNonStandardValue) + self.assertEqual(acc._client.informClient.call_count, 1) + self.assertEqual(acc.name, name) + self.assertIs(type(acc.alter), float) + self.assertEqual(acc.alter, alter) + self.assertEqual(acc.modifier, modifier) + + def testUnsupportedAccidentalValues(self): + ''' + Anything else raises, naming the value (lowercased). + ''' + # '' is natural's modifier, but set() does not take it + for value, shown in [ + ('flat-flat-up', 'flat-flat-up'), + ('FLAT-FLAT-UP', 'flat-flat-up'), + ('Dièse', 'dièse'), + ('', ''), + (5, '5'), + (0.25, '0.25'), + (None, 'None'), + (['flat'], "['flat']"), + ]: + with self.subTest(value=value): + message = f'{shown} is not a supported accidental type' + with self.assertRaises(AccidentalException) as cm: + Accidental(value) + self.assertEqual(str(cm.exception), message) + + acc = Accidental('sharp') + acc._client = mock.Mock() + with self.assertRaises(AccidentalException) as cm: + acc.set(value) + self.assertEqual(str(cm.exception), message) + self.assertEqual((acc.name, acc.alter, acc.modifier), ('sharp', 1.0, '#')) + self.assertEqual(acc._client.informClient.call_count, 0) + + def testAccidentalSetNonStandardValue(self): + ''' + A nonstandard string changes only the name (lowercased); a nonstandard + number changes only the alter. Neither tells the client. + ''' + for value, name, alter in [ + ('quintuple-sharp', 'quintuple-sharp', 1.0), + ('FLAT-FLAT-UP', 'flat-flat-up', 1.0), + ('', '', 1.0), + (5, 'sharp', 5), + (0.25, 'sharp', 0.25), + ]: + with self.subTest(value=value): + acc = Accidental('sharp') + acc._client = mock.Mock() + acc.set(value, allowNonStandardValue=True) + self.assertEqual(acc.name, name) + self.assertEqual(acc.alter, alter) + self.assertEqual(acc.modifier, '#') + self.assertEqual(acc._client.informClient.call_count, 0) + + def testAccidentalInitDefaults(self): + for acc in (Accidental(), Accidental('Flat')): + self.assertEqual(acc.displayType, 'normal') + self.assertIsNone(acc.displayStatus) + self.assertEqual(acc.displayStyle, 'normal') + self.assertEqual(acc.displaySize, 'full') + self.assertEqual(acc.displayLocation, 'normal') + self.assertIsNone(acc._client) + self.assertFalse(acc.hasStyleInformation) + self.assertIsNone(acc._editorial) + self.assertEqual(repr(Accidental()), '') + def testUpdateAccidentalDisplaySimple(self): ''' Test updating accidental display. From 572d2eea35cd5dee2e01d3bde3228c2ab1fcfcc0 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 19:01:38 -1000 Subject: [PATCH 150/312] Accidental.__init__: set name, modifier, alter before calling set() When the lookup misses, __init__ falls back to set(), as master always did. Master defined _name, _modifier, and _alter first, so a subclass's set() could rely on them (for instance, one that sets only the name with allowNonStandardValue=True). Restore that on the fallback path; the fast path is unchanged. AI-assisted (Claude) --- music21/pitch.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/music21/pitch.py b/music21/pitch.py index 64ef5828e9..fc13f10267 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1066,6 +1066,9 @@ def __init__( # pylint: disable=super-init-not-called except TypeError: # unhashable standard = None if standard is None: + self._name = '' + self._modifier = '' + self._alter = 0.0 self.set(specifier) else: # alter: semitones to alter step; potentially a fraction, but not exponent From 0c336eefc22c7ace10a8d83bece4686983506b0d Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 19:11:43 -1000 Subject: [PATCH 151/312] Faster whole-number ps and MIDI pitches pitchClassToDefaultStepAlter gives the step and alter for each pitch class. _convertPsToStep, the ps setter, and Pitch(number) use it for whole numbers. The ps setter no longer builds a natural Accidental only to discard it, or a Microtone(0) (_microtone stays None, which every reader treats the same), and it assigns step, accidental, and octave directly, informing the client once instead of three times. Results are unchanged: p.midi = 60 still gives no accidental, and Pitch(60) still gives a natural. p.midi = 60 745 ns -> 165 ns p.midi = 61 836 ns -> 366 ns p.ps = 61.0 952 ns -> 345 ns Pitch(60) 658 ns -> 406 ns Pitch(midi=60) 906 ns -> 324 ns Note(60) 1779 ns -> 1482 ns Whole-file MIDI imports do not measurably change: pitches are a tiny part. AI-assisted (Claude) --- music21/pitch.py | 56 ++++++++++++++++++++++++++------------ music21/test/test_pitch.py | 31 +++++++++++++++++++++ 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index a644e758cd..f88abf957a 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -63,6 +63,20 @@ 9: 'A', 11: 'B', } +pitchClassToDefaultStepAlter: tuple[tuple[StepName, int], ...] = ( + ('C', 0), # 0 + ('C', 1), # 1 + ('D', 0), # 2 + ('E', -1), # 3 + ('E', 0), # 4 + ('F', 0), # 5 + ('F', 1), # 6 + ('G', 0), # 7 + ('G', 1), # 8 + ('A', 0), # 9 + ('B', -1), # 10 + ('B', 0), # 11 +) STEPNAMES: set[StepName] = {'C', 'D', 'E', 'F', 'G', 'A', 'B'} # set STEP_TO_DNN_OFFSET: dict[StepName, int] = { 'C': 0, @@ -369,14 +383,9 @@ def _convertPsToStep( >>> pitch._convertPsToStep(59.9999999) ('C', , , 0) ''' - if isinstance(ps, int): - pc = ps % 12 - alter = 0.0 - micro = 0.0 - elif ps == int(ps): - pc = int(ps) % 12 - alter = 0.0 - micro = 0.0 + if isinstance(ps, int) or ps == int(ps): + step, wholeAlter = pitchClassToDefaultStepAlter[int(ps) % 12] + return step, Accidental(wholeAlter), Microtone(0), 0 else: # rounding here is essential ps = round(ps, PITCH_SPACE_SIG_DIGITS) @@ -1959,7 +1968,11 @@ def __init__(self, else: # is a number # is a midiNumber or a ps -- a float midiNumber # get step and accidental w/o octave - self.step, self._accidental = _convertPsToStep(name)[0:2] + if isinstance(name, int) or name == int(name): + self._step, alter = pitchClassToDefaultStepAlter[int(name) % 12] + self._accidental = Accidental(alter) + else: + self.step, self._accidental = _convertPsToStep(name)[0:2] self.spellingIsInferred = True if name >= 12: # is not a pitchClass self._octave = int(name / 12) - 1 @@ -2641,19 +2654,28 @@ def ps(self) -> float: @ps.setter def ps(self, value: int|float) -> None: - # can assign microtone here; will be either None or a Microtone object - self.step, acc, self._microtone, octShift = _convertPsToStep(value) - - # replace a natural with a None - if acc.name == 'natural': - self.accidental = None + if isinstance(value, int) or value == int(value): + wholePs = int(value) + self._step, alter = pitchClassToDefaultStepAlter[wholePs % 12] + # a natural is None + self._accidental = Accidental(alter) if alter else None + self._microtone = None + self._octave = wholePs // 12 - 1 else: - self.accidental = acc - self.octave = _convertPsToOct(value) + octShift + # can assign microtone here; will be either None or a Microtone object + self._step, acc, self._microtone, octShift = _convertPsToStep(value) + + # replace a natural with a None + if acc.name == 'natural': + self._accidental = None + else: + self._accidental = acc + self._octave = _convertPsToOct(value) + octShift # all ps settings must set implicit to True, as we do not know # what accidental this is self.spellingIsInferred = True + self.informClient() @property def midi(self) -> int: diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 97607e8ca7..92e9d62bd4 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -131,6 +131,37 @@ def testInitShortcutsMatchParsing(self): with self.assertRaises(PitchException): Pitch(step='CD') + def testWholeNumberPsAndMidi(self): + ''' + Setting a whole-number .ps or .midi spells the pitch, resets the microtone, + marks the spelling inferred, and tells the Note. A natural is None. + ''' + names = ['C', 'C#', 'D', 'E-', 'E', 'F', 'F#', 'G', 'G#', 'A', 'B-', 'B'] + for ps in range(-13, 140): + for attribute, value in (('ps', ps), ('ps', float(ps)), ('midi', ps)): + if attribute == 'midi' and not 0 <= ps <= 127: + continue + with self.subTest(attribute=attribute, value=value): + n = note.Note('D~5') + n.pitch.microtone = 20 + n._cache['junk'] = 1 + setattr(n.pitch, attribute, value) + self.assertEqual(n.pitch.name, names[ps % 12]) + self.assertEqual(n.pitch.octave, ps // 12 - 1) + self.assertEqual(n.pitch.ps, ps) + if len(names[ps % 12]) == 1: + self.assertIsNone(n.pitch.accidental) + self.assertEqual(n.pitch.microtone.cents, 0) + self.assertTrue(n.pitch.spellingIsInferred) + self.assertEqual(n._cache, {}) + + # Pitch(number) keeps the natural + for midiNumber in range(12, 128): + p = Pitch(midiNumber) + self.assertEqual(p.nameWithOctave, f'{names[midiNumber % 12]}{midiNumber // 12 - 1}') + self.assertIsNotNone(p.accidental) + self.assertTrue(p.spellingIsInferred) + def testAccidentalImport(self): From d912098ba390de8273cbc7958c865020e386d0f2 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 19:48:45 -1000 Subject: [PATCH 152/312] _convertPsToStep: no Microtone for whole numbers; one int() check _convertPsToStep returns None instead of Microtone(0) for whole numbers. The ps setter goes back to a single _convertPsToStep call rather than repeating the table lookup; Pitch.__init__ keeps an inline copy of the lookup, which saves a call (about 45 ns), with comments in both places to keep them in step. Whole numbers are found by comparing int(ps) with ps, which is cheaper than the isinstance check plus a second int(). TODOs note that _convertPsToStep should return an alter, so that the ps setter can change the existing accidental instead of replacing it. AI-assisted (Claude) --- music21/pitch.py | 70 +++++++++++++++++++++----------------- music21/test/test_pitch.py | 10 +++--- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index f88abf957a..180b57c115 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -321,7 +321,7 @@ def _convertPsToStep( ps: int|float ) -> tuple[StepName, Accidental, - Microtone, + Microtone|None, int]: ''' Utility conversion; does not process internal representations. @@ -331,29 +331,31 @@ def _convertPsToStep( Returns a tuple of Step, an Accidental object, a Microtone object or None, and an int representing octave shift (which is nearly always zero, but can be 1 - for a B with very high microtones). + for a B with very high microtones). Whole numbers get None for the Microtone. >>> pitch._convertPsToStep(60) - ('C', , , 0) + ('C', , None, 0) + >>> pitch._convertPsToStep(60.0) + ('C', , None, 0) >>> pitch._convertPsToStep(66) - ('F', , , 0) + ('F', , None, 0) >>> pitch._convertPsToStep(67) - ('G', , , 0) + ('G', , None, 0) >>> pitch._convertPsToStep(68) - ('G', , , 0) + ('G', , None, 0) >>> pitch._convertPsToStep(-2) - ('B', , , 0) + ('B', , None, 0) >>> pitch._convertPsToStep(60.5) ('C', , , 0) >>> pitch._convertPsToStep(62) - ('D', , , 0) + ('D', , None, 0) >>> pitch._convertPsToStep(62.5) ('D', , , 0) >>> pitch._convertPsToStep(135) - ('E', , , 0) + ('E', , None, 0) >>> pitch._convertPsToStep(70) - ('B', , , 0) + ('B', , None, 0) >>> pitch._convertPsToStep(70.2) ('B', , , 0) >>> pitch._convertPsToStep(70.5) @@ -382,10 +384,17 @@ def _convertPsToStep( >>> pitch._convertPsToStep(59.9999999) ('C', , , 0) + + * Changed in v11: whole numbers return None instead of a Microtone of 0 cents. ''' - if isinstance(ps, int) or ps == int(ps): - step, wholeAlter = pitchClassToDefaultStepAlter[int(ps) % 12] - return step, Accidental(wholeAlter), Microtone(0), 0 + # TODO: make _convertPsToStep return an alter not an accidental, and then change + # the alter of the Pitch's existing accidental (setting displayStatus to None) + # to preserve style and displayType information + wholePs = int(ps) + if wholePs == ps: + # if this changes, update Pitch.__init__ too + step, wholeAlter = pitchClassToDefaultStepAlter[wholePs % 12] + return step, Accidental(wholeAlter), None, 0 else: # rounding here is essential ps = round(ps, PITCH_SPACE_SIG_DIGITS) @@ -1968,11 +1977,13 @@ def __init__(self, else: # is a number # is a midiNumber or a ps -- a float midiNumber # get step and accidental w/o octave - if isinstance(name, int) or name == int(name): - self._step, alter = pitchClassToDefaultStepAlter[int(name) % 12] + wholePs = int(name) + if wholePs == name: + # if this changes, update _convertPsToStep too + self._step, alter = pitchClassToDefaultStepAlter[wholePs % 12] self._accidental = Accidental(alter) else: - self.step, self._accidental = _convertPsToStep(name)[0:2] + self._step, self._accidental = _convertPsToStep(name)[0:2] self.spellingIsInferred = True if name >= 12: # is not a pitchClass self._octave = int(name / 12) - 1 @@ -2654,23 +2665,18 @@ def ps(self) -> float: @ps.setter def ps(self, value: int|float) -> None: - if isinstance(value, int) or value == int(value): - wholePs = int(value) - self._step, alter = pitchClassToDefaultStepAlter[wholePs % 12] - # a natural is None - self._accidental = Accidental(alter) if alter else None - self._microtone = None - self._octave = wholePs // 12 - 1 + # TODO: make _convertPsToStep return an alter not an accidental, and then change + # the alter of the Pitch's existing accidental (setting displayStatus to None) + # to preserve style and displayType information + # can assign microtone here; will be either None or a Microtone object + self._step, acc, self._microtone, octShift = _convertPsToStep(value) + + # replace a natural with a None + if acc.name == 'natural': + self._accidental = None else: - # can assign microtone here; will be either None or a Microtone object - self._step, acc, self._microtone, octShift = _convertPsToStep(value) - - # replace a natural with a None - if acc.name == 'natural': - self._accidental = None - else: - self._accidental = acc - self._octave = _convertPsToOct(value) + octShift + self._accidental = acc + self._octave = _convertPsToOct(value) + octShift # all ps settings must set implicit to True, as we do not know # what accidental this is diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 92e9d62bd4..8460e31cae 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -157,10 +157,12 @@ def testWholeNumberPsAndMidi(self): # Pitch(number) keeps the natural for midiNumber in range(12, 128): - p = Pitch(midiNumber) - self.assertEqual(p.nameWithOctave, f'{names[midiNumber % 12]}{midiNumber // 12 - 1}') - self.assertIsNotNone(p.accidental) - self.assertTrue(p.spellingIsInferred) + for value in (midiNumber, float(midiNumber)): + p = Pitch(value) + self.assertEqual(p.nameWithOctave, + f'{names[midiNumber % 12]}{midiNumber // 12 - 1}') + self.assertIsNotNone(p.accidental) + self.assertTrue(p.spellingIsInferred) From f91ab1c1881ae5d4d1f946fd0c11baf3918f9a58 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 19:56:48 -1000 Subject: [PATCH 153/312] Faster copies: cache slots per class; explicit Accidental, Pitch deepcopy _getSlotsRecursive caches its result per class as a frozenset (callers only loop over it), which speeds up copying and pickling every slotted object. Accidental.__deepcopy__ and Pitch.__deepcopy__ assign each attribute directly instead of looping with getattr/setattr; Accidental's copy no longer carries over _client. Pitch attributes set after __init__ are still deep-copied. Accidental 1115 ns -> 394 ns Pitch, sharp 1966 ns -> 803 ns Pitch, no acc. 877 ns -> 490 ns Duration 3089 ns -> 2777 ns Beams 8203 ns -> 6906 ns Note, sharp 12626 ns -> 10738 ns pickle Note 11332 ns -> 9838 ns AI-assisted (Claude) --- music21/common/objects.py | 14 +++++++++--- music21/pitch.py | 44 +++++++++++++++++++++++++++----------- music21/test/test_pitch.py | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/music21/common/objects.py b/music21/common/objects.py index f0fbe3c089..24a63083b2 100644 --- a/music21/common/objects.py +++ b/music21/common/objects.py @@ -159,6 +159,8 @@ def __call__(self) -> int: # ------------------------------------------------------------------------------ +_slotsByClass: dict[type, frozenset[str]] = {} + class SlottedObjectMixin: r''' @@ -223,9 +225,9 @@ def __setstate__(self, state: dict[str, t.Any]) -> None: for slot, value in state.items(): setattr(self, slot, value) - def _getSlotsRecursive(self) -> set[str]: + def _getSlotsRecursive(self) -> frozenset[str]: ''' - Find all slots recursively. + Find all slots recursively. Cached per class. A private attribute so as not to change the contents of inheriting objects private interfaces: @@ -250,10 +252,16 @@ def _getSlotsRecursive(self) -> set[str]: ['_editorial', '_style', 'direction', 'funkiness', 'groovability', 'id', 'independentAngle', 'number', 'type'] ''' + try: + return _slotsByClass[type(self)] + except KeyError: + pass slots: set[str] = set() for cls in self.__class__.mro(): slots.update(getattr(cls, '__slots__', ())) - return slots + frozenSlots = frozenset(slots) + _slotsByClass[type(self)] = frozenSlots + return frozenSlots class EqualSlottedObjectMixin(SlottedObjectMixin): diff --git a/music21/pitch.py b/music21/pitch.py index a644e758cd..e78b97ad4b 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1042,8 +1042,17 @@ def __hash__(self) -> int: def __deepcopy__(self, memo: dict[int, t.Any]) -> Accidental: if type(self) is Accidental: # pylint: disable=unidiomatic-typecheck new = Accidental.__new__(Accidental) - for s in self._getSlotsRecursive(): - setattr(new, s, getattr(self, s)) + new._alter = self._alter + new._displayStatus = self._displayStatus + new._displayType = self._displayType + new._modifier = self._modifier + new._name = self._name + new._client = None + new.displayLocation = self.displayLocation + new.displaySize = self.displaySize + new.displayStyle = self.displayStyle + new._style = None + new._editorial = None if self._style is not None: new._style = copy.deepcopy(self._style, memo) if self._editorial is not None: @@ -2068,17 +2077,26 @@ def __deepcopy__(self, memo: dict[int, t.Any]) -> Pitch: ''' if type(self) is Pitch: # pylint: disable=unidiomatic-typecheck new = Pitch.__new__(Pitch) - for k in self.__dict__: - v = getattr(self, k, None) - if k in ('_step', '_overridden_freq440', - '_octave', 'spellingIsInferred'): - setattr(new, k, v) - elif k == '_client': - setattr(new, k, None) - elif v is None: # common -- save time over deepcopy. - setattr(new, k, None) - else: - setattr(new, k, copy.deepcopy(v, memo)) + new._groups = None if self._groups is None else copy.deepcopy(self._groups, memo) + new._step = self._step + new._overridden_freq440 = self._overridden_freq440 + new._accidental = (None if self._accidental is None + else copy.deepcopy(self._accidental, memo)) + new._microtone = (None if self._microtone is None + else copy.deepcopy(self._microtone, memo)) + new._octave = self._octave + new.spellingIsInferred = self.spellingIsInferred + new.fundamental = (None if self.fundamental is None + else copy.deepcopy(self.fundamental, memo)) + new._client = None + + selfDict = self.__dict__ + newDict = new.__dict__ + if len(selfDict) != len(newDict): + # attributes not set in __init__ + for k, v in selfDict.items(): + if k not in newDict: + setattr(new, k, copy.deepcopy(v, memo)) return new else: # pragma: no cover return common.defaultDeepcopy(self, memo) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 97607e8ca7..4eb16a1b02 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -34,6 +34,27 @@ def testCopyManually(self): self.assertIsNot(p1, p2) self.assertIsNot(p1.accidental, p2.accidental) + def testPitchDeepcopyAttributes(self): + ''' + Pitch.__deepcopy__ copies attributes one at a time, for speed. Check that it + copies every attribute, including any set after __init__. Objects are copied, + not shared, and _client is cleared. + ''' + p = Pitch('C#4', microtone=20, fundamental=Pitch('C2')) + p.groups.append('ficta') + owner = note.Note(p) + self.assertIs(p._client, owner) + p.frequencyShifts = [1, 0.5, -1, 2] # not set by __init__ + + p2 = copy.deepcopy(p) + self.assertEqual(set(vars(p2)), set(vars(p))) + self.assertIsNone(p2._client) + for attr in ('_accidental', '_microtone', '_groups', 'fundamental', 'frequencyShifts'): + self.assertEqual(getattr(p2, attr), getattr(p, attr), attr) + self.assertIsNot(getattr(p2, attr), getattr(p, attr), attr) + for attr in ('_step', '_octave', '_overridden_freq440', 'spellingIsInferred'): + self.assertEqual(getattr(p2, attr), getattr(p, attr), attr) + def testRepr(self): p = pitch.Pitch('B#3') self.assertEqual(repr(p), '') @@ -165,6 +186,24 @@ def testAccidentalDeepcopyDoesNotShareStyleOrEditorial(self): self.assertEqual(a.style.color, 'red') self.assertEqual(len(a.editorial.comments), 1) + def testAccidentalDeepcopyCopiesEverySlot(self): + ''' + Every slot but _client is copied; a slot added to Accidental + must be added to its __deepcopy__ too. + ''' + a = Accidental('sharp') + a.displayStatus = True + a.displayType = 'always' + a.displayStyle = 'parentheses' + a.displaySize = 'cue' + a.displayLocation = 'above' + a._client = Pitch('C#4') # Accidental has no public client + b = copy.deepcopy(a) + for slot in a._getSlotsRecursive() - {'_client'}: + with self.subTest(slot=slot): + self.assertEqual(getattr(b, slot), getattr(a, slot)) + self.assertIsNone(b._client) + def testUpdateAccidentalDisplaySimple(self): ''' Test updating accidental display. From 8c5fe0ea07fa33ca594b4b6b02044bdff3225397 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 20:00:38 -1000 Subject: [PATCH 154/312] testWholeNumberPsAndMidi: chosen cases, and a mock for the Note Test each pitch class once plus the octave edges (0, 11, 12, 127, and a negative ps) instead of every number from -13 to 139, and check that the Note is told once with a mock of pitchChanged instead of planting an entry in its private cache. AI-assisted (Claude) --- music21/test/test_pitch.py | 41 ++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 8460e31cae..ff3802e1e7 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -12,6 +12,7 @@ import copy import unittest +from unittest import mock from music21 import common from music21 import converter @@ -134,35 +135,37 @@ def testInitShortcutsMatchParsing(self): def testWholeNumberPsAndMidi(self): ''' Setting a whole-number .ps or .midi spells the pitch, resets the microtone, - marks the spelling inferred, and tells the Note. A natural is None. + marks the spelling inferred, and tells the Note once. A natural is None. ''' - names = ['C', 'C#', 'D', 'E-', 'E', 'F', 'F#', 'G', 'G#', 'A', 'B-', 'B'] - for ps in range(-13, 140): + # every pitch class once, then the octave edges and a negative ps + for ps, name, octave in [ + (60, 'C', 4), (61, 'C#', 4), (62, 'D', 4), (63, 'E-', 4), + (64, 'E', 4), (65, 'F', 4), (66, 'F#', 4), (67, 'G', 4), + (68, 'G#', 4), (69, 'A', 4), (70, 'B-', 4), (71, 'B', 4), + (0, 'C', -1), (11, 'B', -1), (12, 'C', 0), (127, 'G', 9), (-1, 'B', -2), + ]: for attribute, value in (('ps', ps), ('ps', float(ps)), ('midi', ps)): - if attribute == 'midi' and not 0 <= ps <= 127: - continue + if attribute == 'midi' and ps < 0: + continue # the midi setter wraps negative values into 0-11 with self.subTest(attribute=attribute, value=value): n = note.Note('D~5') n.pitch.microtone = 20 - n._cache['junk'] = 1 - setattr(n.pitch, attribute, value) - self.assertEqual(n.pitch.name, names[ps % 12]) - self.assertEqual(n.pitch.octave, ps // 12 - 1) - self.assertEqual(n.pitch.ps, ps) - if len(names[ps % 12]) == 1: + with mock.patch.object(n, 'pitchChanged') as pitchChanged: + setattr(n.pitch, attribute, value) + pitchChanged.assert_called_once() + self.assertEqual(n.pitch.name, name) + self.assertEqual(n.pitch.octave, octave) + if len(name) == 1: self.assertIsNone(n.pitch.accidental) self.assertEqual(n.pitch.microtone.cents, 0) self.assertTrue(n.pitch.spellingIsInferred) - self.assertEqual(n._cache, {}) # Pitch(number) keeps the natural - for midiNumber in range(12, 128): - for value in (midiNumber, float(midiNumber)): - p = Pitch(value) - self.assertEqual(p.nameWithOctave, - f'{names[midiNumber % 12]}{midiNumber // 12 - 1}') - self.assertIsNotNone(p.accidental) - self.assertTrue(p.spellingIsInferred) + for value, nameWithOctave in [(60, 'C4'), (60.0, 'C4'), (70, 'B-4'), (70.0, 'B-4')]: + p = Pitch(value) + self.assertEqual(p.nameWithOctave, nameWithOctave) + self.assertIsNotNone(p.accidental) + self.assertTrue(p.spellingIsInferred) From 43e4375886fa76c784d7e95b93aa267d257dbcb8 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 20:07:37 -1000 Subject: [PATCH 155/312] Tests: only what is not covered elsewhere testPsAndMidiSettersInformNoteOnce checks that the ps and midi setters tell the Note once. testWholeNumberPsAndMidi keeps only cases the doctests do not cover: clearing a quarter-tone accidental and a microtone, a natural as None, a negative ps, the midi setter above 127, and Pitch(number) keeping its natural. AI-assisted (Claude) --- music21/test/test_pitch.py | 60 ++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index fb1b933d58..e10b169f0e 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -133,40 +133,44 @@ def testInitShortcutsMatchParsing(self): with self.assertRaises(PitchException): Pitch(step='CD') + def testPsAndMidiSettersInformNoteOnce(self): + for attribute in ('ps', 'midi'): + with self.subTest(attribute=attribute): + n = note.Note('D4') + with mock.patch.object(n, 'pitchChanged') as pitchChanged: + setattr(n.pitch, attribute, 61) + pitchChanged.assert_called_once() + def testWholeNumberPsAndMidi(self): ''' - Setting a whole-number .ps or .midi spells the pitch, resets the microtone, - marks the spelling inferred, and tells the Note once. A natural is None. + Setting a whole-number .ps or .midi replaces a quarter-tone accidental + and clears a microtone. ''' - # every pitch class once, then the octave edges and a negative ps - for ps, name, octave in [ - (60, 'C', 4), (61, 'C#', 4), (62, 'D', 4), (63, 'E-', 4), - (64, 'E', 4), (65, 'F', 4), (66, 'F#', 4), (67, 'G', 4), - (68, 'G#', 4), (69, 'A', 4), (70, 'B-', 4), (71, 'B', 4), - (0, 'C', -1), (11, 'B', -1), (12, 'C', 0), (127, 'G', 9), (-1, 'B', -2), - ]: - for attribute, value in (('ps', ps), ('ps', float(ps)), ('midi', ps)): - if attribute == 'midi' and ps < 0: - continue # the midi setter wraps negative values into 0-11 - with self.subTest(attribute=attribute, value=value): - n = note.Note('D~5') - n.pitch.microtone = 20 - with mock.patch.object(n, 'pitchChanged') as pitchChanged: - setattr(n.pitch, attribute, value) - pitchChanged.assert_called_once() - self.assertEqual(n.pitch.name, name) - self.assertEqual(n.pitch.octave, octave) - if len(name) == 1: - self.assertIsNone(n.pitch.accidental) - self.assertEqual(n.pitch.microtone.cents, 0) - self.assertTrue(n.pitch.spellingIsInferred) + p = Pitch('D~5') + p.microtone = 20 + p.ps = 61 + self.assertEqual(p.nameWithOctave, 'C#4') + self.assertEqual(p.microtone.cents, 0) + + # a natural is None, and a negative ps floors to the octave below + p.ps = 60.0 + self.assertEqual(p.nameWithOctave, 'C4') + self.assertIsNone(p.accidental) + p.ps = -1 + self.assertEqual((p.step, p.octave), ('B', -2)) + + # above 127, the midi setter wraps into the top octave; ps does not + p = Pitch() + p.midi = 130 + self.assertEqual(p.nameWithOctave, 'B-8') + p.ps = 130 + self.assertEqual(p.nameWithOctave, 'B-9') # Pitch(number) keeps the natural - for value, nameWithOctave in [(60, 'C4'), (60.0, 'C4'), (70, 'B-4'), (70.0, 'B-4')]: + for value in (60, 60.0): p = Pitch(value) - self.assertEqual(p.nameWithOctave, nameWithOctave) - self.assertIsNotNone(p.accidental) - self.assertTrue(p.spellingIsInferred) + self.assertEqual(p.nameWithOctave, 'C4') + self.assertEqual(p.accidental.name, 'natural') From aaf1c393b2b78669856087276ef7382ae29ff1c5 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 7 Oct 2026 23:40:16 -1000 Subject: [PATCH 156/312] testOctaveIsImplicit: mock pitchChanged instead of filling the Note cache AI-assisted (Claude) --- music21/test/test_pitch.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index e10b169f0e..81d1e7fcbb 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -101,11 +101,11 @@ def testOctaveIsImplicit(self): # the setter informs a Note client only when something changed n = note.Note('C4') - n._cache['junk'] = 1 - n.pitch.octaveIsImplicit = False - self.assertEqual(n._cache, {'junk': 1}) - n.pitch.octaveIsImplicit = True - self.assertEqual(n._cache, {}) + with mock.patch.object(n, 'pitchChanged') as pitchChanged: + n.pitch.octaveIsImplicit = False + pitchChanged.assert_not_called() + n.pitch.octaveIsImplicit = True + pitchChanged.assert_called_once() def testNameSetting(self): with self.assertRaisesRegex(ValueError, From 7085364936dc589b305e7700327d718d01724d1a Mon Sep 17 00:00:00 2001 From: hill Date: Thu, 8 Oct 2026 13:44:22 +0200 Subject: [PATCH 157/312] Find nextPitch neighbors among altered pitches nextPitch looked for the neighbors of an origin that is not in the network without the altered degrees, so in A harmonic minor G4 sat on the G node, whose realized pitch is G#, and ascending went to A3. --- music21/scale/intervalNetwork.py | 5 +++-- music21/scale/test_scale_main.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index c8044f83ae..f31c253482 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -1353,7 +1353,8 @@ def nextPitch( neighborIds = self.getNeighborNodeIds(pitchReference=pitchReference, nodeName=nodeName, pitchTarget=pitchOriginObj, - direction=direction) # must add direction + direction=direction, + alteredDegrees=alteredDegrees) lowId, highId = t.cast( 'tuple[Terminus|int, Terminus|int]', neighborIds) @@ -1740,7 +1741,7 @@ def _walkAscending( break if len(walked) >= 100: raise IntervalNetworkException( - 'Cannot realize these pitches; is your scale ' + 'Cannot realize these pitches; is the network ' + "well-formed? (especially check if you're giving notes without octaves)") return walked diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index 73d876f0e7..6249641fbd 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -360,6 +360,8 @@ def testNextPitchNodeNearOrigin(self): aMinor = scale.HarmonicMinorScale('a4') self.assertEqual(str(aMinor.nextPitch('f##4')), 'G#4') self.assertEqual(str(aMinor.nextPitch('a4', Direction.DESCENDING)), 'G#4') + self.assertEqual(str(aMinor.nextPitch('g4')), 'G#4') + self.assertEqual(str(aMinor.nextPitch('g4', Direction.DESCENDING)), 'F4') gMinor = scale.HarmonicMinorScale('g4') gMinor.nextPitch('a4') From 37acfe7f524e9552f9c3d54948aaaf26dfc6b94c Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 9 Oct 2026 03:33:04 -1000 Subject: [PATCH 158/312] Cache parsed Pitch names The .name setter remembers each string it parses as (step, accidental string, octave) in a module-level dict, cleared at 1000 entries. A remembered name sets the attributes directly, makes a new Accidental (they are mutable, so never shared), and informs the Note once. Names that fail to parse are not remembered and raise as before. Best of 7, master -> branch: Pitch('D5') 543 -> 230 ns Pitch('F#4') 691 -> 319 ns p.name = 'F#4' 556 -> 172 ns Note('F#4') 1812 -> 1382 ns AI-assisted (Claude) --- music21/pitch.py | 41 +++++++++++++++++++++++++++++++++++++- music21/test/test_pitch.py | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/music21/pitch.py b/music21/pitch.py index 41bf57f0f8..8d427281c9 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1653,6 +1653,12 @@ def fullName(self) -> str: # ------------------------------------------------------------------------------ +# strings that Pitch.name parsed: (step, accidental string or None, octave or None). +# Holds no Accidental objects, since those are mutable. Cleared when full. +_pitchNameCache: dict[str, tuple[StepName, str|None, int|None]] = {} +_pitchNameCacheSize = 1000 + + # tried as SlottedObjectMixin -- made creation time slower! Not worth the restrictions class Pitch(prebase.ProtoM21Object): ''' @@ -2846,6 +2852,32 @@ def name(self, usrStr: str) -> None: Set name, which may be provided with or without octave values. C4 or D-3 are both accepted. ''' + try: + step, accidentalStr, octave = _pitchNameCache[usrStr] + except (KeyError, TypeError): # TypeError: not hashable, so not a str + self._parseName(usrStr) + return + self._step = step + self.spellingIsInferred = False + self._accidental = (None if accidentalStr is None + else Accidental(accidentalStr)) + if octave is not None: + self._octave = octave + self.informClient() + + def _parseName(self, usrStr: str) -> None: + ''' + The `.name` setter for a string not yet seen: parses it, sets step, + accidental, and octave, and remembers the result for next time. + + >>> p = pitch.Pitch() + >>> p._parseName(' f#5') + >>> p + + >>> pitch._pitchNameCache[' f#5'] + ('F', '#', 5) + ''' + cacheKey = usrStr try: usrStr = usrStr.strip() except AttributeError: @@ -2866,6 +2898,7 @@ def name(self, usrStr: str) -> None: octNot.append(char) usrStr = ''.join(octNot) octFoundStr = ''.join(octFound) + accidentalStr: str|None = None # we have nothing but pitch specification if len(usrStr) == 1: self.step = usrStr # type: ignore @@ -2873,14 +2906,20 @@ def name(self, usrStr: str) -> None: # assume everything following pitch is accidental specification elif len(usrStr) > 1: self.step = usrStr[0] # type: ignore - self.accidental = Accidental(usrStr[1:]) + accidentalStr = usrStr[1:] + self.accidental = Accidental(accidentalStr) else: raise PitchException(f'Cannot make a name out of {usrStr!r}') + octave: int|None = None if octFoundStr: # bool('0') == True, so okay octave = int(octFoundStr) self.octave = octave + if len(_pitchNameCache) >= _pitchNameCacheSize: + _pitchNameCache.clear() + _pitchNameCache[cacheKey] = (self._step, accidentalStr, octave) + @property def unicodeName(self) -> str: ''' diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 4037e92f52..d4aa977dea 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -142,6 +142,43 @@ def testNameSetting(self): ): p.name = 32 + def testNameCache(self): + ''' + A name is parsed once and remembered; names that cannot be parsed are not. + ''' + with mock.patch.dict(pitch._pitchNameCache, clear=True): + first = Pitch('E-6') + self.assertEqual(pitch._pitchNameCache, {'E-6': ('E', '-', 6)}) + second = Pitch('E-6') + self.assertEqual(second.nameWithOctave, 'E-6') + # Accidentals can be changed, so each Pitch gets its own + self.assertIsNot(second.accidental, first.accidental) + + with self.assertRaises(AccidentalException): + Pitch('C$') + with self.assertRaisesRegex(ValueError, 'must be a string'): + second.name = ['C4'] # not hashable + self.assertEqual(list(pitch._pitchNameCache), ['E-6']) + + # cleared when full + with mock.patch.object(pitch, '_pitchNameCacheSize', 1): + Pitch('F4') + self.assertEqual(list(pitch._pitchNameCache), ['F4']) + + def testRememberedNameInformsNoteOnce(self): + ''' + Setting a remembered name without an octave keeps the octave and the + microtone, makes the spelling explicit, and tells the Note once. + ''' + Pitch('B-') + n = note.Note(73) # C#5, spelling inferred + n.pitch.microtone = 20 + with mock.patch.object(n, 'pitchChanged') as pitchChanged: + n.pitch.name = 'B-' + pitchChanged.assert_called_once() + self.assertEqual(str(n.pitch), 'B-5(+20c)') + self.assertFalse(n.pitch.spellingIsInferred) + def testInitShortcutsMatchParsing(self): # 'C', 'C4', and step= skip the name and step setters self.assertEqual(Pitch('C'), Pitch('c')) From a86db327d6d046c181091f599e87a56c3386332e Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 9 Oct 2026 16:49:35 -1000 Subject: [PATCH 159/312] Keep only the slots cache; restore master's copying and Accidental init Accidental.__deepcopy__ and Pitch.__deepcopy__ go back to master's loops, so a slot or attribute added to a class or its parents is still copied without anyone updating a list. The tests that checked those lists go too. Accidental.__init__ calls super().__init__() again instead of inlining StyleMixin.__init__ (#2070), which listed a parent's attributes in a subclass. That costs about 30 ns per Accidental (91 -> 121 ns). What is left is the per-class _getSlotsRecursive cache. Best of 7, master -> branch: copy Accidental 1128 -> 787 ns copy Pitch, sharp 1964 -> 1627 ns copy Duration 3153 -> 2797 ns copy Note, sharp 12793 -> 11982 ns copy Measure of 4 notes 101429 -> 97116 ns pickle Note 11731 -> 10043 ns AI-assisted (Claude) --- music21/pitch.py | 53 +++++++++++--------------------------- music21/test/test_pitch.py | 39 ---------------------------- 2 files changed, 15 insertions(+), 77 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index 4a1245bf8c..51656f12e0 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -1039,13 +1039,8 @@ class Accidental(prebase.ProtoM21Object, style.StyleMixin): # INITIALIZER # - def __init__( # pylint: disable=super-init-not-called - self, - specifier: int|str|float = 'natural', - ) -> None: - # StyleMixin.__init__, inlined for speed; keep in sync - self._style = None - self._editorial = None + def __init__(self, specifier: int|str|float = 'natural') -> None: + super().__init__() # managed by properties self._displayType = 'normal' # normal, always, never, if-absolutely-necessary, @@ -1093,17 +1088,8 @@ def __hash__(self) -> int: def __deepcopy__(self, memo: dict[int, t.Any]) -> Accidental: if type(self) is Accidental: # pylint: disable=unidiomatic-typecheck new = Accidental.__new__(Accidental) - new._alter = self._alter - new._displayStatus = self._displayStatus - new._displayType = self._displayType - new._modifier = self._modifier - new._name = self._name - new._client = None - new.displayLocation = self.displayLocation - new.displaySize = self.displaySize - new.displayStyle = self.displayStyle - new._style = None - new._editorial = None + for s in self._getSlotsRecursive(): + setattr(new, s, getattr(self, s)) if self._style is not None: new._style = copy.deepcopy(self._style, memo) if self._editorial is not None: @@ -2076,26 +2062,17 @@ def __deepcopy__(self, memo: dict[int, t.Any]) -> Pitch: ''' if type(self) is Pitch: # pylint: disable=unidiomatic-typecheck new = Pitch.__new__(Pitch) - new._groups = None if self._groups is None else copy.deepcopy(self._groups, memo) - new._step = self._step - new._overridden_freq440 = self._overridden_freq440 - new._accidental = (None if self._accidental is None - else copy.deepcopy(self._accidental, memo)) - new._microtone = (None if self._microtone is None - else copy.deepcopy(self._microtone, memo)) - new._octave = self._octave - new.spellingIsInferred = self.spellingIsInferred - new.fundamental = (None if self.fundamental is None - else copy.deepcopy(self.fundamental, memo)) - new._client = None - - selfDict = self.__dict__ - newDict = new.__dict__ - if len(selfDict) != len(newDict): - # attributes not set in __init__ - for k, v in selfDict.items(): - if k not in newDict: - setattr(new, k, copy.deepcopy(v, memo)) + for k in self.__dict__: + v = getattr(self, k, None) + if k in ('_step', '_overridden_freq440', + '_octave', 'spellingIsInferred'): + setattr(new, k, v) + elif k == '_client': + setattr(new, k, None) + elif v is None: # common -- save time over deepcopy. + setattr(new, k, None) + else: + setattr(new, k, copy.deepcopy(v, memo)) return new else: # pragma: no cover return common.defaultDeepcopy(self, memo) diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index ad59e21123..35875404a9 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -36,27 +36,6 @@ def testCopyManually(self): self.assertIsNot(p1, p2) self.assertIsNot(p1.accidental, p2.accidental) - def testPitchDeepcopyAttributes(self): - ''' - Pitch.__deepcopy__ copies attributes one at a time, for speed. Check that it - copies every attribute, including any set after __init__. Objects are copied, - not shared, and _client is cleared. - ''' - p = Pitch('C#4', microtone=20, fundamental=Pitch('C2')) - p.groups.append('ficta') - owner = note.Note(p) - self.assertIs(p._client, owner) - p.frequencyShifts = [1, 0.5, -1, 2] # not set by __init__ - - p2 = copy.deepcopy(p) - self.assertEqual(set(vars(p2)), set(vars(p))) - self.assertIsNone(p2._client) - for attr in ('_accidental', '_microtone', '_groups', 'fundamental', 'frequencyShifts'): - self.assertEqual(getattr(p2, attr), getattr(p, attr), attr) - self.assertIsNot(getattr(p2, attr), getattr(p, attr), attr) - for attr in ('_step', '_octave', '_overridden_freq440', 'spellingIsInferred'): - self.assertEqual(getattr(p2, attr), getattr(p, attr), attr) - def testRepr(self): p = pitch.Pitch('B#3') self.assertEqual(repr(p), '') @@ -188,24 +167,6 @@ def testAccidentalDeepcopyDoesNotShareStyleOrEditorial(self): self.assertEqual(a.style.color, 'red') self.assertEqual(len(a.editorial.comments), 1) - def testAccidentalDeepcopyCopiesEverySlot(self): - ''' - Every slot but _client is copied; a slot added to Accidental - must be added to its __deepcopy__ too. - ''' - a = Accidental('sharp') - a.displayStatus = True - a.displayType = 'always' - a.displayStyle = 'parentheses' - a.displaySize = 'cue' - a.displayLocation = 'above' - a._client = Pitch('C#4') # Accidental has no public client - b = copy.deepcopy(a) - for slot in a._getSlotsRecursive() - {'_client'}: - with self.subTest(slot=slot): - self.assertEqual(getattr(b, slot), getattr(a, slot)) - self.assertIsNone(b._client) - def testAccidentalLookupTable(self): ''' Every way of writing each standard accidental gives the same accidental From 26fac461ce73e50455ca41d2dd6d80941b7d6c06 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 9 Oct 2026 17:04:20 -1000 Subject: [PATCH 160/312] Pitch.transpose(inPlace=True) takes the transposed microtone, even none The in-place copy-back took the transposed copy's microtone only when it was not None, so a microtone the transposition removed stayed: C4(+50c) up a ChromaticInterval(0.5) in place gave C#4(+50c), while the transposed copy is C#4. AI-assisted (Claude) --- music21/pitch.py | 5 ++--- music21/test/test_pitch.py | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/music21/pitch.py b/music21/pitch.py index ea41aa7942..a50d9e0b34 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -4726,9 +4726,8 @@ def transpose( # set fundamental self.fundamental = p.fundamental self.spellingIsInferred = p.spellingIsInferred - # deepcopy _microtone if present (not thru microtone property to detect None) - if p._microtone is not None: - self._microtone = copy.deepcopy(p._microtone) + # deepcopy _microtone (not thru microtone property to detect None) + self._microtone = copy.deepcopy(p._microtone) return None # -------------------------------------------------------------------------- diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 81d1e7fcbb..006e6dc8ee 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -19,6 +19,7 @@ from music21 import converter from music21 import corpus from music21 import editorial +from music21 import interval from music21 import key from music21 import note from music21 import pitch @@ -801,6 +802,12 @@ def testLowNotes(self): self.assertEqual(lowC.name, 'C') self.assertEqual(lowC.octave, -1) + def testTransposeInPlaceReplacesMicrotone(self): + p = Pitch('C4') + p.microtone = 50 + p.transpose(interval.ChromaticInterval(0.5), inPlace=True) + self.assertEqual(str(p), 'C#4') + def testQuarterToneA(self): p1 = Pitch('D#~') # environLocal.printDebug([p1, p1.accidental]) From 460eaf682b8c9b5a3697944752c9598a924d7558 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 9 Oct 2026 17:30:30 -1000 Subject: [PATCH 161/312] Fix caching problem / remove ref to defaultOctave Small PR --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/defaults.py | 3 +++ music21/voiceLeading.py | 10 +++++++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index 9d2d4e79d8..b786868966 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b11' +__version__ = '11.0.0b12' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index b049c44f76..87c452e8ec 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b11' +'11.0.0b12' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/defaults.py b/music21/defaults.py index 95162b86a3..99feeb555c 100644 --- a/music21/defaults.py +++ b/music21/defaults.py @@ -39,6 +39,9 @@ # not allowing more can be construed as a feature. +# we used to have a defaultStep ('C') and defaultOctave (4) but +# no one ever changed them, and inlining makes so much faster. + partGroup = 'Part Group' partGroupAbbreviation = 'PG' diff --git a/music21/voiceLeading.py b/music21/voiceLeading.py index 9022376e88..ac983bb251 100644 --- a/music21/voiceLeading.py +++ b/music21/voiceLeading.py @@ -2216,7 +2216,7 @@ def couldBePassingTone(self) -> bool: >>> voiceLeading.ThreeNoteLinearSegment('B##3', 'C4', 'D--4').couldBePassingTone() False - If no octave is given then ._defaultOctave is used. This is generally octave 4. + If no octave is given then octave 4 is used: >>> voiceLeading.ThreeNoteLinearSegment('C', 'D', 'E').couldBePassingTone() True @@ -2225,6 +2225,14 @@ def couldBePassingTone(self) -> bool: >>> voiceLeading.ThreeNoteLinearSegment('C5', 'D', 'E').couldBePassingTone() False + But I wouldn't skip the octave, because otherwise this will always seem baffling: + + >>> voiceLeading.ThreeNoteLinearSegment('G', 'A', 'B').couldBePassingTone() + True + >>> voiceLeading.ThreeNoteLinearSegment('A', 'B', 'C').couldBePassingTone() + False + + Method returns True if either a chromatic passing tone or a diatonic passing tone is identified. Spelling of the pitch does matter! From ebc27ea393eca89418933eeb8746bd51f209fd01 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Fri, 9 Oct 2026 17:56:08 -1000 Subject: [PATCH 162/312] Name cache: parse into the cache, then set once; early return for style _parseName is now _cacheParsedName: it only parses and remembers (step, accidental string, octave). The .name setter reads the cache after a miss too, so it sets the attributes and informs the Note once even the first time, and a name that fails to parse leaves the pitch unchanged (before, D$ changed the step to D and then raised). Names over 100 characters raise a ValueError, so the cache cannot hold huge strings. setStyleAttributes returns early for a tag with no attributes (most , , tags): natural 1378 -> 491 ns; about 1-3% on whole MusicXML files. AI-assisted (Claude) --- music21/musicxml/xmlToM21.py | 3 +++ music21/pitch.py | 41 ++++++++++++++++++------------------ music21/test/test_pitch.py | 17 +++++++++------ 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index de35ee2e52..07d8c39ebf 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -301,6 +301,9 @@ def setStyleAttributes(self, mxObject, m21Object, musicXMLNames, m21Names=None): >>> m21Obj.style.hideObjectOnPrint True ''' + if not mxObject.attrib: + return + if isinstance(m21Object, style.Style): stObj = m21Object else: diff --git a/music21/pitch.py b/music21/pitch.py index 67ab52e24b..145c3d81d3 100644 --- a/music21/pitch.py +++ b/music21/pitch.py @@ -2817,6 +2817,8 @@ def name(self) -> str: >>> a = pitch.Pitch('B---') >>> a.name 'B---' + + * Changed in v11: a name longer than 100 characters raises a ValueError. ''' if self.accidental is not None: return self.step + self.accidental.modifier @@ -2832,8 +2834,8 @@ def name(self, usrStr: str) -> None: try: step, accidentalStr, octave = _pitchNameCache[usrStr] except (KeyError, TypeError): # TypeError: not hashable, so not a str - self._parseName(usrStr) - return + self._cacheParsedName(usrStr) + step, accidentalStr, octave = _pitchNameCache[usrStr] self._step = step self.spellingIsInferred = False self._accidental = (None if accidentalStr is None @@ -2842,15 +2844,13 @@ def name(self, usrStr: str) -> None: self._octave = octave self.informClient() - def _parseName(self, usrStr: str) -> None: + def _cacheParsedName(self, usrStr: str) -> None: ''' - The `.name` setter for a string not yet seen: parses it, sets step, - accidental, and octave, and remembers the result for next time. + Parse a name not yet seen and remember its step, accidental, and + octave for the `.name` setter. Raises if it is not a name. >>> p = pitch.Pitch() - >>> p._parseName(' f#5') - >>> p - + >>> p._cacheParsedName(' f#5') >>> pitch._pitchNameCache[' f#5'] ('F', '#', 5) ''' @@ -2859,6 +2859,10 @@ def _parseName(self, usrStr: str) -> None: usrStr = usrStr.strip() except AttributeError: raise ValueError(f'Argument to name, {usrStr!r}, must be a string, not {type(usrStr)}.') + # remembered names are kept, so do not keep huge ones + if len(cacheKey) > 100: + raise ValueError( + f'Argument to name must be at most 100 characters, not {len(cacheKey)}.') # extract any numbers that may be octave designations octFound: list[str] = [] @@ -2875,27 +2879,24 @@ def _parseName(self, usrStr: str) -> None: octNot.append(char) usrStr = ''.join(octNot) octFoundStr = ''.join(octFound) - accidentalStr: str|None = None - # we have nothing but pitch specification - if len(usrStr) == 1: - self.step = usrStr # type: ignore - self.accidental = None + if not usrStr: + raise PitchException(f'Cannot make a name out of {usrStr!r}') + step = t.cast(StepName, usrStr[0].upper()) + if step not in STEPNAMES: + raise PitchException(f'Cannot make a step out of {step!r}') # assume everything following pitch is accidental specification - elif len(usrStr) > 1: - self.step = usrStr[0] # type: ignore + accidentalStr: str|None = None + if len(usrStr) > 1: accidentalStr = usrStr[1:] - self.accidental = Accidental(accidentalStr) - else: - raise PitchException(f'Cannot make a name out of {usrStr!r}') + Accidental(accidentalStr) # raises if it is not an accidental octave: int|None = None if octFoundStr: # bool('0') == True, so okay octave = int(octFoundStr) - self.octave = octave if len(_pitchNameCache) >= _pitchNameCacheSize: _pitchNameCache.clear() - _pitchNameCache[cacheKey] = (self._step, accidentalStr, octave) + _pitchNameCache[cacheKey] = (step, accidentalStr, octave) @property def unicodeName(self) -> str: diff --git a/music21/test/test_pitch.py b/music21/test/test_pitch.py index 165238677a..6c57994ab0 100644 --- a/music21/test/test_pitch.py +++ b/music21/test/test_pitch.py @@ -134,26 +134,31 @@ def testNameCache(self): # Accidentals can be changed, so each Pitch gets its own self.assertIsNot(second.accidental, first.accidental) + # a name that cannot be parsed changes nothing with self.assertRaises(AccidentalException): - Pitch('C$') + second.name = 'D$' + self.assertEqual(second.nameWithOctave, 'E-6') with self.assertRaisesRegex(ValueError, 'must be a string'): second.name = ['C4'] # not hashable + with self.assertRaisesRegex(ValueError, 'at most 100 characters'): + second.name = 'C4'.center(101) # padding counts self.assertEqual(list(pitch._pitchNameCache), ['E-6']) + self.assertEqual(Pitch('C4'.center(100)).nameWithOctave, 'C4') # cleared when full with mock.patch.object(pitch, '_pitchNameCacheSize', 1): Pitch('F4') self.assertEqual(list(pitch._pitchNameCache), ['F4']) - def testRememberedNameInformsNoteOnce(self): + def testNameSetterInformsNoteOnce(self): ''' - Setting a remembered name without an octave keeps the octave and the - microtone, makes the spelling explicit, and tells the Note once. + Setting a name without an octave, even one not seen before, keeps the + octave and the microtone, makes the spelling explicit, and tells the Note once. ''' - Pitch('B-') n = note.Note(73) # C#5, spelling inferred n.pitch.microtone = 20 - with mock.patch.object(n, 'pitchChanged') as pitchChanged: + with (mock.patch.dict(pitch._pitchNameCache, clear=True), + mock.patch.object(n, 'pitchChanged') as pitchChanged): n.pitch.name = 'B-' pitchChanged.assert_called_once() self.assertEqual(str(n.pitch), 'B-5(+20c)') From 56384bd6675d600b24e66399e1e37ed321e5fdaf Mon Sep 17 00:00:00 2001 From: Christopher Werner Date: Mon, 12 Oct 2026 15:27:54 +0200 Subject: [PATCH 163/312] preferSecondaryDominants: skip chords diatonic to the key romanNumeralFromChord(..., preferSecondaryDominants=True) relabelled chords that belong to the key, although the docstring says only chromatically altered chords are changed: I in major became V/IV, III in minor V/VI, and IV in minor V/VII. The substitution now only runs when the chord has a pitch outside the key's scale. In minor, the raised sixth and seventh count as diatonic. Fixes #2053 Co-Authored-By: Claude Opus 5.5 --- music21/roman.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/music21/roman.py b/music21/roman.py index d9de8dc1f4..28265807f4 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -917,6 +917,24 @@ def correctRNAlterationForMinor( return FigureTuple(figureTuple.degFromRefPitch, alter, rootAlterationString) +def _isDiatonicToKey(chordObj: chord.Chord, keyObj: key.Key) -> bool: + ''' + True if every pitch name in the chord is in the key's scale. + In minor, the raised sixth and seventh are in the scale too. + + >>> roman._isDiatonicToKey(chord.Chord('F4 A4 C5'), key.Key('c')) + True + >>> roman._isDiatonicToKey(chord.Chord('F4 A4 C5'), key.Key('f')) + False + ''' + scalePitches = keyObj.pitches + scaleNames = {p.name for p in scalePitches} + if keyObj.mode == 'minor': + for naturalSixthOrSeventh in scalePitches[5:7]: + scaleNames.add(naturalSixthOrSeventh.transpose('A1').name) + return all(p.name in scaleNames for p in chordObj.pitches) + + def romanNumeralFromChord( chordObj: chord.Chord, keyObj: key.Key|str|None = None, @@ -1142,7 +1160,8 @@ def romanNumeralFromChord( * the chord is a major triad or dominant seventh - * the chord is not diatonic to the primary key (i.e., chromatically altered) + * the chord is not diatonic to the primary key (i.e., chromatically altered; + in minor, the raised sixth and seventh count as diatonic) * the root of secondary key is diatonic to the primary key. @@ -1417,6 +1436,7 @@ def romanNumeralFromChord( preferSecondaryDominants and stepRoman != 'V' # ignore if already "primary" dominant and (chordObj.isDominantSeventh() or chordObj.isMajorTriad()) + and not _isDiatonicToKey(chordObj, keyObj) ): possibleSecondaryTonic = chordObj.root().transpose('P4').name degree = keyObj.getScaleDegreeFromPitch(possibleSecondaryTonic) @@ -4901,6 +4921,20 @@ def testXYZAreNotFigures(self): with self.assertRaises(RomanNumeralException): RomanNumeral(fig, 'C') + def testPreferSecondaryDominantsSkipsDiatonicChords(self): + # https://github.com/cuthbertLab/music21/issues/2053 + # in minor, the raised sixth and seventh count as diatonic + for chordString, keyString, figure in ( + ('C4 E4 G4', 'C', 'I'), + ('E-4 G4 B-4', 'c', 'III'), + ('F4 A4 C5', 'c', 'IV'), + ): + with self.subTest(chord=chordString, key=keyString): + rn = romanNumeralFromChord(chord.Chord(chordString), + keyString, + preferSecondaryDominants=True) + self.assertEqual(rn.figure, figure) + class TestExternal(unittest.TestCase): show = True From b293d927007a00a8256f7d53627b8ba057ebf629 Mon Sep 17 00:00:00 2001 From: Christopher Werner Date: Tue, 13 Oct 2026 11:20:21 +0200 Subject: [PATCH 164/312] Speed up _isDiatonicToKey and test both preferSecondaryDominants settings Compare each pitch with the key signature instead of building the scale with keyObj.pitches and transposing the sixth and seventh; same results, about 100 times faster. The test now checks every chord with and without preferSecondaryDominants, including two chords whose figure changes. Co-Authored-By: Claude Opus 5.5 --- music21/roman.py | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/music21/roman.py b/music21/roman.py index 28265807f4..4ca5b7f00c 100644 --- a/music21/roman.py +++ b/music21/roman.py @@ -922,17 +922,28 @@ def _isDiatonicToKey(chordObj: chord.Chord, keyObj: key.Key) -> bool: True if every pitch name in the chord is in the key's scale. In minor, the raised sixth and seventh are in the scale too. + AI-assisted (Claude). + >>> roman._isDiatonicToKey(chord.Chord('F4 A4 C5'), key.Key('c')) True >>> roman._isDiatonicToKey(chord.Chord('F4 A4 C5'), key.Key('f')) False ''' - scalePitches = keyObj.pitches - scaleNames = {p.name for p in scalePitches} - if keyObj.mode == 'minor': - for naturalSixthOrSeventh in scalePitches[5:7]: - scaleNames.add(naturalSixthOrSeventh.transpose('A1').name) - return all(p.name in scaleNames for p in chordObj.pitches) + # keyObj.pitches is slow; the key signature gives the same scale + signatureAlters = {p.step: p.alter for p in keyObj.alteredPitches} + outsideScale = [p for p in chordObj.pitches + if p.alter != signatureAlters.get(p.step, 0)] + if not outsideScale: + return True + if keyObj.mode != 'minor': + return False + stepOrder = 'CDEFGAB' + tonicIndex = stepOrder.index(keyObj.tonic.step) + sixthAndSeventhSteps = (stepOrder[(tonicIndex + 5) % 7], + stepOrder[(tonicIndex + 6) % 7]) + return all(p.step in sixthAndSeventhSteps + and p.alter == signatureAlters.get(p.step, 0) + 1 + for p in outsideScale) def romanNumeralFromChord( @@ -4924,16 +4935,21 @@ def testXYZAreNotFigures(self): def testPreferSecondaryDominantsSkipsDiatonicChords(self): # https://github.com/cuthbertLab/music21/issues/2053 # in minor, the raised sixth and seventh count as diatonic - for chordString, keyString, figure in ( - ('C4 E4 G4', 'C', 'I'), - ('E-4 G4 B-4', 'c', 'III'), - ('F4 A4 C5', 'c', 'IV'), + for chordString, keyString, plainFigure, preferredFigure in ( + ('C4 E4 G4', 'C', 'I', 'I'), + ('E-4 G4 B-4', 'c', 'III', 'III'), + ('F4 A4 C5', 'c', 'IV', 'IV'), + ('D4 F#4 A4', 'C', 'II', 'V/V'), + ('F4 A4 C5', 'f', 'I', 'V/iv'), ): with self.subTest(chord=chordString, key=keyString): - rn = romanNumeralFromChord(chord.Chord(chordString), - keyString, - preferSecondaryDominants=True) - self.assertEqual(rn.figure, figure) + cObj = chord.Chord(chordString) + plain = romanNumeralFromChord(cObj, keyString) + self.assertEqual(plain.figure, plainFigure) + preferred = romanNumeralFromChord(cObj, + keyString, + preferSecondaryDominants=True) + self.assertEqual(preferred.figure, preferredFigure) class TestExternal(unittest.TestCase): From d99e226b37ad4e66550d9f955b117e85f0eaccfa Mon Sep 17 00:00:00 2001 From: 72004 Date: Sat, 17 Oct 2026 18:32:04 +0500 Subject: [PATCH 165/312] Read MusicXML octave-change in without --- music21/_version.py | 2 +- music21/base.py | 2 +- music21/musicxml/test_xmlToM21.py | 8 ++++++++ music21/musicxml/xmlToM21.py | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/music21/_version.py b/music21/_version.py index b786868966..108249199e 100644 --- a/music21/_version.py +++ b/music21/_version.py @@ -47,7 +47,7 @@ ''' from __future__ import annotations -__version__ = '11.0.0b12' +__version__ = '11.0.0b13' def get_version_tuple(vv): v = vv.split('.') diff --git a/music21/base.py b/music21/base.py index 87c452e8ec..efd1b121c9 100644 --- a/music21/base.py +++ b/music21/base.py @@ -26,7 +26,7 @@ >>> music21.VERSION_STR -'11.0.0b12' +'11.0.0b13' Alternatively, after doing a complete import, these classes are available under the module "base": diff --git a/music21/musicxml/test_xmlToM21.py b/music21/musicxml/test_xmlToM21.py index 25af666c2f..bbac94dc85 100644 --- a/music21/musicxml/test_xmlToM21.py +++ b/music21/musicxml/test_xmlToM21.py @@ -510,6 +510,14 @@ def testInstrumentTranspositionA(self): self.assertEqual(str(i1.transposition), '') self.assertEqual(str(i2.transposition), '') + def testTransposeOctaveChangeWithoutDiatonic(self): + # https://github.com/cuthbertLab/music21/issues/2082 + MP = MeasureParser() + mxTranspose = self.EL('0' + '-1') + self.assertEqual(str(MP.xmlTransposeToInterval(mxTranspose)), + '') + def testInstrumentTranspositionB(self): from music21.musicxml import testPrimitive from music21 import converter diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 07d8c39ebf..7e524ff668 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -5906,7 +5906,8 @@ def xmlTransposeToInterval(self, mxTranspose): mxOctaveChange = mxTranspose.find('octave-change') if mxOctaveChange is not None: octaveChange = int(mxOctaveChange.text) * 12 - diatonicStep += 7 * int(mxOctaveChange.text) + if diatonicStep is not None: + diatonicStep += 7 * int(mxOctaveChange.text) # TODO: presently not dealing with # doubled one octave down from what is currently written From 36d9144c0fa0d6ff52134e1596b09f8bdb8d73aa Mon Sep 17 00:00:00 2001 From: wolfgang-aura <169568318+wolfgang-aura@users.noreply.github.com> Date: Mon, 19 Oct 2026 02:28:52 +0800 Subject: [PATCH 166/312] (AI) Braille: read a lone key signature in a keyboard segment heading --- music21/braille/segment.py | 12 +++++------- music21/braille/test.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/music21/braille/segment.py b/music21/braille/segment.py index eb493b4a65..53dece6ee7 100644 --- a/music21/braille/segment.py +++ b/music21/braille/segment.py @@ -1328,13 +1328,11 @@ def extractHeading(self): break self.allKeyPairs.popleft() if useKey.affinity == Affinity.SIGNATURE: - try: - keySignature, timeSignature = useElement[0], useElement[1] - except IndexError: - if isinstance(useElement, key.KeySignature): - keySignature = useElement[0] - else: - timeSignature = useElement[0] + for el in useElement: + if keySignature is None and isinstance(el, key.KeySignature): + keySignature = el + elif timeSignature is None and isinstance(el, meter.TimeSignature): + timeSignature = el elif useKey.affinity == Affinity.TTEXT: tempoText = useElement[0] elif useKey.affinity == Affinity.MMARK: diff --git a/music21/braille/test.py b/music21/braille/test.py index 24e7fc690e..cc5d1d81b9 100644 --- a/music21/braille/test.py +++ b/music21/braille/test.py @@ -10098,6 +10098,36 @@ def test_example26_6(self): ⠁⠀⠨⠜⠨⠫⠬⠱⠬⠀⠀⠨⠝⠨⠤ ⠀⠀⠸⠜⠸⠫⠬⠳⠸⠤⠀⠸⠝⠤⠀ ''' + + def test_keyboardInteriorKeyChangeWithoutMeterChange(self): + # https://github.com/cuthbertLab/music21/issues/1986 + def buildPart(pitchNames): + part = stream.PartStaff() + for i, pitchName in enumerate(pitchNames, 1): + m = stream.Measure(number=i) + if i == 1: + m.append(key.KeySignature(0)) + m.append(meter.TimeSignature('4/4')) + if i == 5: + m.append(BrailleSegmentDivision()) + m.append(key.KeySignature(2)) + m.append(note.Note(pitchName, quarterLength=4.0)) + part.append(m) + return part + + self.method = keyboardPartsToBraille + score = stream.Score() + score.insert(0, buildPart(['C5', 'D5', 'E5', 'F5', 'G5', 'A5', 'B5', 'C6'])) + score.insert(0, buildPart(['C3', 'D3', 'E3', 'F3', 'G3', 'A3', 'B3', 'C4'])) + self.s = score + self.b = ''' + ⠀⠀⠀⠀⠀⠀⠼⠙⠲⠀⠀⠀⠀⠀⠀ + ⠁⠀⠨⠜⠨⠽⠀⠨⠵⠀⠨⠯⠀⠨⠿ + ⠀⠀⠸⠜⠸⠽⠀⠸⠵⠀⠸⠯⠀⠸⠿ + ⠀⠀⠀⠀⠀⠀⠀⠩⠩⠀⠀⠀⠀⠀⠀ + ⠑⠀⠨⠜⠨⠷⠀⠨⠮⠀⠨⠾⠀⠰⠽ + ⠀⠀⠸⠜⠸⠷⠀⠸⠮⠀⠸⠾⠀⠐⠽ + ''' # ------------------------------------------------------------------------------ From 151b74348fcfe954b8758117a331924fd3b0a99b Mon Sep 17 00:00:00 2001 From: 72004 Date: Mon, 19 Oct 2026 23:20:22 +0500 Subject: [PATCH 167/312] Order TimespanTree.allParts() by the source score allParts() sorted the parts by their instrument's partId, which raised a TypeError when the parts had no partId (as with MIDI imports) and put P10 before P2. Use the order of the parts in the source score instead, falling back to partId for parts that are not in the source. Fixes #2074 --- music21/analysis/reduceChords.py | 25 +++++++++++++++++++++++++ music21/tree/timespanTree.py | 32 +++++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/music21/analysis/reduceChords.py b/music21/analysis/reduceChords.py index 6913bfc8f7..2b82ea5ce7 100644 --- a/music21/analysis/reduceChords.py +++ b/music21/analysis/reduceChords.py @@ -737,6 +737,31 @@ def testSimpleMeasure(self): for c in [c1, c2, c3]: s.append(c) + def testPartsWithoutPartId(self): + # parts from MIDI files often have no partId; this used to raise + # a TypeError when sorting the parts + from music21 import instrument + upper = stream.Part([instrument.Instrument(), + stream.Measure([chord.Chord('C4 E4', type='whole')])]) + lower = stream.Part([instrument.Instrument(), + stream.Measure([note.Note('G3', type='whole')])]) + score = stream.Score([upper, lower]) + scoreTree = score.asTimespans() + self.assertEqual(scoreTree.allParts(), [upper, lower]) + ChordReducer().run(score) + + def testPartsOrderedAsInScore(self): + # the last part is used as the bass, so the order must follow the + # score even when partIds do not sort that way + from music21 import instrument + parts = [] + for partId in ('P2', 'P10', 'P1'): + inst = instrument.Instrument() + inst.partId = partId + parts.append(stream.Part([inst, note.Note('C4', type='whole')])) + score = stream.Score(parts) + self.assertEqual(score.asTimespans().allParts(), parts) + class TestExternal(unittest.TestCase): show = True diff --git a/music21/tree/timespanTree.py b/music21/tree/timespanTree.py index 1a2187116d..48986d3422 100644 --- a/music21/tree/timespanTree.py +++ b/music21/tree/timespanTree.py @@ -737,11 +737,33 @@ def unwrapVerticalities(verticalities): # PUBLIC PROPERTIES # def allParts(self): - parts = set() - for timespan in self: - parts.add(timespan.part) - parts = sorted(parts, key=lambda x: x.getInstrument().partId) - return parts + ''' + Returns a list of the parts that the timespans in this tree belong to. + + If the tree's source has parts (such as a Score), the parts are returned + in the order they appear in the source. Other parts come after them, + sorted by the partId of their instrument. + + Parts do not need a partId: + + >>> upper = stream.Part([instrument.Instrument(), note.Note('E4')]) + >>> lower = stream.Part([instrument.Instrument(), note.Note('C3')]) + >>> score = stream.Score([upper, lower]) + >>> scoreTree = score.asTimespans() + >>> scoreTree.allParts() == [upper, lower] + True + ''' + parts = {timespan.part for timespan in self} + source = self.source + sourceOrder = {} + if source is not None and hasattr(source, 'parts'): + sourceOrder = {id(part): i for i, part in enumerate(source.parts)} + + def sortKey(part): + partId = part.getInstrument().partId + return (sourceOrder.get(id(part), len(sourceOrder)), partId is None, str(partId)) + + return sorted(parts, key=sortKey) def maximumOverlap(self): ''' From 5836f38cd55074ace4adc4e649a3148faf166cde Mon Sep 17 00:00:00 2001 From: Sankalp Thakur Date: Mon, 19 Oct 2026 23:11:55 +0400 Subject: [PATCH 168/312] tsvConverter: skip blank rows and strip cell whitespace on import A trailing blank or tab-only line in a DCML TSV file, such as one left by copy-pasting a table from GitHub, raised a bare IndexError (or a ValueError from int('')) in populateFromRow. Padded cells from the same copy-paste left every header name unmatched, so the file then failed in key.Key(''). _importTsv now strips each cell and drops rows with no content. A file with no chord rows after the header crashed in prepStream at chordList[0]; it now raises TsvException, a Music21Exception. Fixes #1541 --- music21/romanText/tsvConverter.py | 47 +++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/music21/romanText/tsvConverter.py b/music21/romanText/tsvConverter.py index 9b81ac4580..0891dabaee 100644 --- a/music21/romanText/tsvConverter.py +++ b/music21/romanText/tsvConverter.py @@ -4,7 +4,7 @@ # # Authors: Mark Gotham # -# Copyright: Copyright © 2019 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2019-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -25,6 +25,7 @@ from music21 import chord from music21 import common from music21 import environment +from music21 import exceptions21 from music21 import harmony from music21 import key from music21 import metadata @@ -35,6 +36,11 @@ environLocal = environment.Environment() + +class TsvException(exceptions21.Music21Exception): + pass + + # A coercion callable maps a raw TSV string cell to a typed value (str, int, # float, or a fraction-aware float via _float_or_frac). type Coercer = t.Callable[[str], t.Any] @@ -566,9 +572,10 @@ def _importTsv(self) -> list[list[str]]: with open(fileName, 'r', encoding='utf-8') as f: tsvreader = csv.reader(f, delimiter='\t', quotechar='"') + rows = ([cell.strip() for cell in row] for row in tsvreader) # The first row is the header - self._get_heading_indices(next(tsvreader)) - return list(tsvreader) + self._get_heading_indices(next(rows)) + return [row for row in rows if any(row)] def _makeTabChord(self, row: list[str]) -> TabChordBase: ''' @@ -653,6 +660,8 @@ def prepStream(self) -> stream.Score: Works like the .template() method, except that we don't have a score to base the template on as such. ''' + if not self.chordList: + raise TsvException(f'no chord rows found in {self.tsvFileName}') s = stream.Score() p = stream.Part() m: stream.Measure|None = None @@ -1369,6 +1378,38 @@ def _test_ending_contents( _test_ending_contents(first_ending, ['2a']) _test_ending_contents(second_ending, ['2b']) + def testWhitespaceRows(self) -> None: + ''' + AI-assisted (Claude) regression coverage for whitespace in DCML TSV files. + ''' + # https://github.com/cuthbertLab/music21/issues/1541 + import os + path = common.getSourceFilePath() / 'romanText' / 'tsvEg_v2_repeats.tsv' + text = path.read_text(encoding='utf-8') + clean = TsvHandler(path, dcml_version=2).toM21Stream() + expected = [rn.figure for rn in clean[roman.RomanNumeral]] + padded = '\n'.join( + '\t'.join(f' {cell} ' for cell in line.split('\t')) for line in text.splitlines() + ) + variants = { + 'blank line': text + '\n', + 'tabs only': text + '\t\t\t\t\t\n', + 'padded cells': padded + '\n\t\t\t\t\t\t\n', + } + tempFile = environment.Environment().getTempFile() + try: + for name, variant in variants.items(): + with open(tempFile, 'w', encoding='utf-8') as f: + f.write(variant) + parsed = TsvHandler(tempFile, dcml_version=2).toM21Stream() + self.assertEqual([rn.figure for rn in parsed[roman.RomanNumeral]], expected, name) + with open(tempFile, 'w', encoding='utf-8') as f: + f.write(text.split('\n', 1)[0] + '\n\n') + with self.assertRaises(TsvException): + TsvHandler(tempFile, dcml_version=2).toM21Stream() + finally: + os.remove(tempFile) + # ------------------------------------------------------------------------------ From 05f7f765f63b6e2b744f32360b435c62c2d92f95 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 20 Oct 2026 13:14:24 -1000 Subject: [PATCH 169/312] Remove uv.lock from repo A library should not pin its dependency tree; CI now resolves fresh from pyproject.toml (uv sync without --locked). AI-assisted (Claude) --- .github/workflows/maincheck.yml | 12 +- .gitignore | 1 + uv.lock | 3338 ------------------------------- 3 files changed, 5 insertions(+), 3346 deletions(-) delete mode 100644 uv.lock diff --git a/.github/workflows/maincheck.yml b/.github/workflows/maincheck.yml index 45ece16361..9f22645724 100644 --- a/.github/workflows/maincheck.yml +++ b/.github/workflows/maincheck.yml @@ -31,10 +31,9 @@ jobs: enable-cache: true cache-dependency-glob: | pyproject.toml - uv.lock - name: Install dependencies run: | - uv sync --locked + uv sync - name: Install Ubuntu dependencies run: ./.github/scripts/install_ubuntu_deps.sh - name: Setup Lilypond @@ -66,10 +65,9 @@ jobs: enable-cache: true cache-dependency-glob: | pyproject.toml - uv.lock - name: Install dependencies run: | - uv sync --locked + uv sync - name: Lint with pylint run: | uv run pylint -j0 music21 @@ -89,10 +87,9 @@ jobs: enable-cache: true cache-dependency-glob: | pyproject.toml - uv.lock - name: Install dependencies run: | - uv sync --locked + uv sync - name: Code-style etc. with Ruff run: | uv run ruff check music21 @@ -112,10 +109,9 @@ jobs: enable-cache: true cache-dependency-glob: | pyproject.toml - uv.lock - name: Install dependencies run: | - uv sync --locked + uv sync - name: Type-check all modules with mypy run: | uv run mypy music21 diff --git a/.gitignore b/.gitignore index 635ab32116..a6d2259fb4 100644 --- a/.gitignore +++ b/.gitignore @@ -97,3 +97,4 @@ music21/monkeytype.sqlite3 # agents .agents/worktrees .claude/worktrees +uv.lock diff --git a/uv.lock b/uv.lock deleted file mode 100644 index a50d86a647..0000000000 --- a/uv.lock +++ /dev/null @@ -1,3338 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.12" -resolution-markers = [ - "python_full_version >= '3.15'", - "python_full_version == '3.14.*'", - "python_full_version < '3.14'", -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, -] - -[[package]] -name = "annotated-doc" -version = "0.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, -] - -[[package]] -name = "anyio" -version = "4.14.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, -] - -[[package]] -name = "appnope" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170, upload-time = "2024-02-06T09:43:11.258Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321, upload-time = "2024-02-06T09:43:09.663Z" }, -] - -[[package]] -name = "argon2-cffi" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "argon2-cffi-bindings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706, upload-time = "2025-06-03T06:55:32.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657, upload-time = "2025-06-03T06:55:30.804Z" }, -] - -[[package]] -name = "argon2-cffi-bindings" -version = "25.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441, upload-time = "2025-07-30T10:02:05.147Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393, upload-time = "2025-07-30T10:01:40.97Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328, upload-time = "2025-07-30T10:01:41.916Z" }, - { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269, upload-time = "2025-07-30T10:01:42.716Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558, upload-time = "2025-07-30T10:01:43.943Z" }, - { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364, upload-time = "2025-07-30T10:01:44.887Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637, upload-time = "2025-07-30T10:01:46.225Z" }, - { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934, upload-time = "2025-07-30T10:01:47.203Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158, upload-time = "2025-07-30T10:01:48.341Z" }, - { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597, upload-time = "2025-07-30T10:01:49.112Z" }, - { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231, upload-time = "2025-07-30T10:01:49.92Z" }, - { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121, upload-time = "2025-07-30T10:01:50.815Z" }, - { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177, upload-time = "2025-07-30T10:01:51.681Z" }, - { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090, upload-time = "2025-07-30T10:01:53.184Z" }, - { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246, upload-time = "2025-07-30T10:01:54.145Z" }, - { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126, upload-time = "2025-07-30T10:01:55.074Z" }, - { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343, upload-time = "2025-07-30T10:01:56.007Z" }, - { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777, upload-time = "2025-07-30T10:01:56.943Z" }, - { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180, upload-time = "2025-07-30T10:01:57.759Z" }, - { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715, upload-time = "2025-07-30T10:01:58.56Z" }, - { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149, upload-time = "2025-07-30T10:01:59.329Z" }, -] - -[[package]] -name = "arrow" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "python-dateutil" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931, upload-time = "2025-10-18T17:46:46.761Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, -] - -[[package]] -name = "astroid" -version = "4.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/63/0adf26577da5eff6eb7a177876c1cfa213856be9926a000f65c4add9692b/astroid-4.0.4.tar.gz", hash = "sha256:986fed8bcf79fb82c78b18a53352a0b287a73817d6dbcfba3162da36667c49a0", size = 406358, upload-time = "2026-02-07T23:35:07.509Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/cf/1c5f42b110e57bc5502eb80dbc3b03d256926062519224835ef08134f1f9/astroid-4.0.4-py3-none-any.whl", hash = "sha256:52f39653876c7dec3e3afd4c2696920e05c83832b9737afc21928f2d2eb7a753", size = 276445, upload-time = "2026-02-07T23:35:05.344Z" }, -] - -[[package]] -name = "asttokens" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, -] - -[[package]] -name = "async-lru" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332, upload-time = "2026-03-19T01:04:32.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403, upload-time = "2026-03-19T01:04:30.883Z" }, -] - -[[package]] -name = "attrs" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, -] - -[[package]] -name = "backports-zstd" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/b1/36a5182ce1d8ef9ef32bff69037bd28b389bbdb66338f8069e61da7028cb/backports_zstd-1.3.0.tar.gz", hash = "sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2", size = 997138, upload-time = "2025-12-29T17:28:06.143Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/72/d4/356da49d3053f4bc50e71a8535631b57bc9ca4e8c6d2442e073e0ab41c44/backports_zstd-1.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f4a292e357f3046d18766ce06d990ccbab97411708d3acb934e63529c2ea7786", size = 435972, upload-time = "2025-12-29T17:26:18.752Z" }, - { url = "https://files.pythonhosted.org/packages/30/8f/dbe389e60c7e47af488520f31a4aa14028d66da5bf3c60d3044b571eb906/backports_zstd-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb4c386f38323698991b38edcc9c091d46d4713f5df02a3b5c80a28b40e289ea", size = 362124, upload-time = "2025-12-29T17:26:19.995Z" }, - { url = "https://files.pythonhosted.org/packages/55/4b/173beafc99e99e7276ce008ef060b704471e75124c826bc5e2092815da37/backports_zstd-1.3.0-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f52523d2bdada29e653261abdc9cfcecd9e5500d305708b7e37caddb24909d4e", size = 506378, upload-time = "2025-12-29T17:26:21.855Z" }, - { url = "https://files.pythonhosted.org/packages/df/c8/3f12a411d9a99d262cdb37b521025eecc2aa7e4a93277be3f4f4889adb74/backports_zstd-1.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3321d00beaacbd647252a7f581c1e1cdbdbda2407f2addce4bfb10e8e404b7c7", size = 476201, upload-time = "2025-12-29T17:26:23.047Z" }, - { url = "https://files.pythonhosted.org/packages/43/dc/73c090e4a2d5671422512e1b6d276ca6ea0cc0c45ec4634789106adc0d66/backports_zstd-1.3.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88f94d238ef36c639c0ae17cf41054ce103da9c4d399c6a778ce82690d9f4919", size = 581659, upload-time = "2025-12-29T17:26:24.189Z" }, - { url = "https://files.pythonhosted.org/packages/08/4f/11bfcef534aa2bf3f476f52130217b45337f334d8a287edb2e06744a6515/backports_zstd-1.3.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:97d8c78fe20c7442c810adccfd5e3ea6a4e6f4f1fa4c73da2bc083260ebead17", size = 640388, upload-time = "2025-12-29T17:26:25.47Z" }, - { url = "https://files.pythonhosted.org/packages/71/17/8faea426d4f49b63238bdfd9f211a9f01c862efe0d756d3abeb84265a4e2/backports_zstd-1.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eefda80c3dbfbd924f1c317e7b0543d39304ee645583cb58bae29e19f42948ed", size = 494173, upload-time = "2025-12-29T17:26:26.736Z" }, - { url = "https://files.pythonhosted.org/packages/ba/9d/901f19ac90f3cd999bdcfb6edb4d7b4dc383dfba537f06f533fc9ac4777b/backports_zstd-1.3.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2ab5d3b5a54a674f4f6367bb9e0914063f22cd102323876135e9cc7a8f14f17e", size = 568628, upload-time = "2025-12-29T17:26:28.12Z" }, - { url = "https://files.pythonhosted.org/packages/60/39/4d29788590c2465a570c2fae49dbff05741d1f0c8e4a0fb2c1c310f31804/backports_zstd-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7558fb0e8c8197c59a5f80c56bf8f56c3690c45fd62f14e9e2081661556e3e64", size = 482233, upload-time = "2025-12-29T17:26:29.399Z" }, - { url = "https://files.pythonhosted.org/packages/d9/4b/24c7c9e8ef384b19d515a7b1644a500ceb3da3baeff6d579687da1a0f62b/backports_zstd-1.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:27744870e38f017159b9c0241ea51562f94c7fefcfa4c5190fb3ec4a65a7fc63", size = 509806, upload-time = "2025-12-29T17:26:30.605Z" }, - { url = "https://files.pythonhosted.org/packages/3f/7e/7ba1aeecf0b5859f1855c0e661b4559566b64000f0627698ebd9e83f2138/backports_zstd-1.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b099750755bb74c280827c7d68de621da0f245189082ab48ff91bda0ec2db9df", size = 586037, upload-time = "2025-12-29T17:26:32.201Z" }, - { url = "https://files.pythonhosted.org/packages/4a/1a/18f0402b36b9cfb0aea010b5df900cfd42c214f37493561dba3abac90c4e/backports_zstd-1.3.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5434e86f2836d453ae3e19a2711449683b7e21e107686838d12a255ad256ca99", size = 566220, upload-time = "2025-12-29T17:26:33.5Z" }, - { url = "https://files.pythonhosted.org/packages/dc/d9/44c098ab31b948bbfd909ec4ae08e1e44c5025a2d846f62991a62ab3ebea/backports_zstd-1.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:407e451f64e2f357c9218f5be4e372bb6102d7ae88582d415262a9d0a4f9b625", size = 630847, upload-time = "2025-12-29T17:26:35.273Z" }, - { url = "https://files.pythonhosted.org/packages/30/33/e74cb2cfb162d2e9e00dad8bcdf53118ca7786cfd467925d6864732f79cc/backports_zstd-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:58a071f3c198c781b2df801070290b7174e3ff61875454e9df93ab7ea9ea832b", size = 498665, upload-time = "2025-12-29T17:26:37.123Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a9/67a24007c333ed22736d5cd79f1aa1d7209f09be772ff82a8fd724c1978e/backports_zstd-1.3.0-cp312-cp312-win32.whl", hash = "sha256:21a9a542ccc7958ddb51ae6e46d8ed25d585b54d0d52aaa1c8da431ea158046a", size = 288809, upload-time = "2025-12-29T17:26:38.373Z" }, - { url = "https://files.pythonhosted.org/packages/42/24/34b816118ea913debb2ea23e71ffd0fb2e2ac738064c4ac32e3fb62c18bb/backports_zstd-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:89ea8281821123b071a06b30b80da8e4d8a2b40a4f57315a19850337a21297ac", size = 313815, upload-time = "2025-12-29T17:26:39.665Z" }, - { url = "https://files.pythonhosted.org/packages/4e/2f/babd02c9fc4ca35376ada7c291193a208165c7be2455f0f98bc1e1243f31/backports_zstd-1.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:f6843ecb181480e423b02f60fe29e393cbc31a95fb532acdf0d3a2c87bd50ce3", size = 288927, upload-time = "2025-12-29T17:26:40.923Z" }, - { url = "https://files.pythonhosted.org/packages/0c/7d/53e8da5950cdfc5e8fe23efd5165ce2f4fed5222f9a3292e0cdb03dd8c0d/backports_zstd-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e86e03e3661900955f01afed6c59cae9baa63574e3b66896d99b7de97eaffce9", size = 435463, upload-time = "2025-12-29T17:26:42.152Z" }, - { url = "https://files.pythonhosted.org/packages/da/78/f98e53870f7404071a41e3d04f2ff514302eeeb3279d931d02b220f437aa/backports_zstd-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:41974dcacc9824c1effe1c8d2f9d762bcf47d265ca4581a3c63321c7b06c61f0", size = 361740, upload-time = "2025-12-29T17:26:43.377Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ed/2c64706205a944c9c346d95c17f632d4e3468db3ce60efb6f5caa7c0dcae/backports_zstd-1.3.0-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:3090a97738d6ce9545d3ca5446df43370928092a962cbc0153e5445a947e98ed", size = 505651, upload-time = "2025-12-29T17:26:44.495Z" }, - { url = "https://files.pythonhosted.org/packages/7b/7b/22998f691dc6e0c7e6fa81d611eb4b1f6a72fb27327f322366d4a7ca8fb3/backports_zstd-1.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ddc874638abf03ea1ff3b0525b4a26a8d0adf7cb46a448c3449f08e4abc276b3", size = 475859, upload-time = "2025-12-29T17:26:45.722Z" }, - { url = "https://files.pythonhosted.org/packages/0b/78/0cde898339a339530e5f932634872d2d64549969535447a48d3b98959e11/backports_zstd-1.3.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:db609e57b8ed88b3472930c87e93c08a4bbd5ffeb94608cd9c7c6f0ac0e166c6", size = 581339, upload-time = "2025-12-29T17:26:46.93Z" }, - { url = "https://files.pythonhosted.org/packages/e2/1d/e0973e0eebe678c12c146473af2c54cda8a3e63b179785ca1a20727ad69c/backports_zstd-1.3.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5f13033a3dd95f323c067199f2e61b4589a7880188ef4ef356c7ffbdb78a9f11", size = 642182, upload-time = "2025-12-29T17:26:48.545Z" }, - { url = "https://files.pythonhosted.org/packages/82/a2/ac67e79e137eb98aead66c7162bafe3cffcb82ef9cdeb6367ec18d88fbce/backports_zstd-1.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c4c7bcda5619a754726e7f5b391827f5efbe4bed8e62e9ec7490d42bff18aa6", size = 490807, upload-time = "2025-12-29T17:26:49.789Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e9/3514b1d065801ae7dce05246e9389003ed8fb1d7c3d71f85aa07a80f41e6/backports_zstd-1.3.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:884a94c40f27affe986f394f219a4fd3cbbd08e1cff2e028d29d467574cd266e", size = 566103, upload-time = "2025-12-29T17:26:51.062Z" }, - { url = "https://files.pythonhosted.org/packages/1b/03/10ddb54cbf032e5fe390c0776d3392611b1fc772d6c3cb5a9bcdff4f915f/backports_zstd-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497f5765126f11a5b3fd8fedfdae0166d1dd867e7179b8148370a3313d047197", size = 481614, upload-time = "2025-12-29T17:26:52.255Z" }, - { url = "https://files.pythonhosted.org/packages/5c/13/21efa7f94c41447f43aee1563b05fc540a235e61bce4597754f6c11c2e97/backports_zstd-1.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a6ff6769948bb29bba07e1c2e8582d5a9765192a366108e42d6581a458475881", size = 509207, upload-time = "2025-12-29T17:26:53.496Z" }, - { url = "https://files.pythonhosted.org/packages/de/e7/12da9256d9e49e71030f0ff75e9f7c258e76091a4eaf5b5f414409be6a57/backports_zstd-1.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1623e5bff1acd9c8ef90d24fc548110f20df2d14432bfe5de59e76fc036824ef", size = 585765, upload-time = "2025-12-29T17:26:54.99Z" }, - { url = "https://files.pythonhosted.org/packages/24/bf/59ca9cb4e7be1e59331bb792e8ef1331828efe596b1a2f8cbbc4e3f70d75/backports_zstd-1.3.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:622c28306dcc429c8f2057fc4421d5722b1f22968d299025b35d71b50cfd4e03", size = 563852, upload-time = "2025-12-29T17:26:56.371Z" }, - { url = "https://files.pythonhosted.org/packages/7c/ee/5a3eaed9a73bdf2c35dc0c7adc0616a99588e0de28f5ab52f3e0caaaa96f/backports_zstd-1.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09a2785e410ed2e812cb39b684ef5eb55083a5897bfd0e6f5de3bbd2c6345f70", size = 632549, upload-time = "2025-12-29T17:26:57.598Z" }, - { url = "https://files.pythonhosted.org/packages/75/b9/c823633afc48a1ac56d6ad34289c8f51b0234685142531bfa8197ca91777/backports_zstd-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ade1f4127fdbe36a02f8067d75aa79c1ea1c8a306bf63c7b818bb7b530e1beaa", size = 495104, upload-time = "2025-12-29T17:26:58.826Z" }, - { url = "https://files.pythonhosted.org/packages/a3/8f/6f7030f18fa7307f87b0f57108a50a3a540b6350e2486d1739c0567629a3/backports_zstd-1.3.0-cp313-cp313-win32.whl", hash = "sha256:668e6fb1805b825cb7504c71436f7b28d4d792bb2663ee901ec9a2bb15804437", size = 288447, upload-time = "2025-12-29T17:27:00.036Z" }, - { url = "https://files.pythonhosted.org/packages/a2/82/b1df1bbbe4e6d3ffd364d0bcffdeb6c4361115c1eccd91238dbdd0c07fec/backports_zstd-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:385bdadf0ea8fe6ba780a95e4c7d7f018db7bafdd630932f0f9f0fad05d608ff", size = 313664, upload-time = "2025-12-29T17:27:01.267Z" }, - { url = "https://files.pythonhosted.org/packages/45/0f/60918fe4d3f2881de8f4088d73be4837df9e4c6567594109d355a2d548b6/backports_zstd-1.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:4321a8a367537224b3559fe7aeb8012b98aea2a60a737e59e51d86e2e856fe0a", size = 288678, upload-time = "2025-12-29T17:27:02.506Z" }, - { url = "https://files.pythonhosted.org/packages/a7/b9/35f423c0bcd85020d5e7be6ab8d7517843e3e4441071beb5c3bd8c5216cb/backports_zstd-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:10057d66fa4f0a7d3f6419ffb84b4fe61088da572e3ac4446134a1c8089e4166", size = 436155, upload-time = "2025-12-29T17:27:03.859Z" }, - { url = "https://files.pythonhosted.org/packages/f6/14/e504daea24e8916f14ecbc223c354b558d8410cfc846606668ab91d96b38/backports_zstd-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4abf29d706ba05f658ca0247eb55675bcc00e10f12bca15736e45b05f1f2d2dc", size = 362436, upload-time = "2025-12-29T17:27:05.076Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f7/06e178dbab7edb88c2872aebd68b54137e07a169eba1aeedf614014f7036/backports_zstd-1.3.0-cp313-cp313t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:127b0d73c745b0684da3d95c31c0939570810dad8967dfe8231eea8f0e047b2f", size = 507600, upload-time = "2025-12-29T17:27:06.254Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f1/2ce499b81c4389d6fa1eeea7e76f6e0bad48effdbb239da7cbcdaaf24b76/backports_zstd-1.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0205ef809fb38bb5ca7f59fa03993596f918768b9378fb7fbd8a68889a6ce028", size = 475496, upload-time = "2025-12-29T17:27:07.939Z" }, - { url = "https://files.pythonhosted.org/packages/18/1e/c82a586f2866aabf3a601a521af3c58756d83d98b724fda200016ac5e7e2/backports_zstd-1.3.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1c389b667b0b07915781aa28beabf2481f11a6062a1a081873c4c443b98601a7", size = 580919, upload-time = "2025-12-29T17:27:09.1Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a3/eb5d9b7c4cb69d1b8ccd011abe244ba6815693b70bed07ed4b77ddda4535/backports_zstd-1.3.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8e7ac5ef693d49d6fb35cd7bbb98c4762cfea94a8bd2bf2ab112027004f70b11", size = 639913, upload-time = "2025-12-29T17:27:10.433Z" }, - { url = "https://files.pythonhosted.org/packages/11/2c/7296b99df79d9f31174a99c81c1964a32de8996ce2b3068f5bc66b413615/backports_zstd-1.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d5543945aae2a76a850b23f283249424f535de6a622d6002957b7d971e6a36d", size = 494800, upload-time = "2025-12-29T17:27:11.59Z" }, - { url = "https://files.pythonhosted.org/packages/f9/fc/b8ae6e104ba72d20cd5f9dfd9baee36675e89c81d432434927967114f30f/backports_zstd-1.3.0-cp313-cp313t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e38be15ebce82737deda2c9410c1f942f1df9da74121049243a009810432db75", size = 570396, upload-time = "2025-12-29T17:27:13.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/56/60a7a9de7a5bc951ea1106358b413c95183c93480394f3abc541313c8679/backports_zstd-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3e3f58c76f4730607a4e0130d629173aa114ae72a5c8d3d5ad94e1bf51f18d8", size = 481980, upload-time = "2025-12-29T17:27:14.317Z" }, - { url = "https://files.pythonhosted.org/packages/4b/bb/93fc1e8e81b8ecba58b0e53a14f7b44375cf837db6354410998f0c4cb6ff/backports_zstd-1.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:b808bf889722d889b792f7894e19c1f904bb0e9092d8c0eb0787b939b08bad9a", size = 511358, upload-time = "2025-12-29T17:27:15.669Z" }, - { url = "https://files.pythonhosted.org/packages/ae/0f/b165c2a6080d22306975cd86ce97270208493f31a298867e343110570370/backports_zstd-1.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f7be27d56f2f715bcd252d0c65c232146d8e1e039c7e2835b8a3ad3dc88bc508", size = 585492, upload-time = "2025-12-29T17:27:16.986Z" }, - { url = "https://files.pythonhosted.org/packages/26/76/85b4bde76e982b24a7eb57a2fb9868807887bef4d2114a3654a6530a67ef/backports_zstd-1.3.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:cbe341c7fcc723893663a37175ba859328b907a4e6d2d40a4c26629cc55efb67", size = 568309, upload-time = "2025-12-29T17:27:18.28Z" }, - { url = "https://files.pythonhosted.org/packages/83/64/9490667827a320766fb883f358a7c19171fdc04f19ade156a8c341c36967/backports_zstd-1.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:b4116a9e12dfcd834dd9132cf6a94657bf0d328cba5b295f26de26ea0ae1adc8", size = 630518, upload-time = "2025-12-29T17:27:19.525Z" }, - { url = "https://files.pythonhosted.org/packages/ea/43/258587233b728bbff457bdb0c52b3e08504c485a8642b3daeb0bdd5a76bc/backports_zstd-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1049e804cc8754290b24dab383d4d6ed0b7f794ad8338813ddcb3907d15a89d0", size = 499429, upload-time = "2025-12-29T17:27:21.063Z" }, - { url = "https://files.pythonhosted.org/packages/32/04/cfab76878f360f124dbb533779e1e4603c801a0f5ada72ae5c742b7c4d7d/backports_zstd-1.3.0-cp313-cp313t-win32.whl", hash = "sha256:7d3f0f2499d2049ec53d2674c605a4b3052c217cc7ee49c05258046411685adc", size = 289389, upload-time = "2025-12-29T17:27:22.287Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ff/dbcfb6c9c922ab6d98f3d321e7d0c7b34ecfa26f3ca71d930fe1ef639737/backports_zstd-1.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:eb2f8fab0b1ea05148394cb34a9e543a43477178765f2d6e7c84ed332e34935e", size = 314776, upload-time = "2025-12-29T17:27:23.458Z" }, - { url = "https://files.pythonhosted.org/packages/01/4b/82e4baae3117806639fe1c693b1f2f7e6133a7cefd1fa2e38018c8edcd68/backports_zstd-1.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c66ad9eb5bfbe28c2387b7fc58ddcdecfb336d6e4e60bcba1694a906c1f21a6c", size = 289315, upload-time = "2025-12-29T17:27:24.601Z" }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.14.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, -] - -[[package]] -name = "bleach" -version = "6.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/3c/e12ac860709702bd5ebeb9b56a4fe334f1001246ee1b8f2b7ee28912df7d/bleach-6.4.0.tar.gz", hash = "sha256:4202482733d85cedd04e59fcb2f89f4e4c7c385a78d3c3c23c30446843a37452", size = 204857, upload-time = "2026-06-05T13:01:13.734Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/9d/40b6267367182187139a4000b82a3b287d84d745bccd808e75d916920e9d/bleach-6.4.0-py3-none-any.whl", hash = "sha256:4b6b6a54fff2e69a3dde9d21cc6301220bee3c3cb792187d11403fd795031081", size = 165109, upload-time = "2026-06-05T13:01:12.504Z" }, -] - -[package.optional-dependencies] -css = [ - { name = "tinycss2" }, -] - -[[package]] -name = "certifi" -version = "2026.4.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, -] - -[[package]] -name = "cffi" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycparser", marker = "implementation_name != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" }, - { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" }, - { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, - { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, - { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, - { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, - { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" }, - { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, -] - -[[package]] -name = "chardet" -version = "7.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/b6/9df434a8eeba2e6628c465a1dfa31034228ef79b26f76f46278f4ef7e49d/chardet-7.4.3.tar.gz", hash = "sha256:cc1d4eb92a4ec1c2df3b490836ffa46922e599d34ce0bb75cf41fd2bf6303d56", size = 784800, upload-time = "2026-04-13T21:33:39.803Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/61/33/29de185079e6675c3f375546e30a559b7ddc75ce972f18d6e566cd9ea4eb/chardet-7.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:75d3c65cc16bddf40b8da1fd25ba84fca5f8070f2b14e86083653c1c85aee971", size = 874870, upload-time = "2026-04-13T21:33:05.977Z" }, - { url = "https://files.pythonhosted.org/packages/9c/2f/4c5af01fd1a7506a1d5375403d68925eac70289229492db5aa68b58103d8/chardet-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:29af5999f654e8729d251f1724a62b538b1262d9292cccaefddf8a02aae1ef6a", size = 854859, upload-time = "2026-04-13T21:33:07.381Z" }, - { url = "https://files.pythonhosted.org/packages/36/21/edb36ad5dfa48d7f8eed97ab43931ecdaa8c15166c21b1d614967e49d681/chardet-7.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:626f00299ad62dfe937058a09572beed442ccc7b58f87aa667949b20fd3db235", size = 875032, upload-time = "2026-04-13T21:33:08.741Z" }, - { url = "https://files.pythonhosted.org/packages/e5/59/a32a241d861cf180853a11c8e5a67641cb1b2af13c3a5ccce83ec07e2c9f/chardet-7.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a4904dd5f071b7a7d7f50b4a67a86db3c902d243bf31708f1d5cde2f68239cb", size = 888283, upload-time = "2026-04-13T21:33:10.213Z" }, - { url = "https://files.pythonhosted.org/packages/87/2e/e1ee6a77abf3782c00e05b89c4d4328c8353bf9500661c4348df1dd68614/chardet-7.4.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5d2879598bc220689e8ce509fe9c3f37ad2fca53a36be9c9bd91abdd91dd364f", size = 879974, upload-time = "2026-04-13T21:33:11.448Z" }, - { url = "https://files.pythonhosted.org/packages/32/60/fca69c534602a7ced04280c952a246ad1edde2a6ca3a164f65d32ac41fe7/chardet-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:4b2799bd58e7245cfa8d4ab2e8ad1d76a5c3a5b1f32318eb6acca4c69a3e7101", size = 943973, upload-time = "2026-04-13T21:33:12.756Z" }, - { url = "https://files.pythonhosted.org/packages/7c/43/79ac9b4db5bc87020c9dbc419125371d80882d1d197e9c4765ba8682b605/chardet-7.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a9e4486df251b8962e86ea9f139ca235aa6e0542a00f7844c9a04160afb99aa9", size = 873769, upload-time = "2026-04-13T21:33:14.002Z" }, - { url = "https://files.pythonhosted.org/packages/55/5f/25bdec773905bff0ff6cf35ca73b17bd05593b4f87bd8c5fa43705f7167d/chardet-7.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4fbff1907925b0c5a1064cffb5e040cd5e338585c9c552625f30de6bc2f3107a", size = 853991, upload-time = "2026-04-13T21:33:15.564Z" }, - { url = "https://files.pythonhosted.org/packages/b4/07/a29380ee0b215d23d77733b5ad60c5c0c7969650e080c667acdf9462040d/chardet-7.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:365135eaf37ba65a828f8e668eb0a8c38c479dcbec724dc25f4dfd781049c357", size = 874024, upload-time = "2026-04-13T21:33:16.915Z" }, - { url = "https://files.pythonhosted.org/packages/a8/b1/3338e121cbd4c8a126b8ccb1061170c2ce51a53f678c502793ea49c6fd6d/chardet-7.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfc134b70c846c21ead8e43ada3ae1a805fff732f6922f8abcf2ff27b8f6493d", size = 887410, upload-time = "2026-04-13T21:33:18.368Z" }, - { url = "https://files.pythonhosted.org/packages/63/1c/44a9a9e0c59c185a5d307ceaeee8768afa1558f0a24f7a4b5fa11b67586b/chardet-7.4.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9acd9988a93e09390f3cd231201ea7166c415eb8da1b735928990ffc05cb9fbb", size = 879269, upload-time = "2026-04-13T21:33:20.377Z" }, - { url = "https://files.pythonhosted.org/packages/1b/b3/5d0e77ea774bd3224321c248880ea0c0379000ac5c2bb6d77609549de247/chardet-7.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:e1b98790c284ff813f18f7cf7de5f05ea2435a080030c7f1a8318f3a4f80b131", size = 944155, upload-time = "2026-04-13T21:33:21.694Z" }, - { url = "https://files.pythonhosted.org/packages/70/a8/bf0811d859e13801279a2ae64f37a408027b282f2047bc0001c75dd356ad/chardet-7.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d892d3dcd652fdef53e3d6327d39b17c0df40a899dfc919abaeb64c974497531", size = 872887, upload-time = "2026-04-13T21:33:23.328Z" }, - { url = "https://files.pythonhosted.org/packages/51/ac/b9d68ebddfe1b02c77af5bf81120e12b036b4432dc6af7a303d90e2bc38b/chardet-7.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:acc46d1b8b7d5783216afe15db56d1c179b9a40e5a1558bc13164c4fd20674c4", size = 853964, upload-time = "2026-04-13T21:33:24.724Z" }, - { url = "https://files.pythonhosted.org/packages/2a/81/17fa103ea9caf5d325a5e4051ab2ba65996fd66baa60b81ee41af1f54e10/chardet-7.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ac3bf11c645734a1701a3804e43eabd98851838192267d08c353a834ab79fea", size = 876006, upload-time = "2026-04-13T21:33:26.098Z" }, - { url = "https://files.pythonhosted.org/packages/c2/20/193faab46a68ea550587331a698c3dca8099f8901d10937c4443135c7ed9/chardet-7.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e3bd9f936e04bae89c254262af08d9e5b98f805175ba1e29d454e6cba3107b7", size = 887680, upload-time = "2026-04-13T21:33:27.49Z" }, - { url = "https://files.pythonhosted.org/packages/40/c6/94a3c673327392652ee8bdea9a45bc8a5f5365197a7387d68f0eed007115/chardet-7.4.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:27cc23da03630cdecc9aa81a895aa86629c211f995cd57651f0fbc280717bf93", size = 879865, upload-time = "2026-04-13T21:33:29.052Z" }, - { url = "https://files.pythonhosted.org/packages/b1/2c/cad8b5e3623a987f3c930b68e2bdd06cfc388cd91cd42ed05f1227701b73/chardet-7.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:b95c934b9ad59e2ba8abb9be49df70d3ad1b0d95d864b9fdb7588d4fa8bd921c", size = 939594, upload-time = "2026-04-13T21:33:31.391Z" }, - { url = "https://files.pythonhosted.org/packages/33/e0/d06e42fd6f02a58e5e227e5106587751cb38adcff0aaf949add744b78b6e/chardet-7.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c77867f0c1cb8bd819502249fcdc500364aedb07881e11b743726fa2148e7b6e", size = 889714, upload-time = "2026-04-13T21:33:32.772Z" }, - { url = "https://files.pythonhosted.org/packages/d4/ed/40d091954d48abea037baae6be8fb79905e5f78d34d12ea955132c7d8011/chardet-7.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:cf1efeaf65a6ef2f5b9cc3a1df6f08ba2831b369ccaa4c7018eaf90aa757bb11", size = 872319, upload-time = "2026-04-13T21:33:34.427Z" }, - { url = "https://files.pythonhosted.org/packages/bb/77/82a46821dbfbdfe062710d2bf2ede13426304e3567a23c57d919c0c31630/chardet-7.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f3504c139a2ad544077dd2d9e412cd08b01786843d76997cd43bb6de311723c", size = 892021, upload-time = "2026-04-13T21:33:35.766Z" }, - { url = "https://files.pythonhosted.org/packages/49/57/42d30c562bda5b4a839766c1aad8d5856b798ad2a1c3247b72a679afec94/chardet-7.4.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457f619882ba66327d4d8d14c6c342269bdb1e4e1c38e8117df941d14d351b04", size = 902509, upload-time = "2026-04-13T21:33:37.096Z" }, - { url = "https://files.pythonhosted.org/packages/8c/6c/0a40afdb50a0fe041ab95553b835a8160b6cf0e81edf2ae2fe9f5224cbf9/chardet-7.4.3-py3-none-any.whl", hash = "sha256:1173b74051570cf08099d7429d92e4882d375ad4217f92a6e5240ccfb26f231e", size = 626562, upload-time = "2026-04-13T21:33:38.559Z" }, -] - -[[package]] -name = "charset-normalizer" -version = "3.4.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, - { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, - { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, - { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, - { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, - { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, - { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, - { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, - { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, - { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, - { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, -] - -[[package]] -name = "click" -version = "8.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "comm" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, -] - -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, - { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, - { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, - { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, - { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, - { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, - { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, - { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, - { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, - { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, - { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, - { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, - { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, - { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, - { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, - { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, - { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, -] - -[[package]] -name = "coverage" -version = "7.13.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c3/a396306ba7db865bf96fc1fb3b7fd29bcbf3d829df642e77b13555163cd6/coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01", size = 219554, upload-time = "2026-03-17T10:30:42.208Z" }, - { url = "https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422", size = 219908, upload-time = "2026-03-17T10:30:43.906Z" }, - { url = "https://files.pythonhosted.org/packages/29/72/20b917c6793af3a5ceb7fb9c50033f3ec7865f2911a1416b34a7cfa0813b/coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f", size = 251419, upload-time = "2026-03-17T10:30:45.545Z" }, - { url = "https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5", size = 254159, upload-time = "2026-03-17T10:30:47.204Z" }, - { url = "https://files.pythonhosted.org/packages/9d/00/7b0edcfe64e2ed4c0340dac14a52ad0f4c9bd0b8b5e531af7d55b703db7c/coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376", size = 255270, upload-time = "2026-03-17T10:30:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/7ffc4ba0f5d0a55c1e84ea7cee39c9fc06af7b170513d83fbf3bbefce280/coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256", size = 257538, upload-time = "2026-03-17T10:30:50.77Z" }, - { url = "https://files.pythonhosted.org/packages/81/bd/73ddf85f93f7e6fa83e77ccecb6162d9415c79007b4bc124008a4995e4a7/coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c", size = 251821, upload-time = "2026-03-17T10:30:52.5Z" }, - { url = "https://files.pythonhosted.org/packages/a0/81/278aff4e8dec4926a0bcb9486320752811f543a3ce5b602cc7a29978d073/coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5", size = 253191, upload-time = "2026-03-17T10:30:54.543Z" }, - { url = "https://files.pythonhosted.org/packages/70/ee/fe1621488e2e0a58d7e94c4800f0d96f79671553488d401a612bebae324b/coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09", size = 251337, upload-time = "2026-03-17T10:30:56.663Z" }, - { url = "https://files.pythonhosted.org/packages/37/a6/f79fb37aa104b562207cc23cb5711ab6793608e246cae1e93f26b2236ed9/coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9", size = 255404, upload-time = "2026-03-17T10:30:58.427Z" }, - { url = "https://files.pythonhosted.org/packages/75/f0/ed15262a58ec81ce457ceb717b7f78752a1713556b19081b76e90896e8d4/coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf", size = 250903, upload-time = "2026-03-17T10:31:00.093Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e9/9129958f20e7e9d4d56d51d42ccf708d15cac355ff4ac6e736e97a9393d2/coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c", size = 252780, upload-time = "2026-03-17T10:31:01.916Z" }, - { url = "https://files.pythonhosted.org/packages/a4/d7/0ad9b15812d81272db94379fe4c6df8fd17781cc7671fdfa30c76ba5ff7b/coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf", size = 222093, upload-time = "2026-03-17T10:31:03.642Z" }, - { url = "https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810", size = 222900, upload-time = "2026-03-17T10:31:05.651Z" }, - { url = "https://files.pythonhosted.org/packages/d4/fa/2238c2ad08e35cf4f020ea721f717e09ec3152aea75d191a7faf3ef009a8/coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de", size = 221515, upload-time = "2026-03-17T10:31:07.293Z" }, - { url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" }, - { url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" }, - { url = "https://files.pythonhosted.org/packages/5f/13/93419671cee82b780bab7ea96b67c8ef448f5f295f36bf5031154ec9a790/coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26", size = 250935, upload-time = "2026-03-17T10:31:12.392Z" }, - { url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" }, - { url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" }, - { url = "https://files.pythonhosted.org/packages/44/b3/cb5bd1a04cfcc49ede6cd8409d80bee17661167686741e041abc7ee1b9a9/coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a", size = 256912, upload-time = "2026-03-17T10:31:17.89Z" }, - { url = "https://files.pythonhosted.org/packages/1b/66/c1dceb7b9714473800b075f5c8a84f4588f887a90eb8645282031676e242/coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969", size = 251165, upload-time = "2026-03-17T10:31:19.605Z" }, - { url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" }, - { url = "https://files.pythonhosted.org/packages/7d/37/7792c2d69854397ca77a55c4646e5897c467928b0e27f2d235d83b5d08c6/coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15", size = 250873, upload-time = "2026-03-17T10:31:23.565Z" }, - { url = "https://files.pythonhosted.org/packages/a3/23/bc866fb6163be52a8a9e5d708ba0d3b1283c12158cefca0a8bbb6e247a43/coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1", size = 255030, upload-time = "2026-03-17T10:31:25.58Z" }, - { url = "https://files.pythonhosted.org/packages/7d/8b/ef67e1c222ef49860701d346b8bbb70881bef283bd5f6cbba68a39a086c7/coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6", size = 250694, upload-time = "2026-03-17T10:31:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" }, - { url = "https://files.pythonhosted.org/packages/7a/f5/be742fec31118f02ce42b21c6af187ad6a344fed546b56ca60caacc6a9a0/coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85", size = 222112, upload-time = "2026-03-17T10:31:31.526Z" }, - { url = "https://files.pythonhosted.org/packages/66/40/7732d648ab9d069a46e686043241f01206348e2bbf128daea85be4d6414b/coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b", size = 222923, upload-time = "2026-03-17T10:31:33.633Z" }, - { url = "https://files.pythonhosted.org/packages/48/af/fea819c12a095781f6ccd504890aaddaf88b8fab263c4940e82c7b770124/coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664", size = 221540, upload-time = "2026-03-17T10:31:35.445Z" }, - { url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" }, - { url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" }, - { url = "https://files.pythonhosted.org/packages/29/9c/f9f5277b95184f764b24e7231e166dfdb5780a46d408a2ac665969416d61/coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806", size = 261912, upload-time = "2026-03-17T10:31:41.324Z" }, - { url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" }, - { url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f6/df63d8660e1a0bff6125947afda112a0502736f470d62ca68b288ea762d8/coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd", size = 267558, upload-time = "2026-03-17T10:31:48.293Z" }, - { url = "https://files.pythonhosted.org/packages/5b/02/353ca81d36779bd108f6d384425f7139ac3c58c750dcfaafe5d0bee6436b/coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606", size = 261163, upload-time = "2026-03-17T10:31:50.125Z" }, - { url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" }, - { url = "https://files.pythonhosted.org/packages/29/c7/c29e0c59ffa6942030ae6f50b88ae49988e7e8da06de7ecdbf49c6d4feae/coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0", size = 261604, upload-time = "2026-03-17T10:31:53.872Z" }, - { url = "https://files.pythonhosted.org/packages/40/48/097cdc3db342f34006a308ab41c3a7c11c3f0d84750d340f45d88a782e00/coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87", size = 265321, upload-time = "2026-03-17T10:31:55.997Z" }, - { url = "https://files.pythonhosted.org/packages/bb/1f/4994af354689e14fd03a75f8ec85a9a68d94e0188bbdab3fc1516b55e512/coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479", size = 260502, upload-time = "2026-03-17T10:31:58.308Z" }, - { url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" }, - { url = "https://files.pythonhosted.org/packages/14/4f/f5df9007e50b15e53e01edea486814783a7f019893733d9e4d6caad75557/coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a", size = 222788, upload-time = "2026-03-17T10:32:02.246Z" }, - { url = "https://files.pythonhosted.org/packages/e1/98/aa7fccaa97d0f3192bec013c4e6fd6d294a6ed44b640e6bb61f479e00ed5/coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819", size = 223851, upload-time = "2026-03-17T10:32:04.416Z" }, - { url = "https://files.pythonhosted.org/packages/3d/8b/e5c469f7352651e5f013198e9e21f97510b23de957dd06a84071683b4b60/coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911", size = 222104, upload-time = "2026-03-17T10:32:06.65Z" }, - { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, - { url = "https://files.pythonhosted.org/packages/6a/6c/1f1917b01eb647c2f2adc9962bd66c79eb978951cab61bdc1acab3290c07/coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a", size = 250992, upload-time = "2026-03-17T10:32:12.41Z" }, - { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, - { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, - { url = "https://files.pythonhosted.org/packages/61/77/50e8d3d85cc0b7ebe09f30f151d670e302c7ff4a1bf6243f71dd8b0981fa/coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6", size = 257161, upload-time = "2026-03-17T10:32:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/3b/c4/b5fd1d4b7bf8d0e75d997afd3925c59ba629fc8616f1b3aae7605132e256/coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0", size = 251021, upload-time = "2026-03-17T10:32:21.344Z" }, - { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ea/879c83cb5d61aa2a35fb80e72715e92672daef8191b84911a643f533840c/coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740", size = 250823, upload-time = "2026-03-17T10:32:25.516Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fb/616d95d3adb88b9803b275580bdeee8bd1b69a886d057652521f83d7322f/coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16", size = 255099, upload-time = "2026-03-17T10:32:27.944Z" }, - { url = "https://files.pythonhosted.org/packages/1c/93/25e6917c90ec1c9a56b0b26f6cad6408e5f13bb6b35d484a0d75c9cf000d/coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0", size = 250638, upload-time = "2026-03-17T10:32:29.914Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, - { url = "https://files.pythonhosted.org/packages/ea/fb/99cbbc56a26e07762a2740713f3c8f9f3f3106e3a3dd8cc4474954bccd34/coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc", size = 222360, upload-time = "2026-03-17T10:32:34.233Z" }, - { url = "https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633", size = 223174, upload-time = "2026-03-17T10:32:36.369Z" }, - { url = "https://files.pythonhosted.org/packages/2c/f2/24d84e1dfe70f8ac9fdf30d338239860d0d1d5da0bda528959d0ebc9da28/coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8", size = 221739, upload-time = "2026-03-17T10:32:38.736Z" }, - { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, - { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, - { url = "https://files.pythonhosted.org/packages/d6/fe/2a924b3055a5e7e4512655a9d4609781b0d62334fa0140c3e742926834e2/coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9", size = 261985, upload-time = "2026-03-17T10:32:45.514Z" }, - { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, - { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, - { url = "https://files.pythonhosted.org/packages/9c/20/d326174c55af36f74eac6ae781612d9492f060ce8244b570bb9d50d9d609/coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90", size = 267650, upload-time = "2026-03-17T10:32:52.391Z" }, - { url = "https://files.pythonhosted.org/packages/7a/5e/31484d62cbd0eabd3412e30d74386ece4a0837d4f6c3040a653878bfc019/coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3", size = 261089, upload-time = "2026-03-17T10:32:54.544Z" }, - { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, - { url = "https://files.pythonhosted.org/packages/06/3b/0351f1bd566e6e4dd39e978efe7958bde1d32f879e85589de147654f57bb/coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562", size = 261579, upload-time = "2026-03-17T10:32:59.466Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/796a2a2f4017f554d7810f5c573449b35b1e46788424a548d4d19201b222/coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2", size = 265316, upload-time = "2026-03-17T10:33:01.847Z" }, - { url = "https://files.pythonhosted.org/packages/3d/16/d5ae91455541d1a78bc90abf495be600588aff8f6db5c8b0dae739fa39c9/coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea", size = 260427, upload-time = "2026-03-17T10:33:03.945Z" }, - { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, - { url = "https://files.pythonhosted.org/packages/91/15/d792371332eb4663115becf4bad47e047d16234b1aff687b1b18c58d60ae/coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215", size = 223146, upload-time = "2026-03-17T10:33:08.756Z" }, - { url = "https://files.pythonhosted.org/packages/db/51/37221f59a111dca5e85be7dbf09696323b5b9f13ff65e0641d535ed06ea8/coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43", size = 224254, upload-time = "2026-03-17T10:33:11.174Z" }, - { url = "https://files.pythonhosted.org/packages/54/83/6acacc889de8987441aa7d5adfbdbf33d288dad28704a67e574f1df9bcbb/coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45", size = 222276, upload-time = "2026-03-17T10:33:13.466Z" }, - { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, -] - -[[package]] -name = "coveralls" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "requests" }, - { name = "typer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/97/16/aa263cb5450de470234f5176ce42664c5e0535aab1a2094b739447065797/coveralls-4.1.0.tar.gz", hash = "sha256:dab364025ba80cbb95ce56c6fc62cd9172d7fd637060ea235dde99d9b46a4494", size = 12736, upload-time = "2026-02-28T15:16:47.591Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/f1/5a884d2e8e3e7e7d854d3032b777bd22aad1598161cf9aa77ac37d684908/coveralls-4.1.0-py3-none-any.whl", hash = "sha256:bfacfda2d443c24fc90d67035027cec15015fff2dbd036427e8bf8f4953dda2e", size = 14168, upload-time = "2026-02-28T15:16:46.181Z" }, -] - -[[package]] -name = "cryptography" -version = "50.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, - { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, - { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, - { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, - { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, - { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, - { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, - { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, - { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, - { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, - { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, - { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, - { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, - { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, - { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, - { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, - { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, - { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, - { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, - { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, - { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, - { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, - { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, - { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, - { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, - { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, - { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, - { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, - { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, -] - -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - -[[package]] -name = "debugpy" -version = "1.8.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e0/b7/cd8080344452e4874aae67c40d8940e2b4d47b01601a8fd9f44786c757c7/debugpy-1.8.20.tar.gz", hash = "sha256:55bc8701714969f1ab89a6d5f2f3d40c36f91b2cbe2f65d98bf8196f6a6a2c33", size = 1645207, upload-time = "2026-01-29T23:03:28.199Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/57/7f34f4736bfb6e00f2e4c96351b07805d83c9a7b33d28580ae01374430f7/debugpy-1.8.20-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:4ae3135e2089905a916909ef31922b2d733d756f66d87345b3e5e52b7a55f13d", size = 2550686, upload-time = "2026-01-29T23:03:42.023Z" }, - { url = "https://files.pythonhosted.org/packages/ab/78/b193a3975ca34458f6f0e24aaf5c3e3da72f5401f6054c0dfd004b41726f/debugpy-1.8.20-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:88f47850a4284b88bd2bfee1f26132147d5d504e4e86c22485dfa44b97e19b4b", size = 4310588, upload-time = "2026-01-29T23:03:43.314Z" }, - { url = "https://files.pythonhosted.org/packages/c1/55/f14deb95eaf4f30f07ef4b90a8590fc05d9e04df85ee379712f6fb6736d7/debugpy-1.8.20-cp312-cp312-win32.whl", hash = "sha256:4057ac68f892064e5f98209ab582abfee3b543fb55d2e87610ddc133a954d390", size = 5331372, upload-time = "2026-01-29T23:03:45.526Z" }, - { url = "https://files.pythonhosted.org/packages/a1/39/2bef246368bd42f9bd7cba99844542b74b84dacbdbea0833e610f384fee8/debugpy-1.8.20-cp312-cp312-win_amd64.whl", hash = "sha256:a1a8f851e7cf171330679ef6997e9c579ef6dd33c9098458bd9986a0f4ca52e3", size = 5372835, upload-time = "2026-01-29T23:03:47.245Z" }, - { url = "https://files.pythonhosted.org/packages/15/e2/fc500524cc6f104a9d049abc85a0a8b3f0d14c0a39b9c140511c61e5b40b/debugpy-1.8.20-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:5dff4bb27027821fdfcc9e8f87309a28988231165147c31730128b1c983e282a", size = 2539560, upload-time = "2026-01-29T23:03:48.738Z" }, - { url = "https://files.pythonhosted.org/packages/90/83/fb33dcea789ed6018f8da20c5a9bc9d82adc65c0c990faed43f7c955da46/debugpy-1.8.20-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:84562982dd7cf5ebebfdea667ca20a064e096099997b175fe204e86817f64eaf", size = 4293272, upload-time = "2026-01-29T23:03:50.169Z" }, - { url = "https://files.pythonhosted.org/packages/a6/25/b1e4a01bfb824d79a6af24b99ef291e24189080c93576dfd9b1a2815cd0f/debugpy-1.8.20-cp313-cp313-win32.whl", hash = "sha256:da11dea6447b2cadbf8ce2bec59ecea87cc18d2c574980f643f2d2dfe4862393", size = 5331208, upload-time = "2026-01-29T23:03:51.547Z" }, - { url = "https://files.pythonhosted.org/packages/13/f7/a0b368ce54ffff9e9028c098bd2d28cfc5b54f9f6c186929083d4c60ba58/debugpy-1.8.20-cp313-cp313-win_amd64.whl", hash = "sha256:eb506e45943cab2efb7c6eafdd65b842f3ae779f020c82221f55aca9de135ed7", size = 5372930, upload-time = "2026-01-29T23:03:53.585Z" }, - { url = "https://files.pythonhosted.org/packages/33/2e/f6cb9a8a13f5058f0a20fe09711a7b726232cd5a78c6a7c05b2ec726cff9/debugpy-1.8.20-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9c74df62fc064cd5e5eaca1353a3ef5a5d50da5eb8058fcef63106f7bebe6173", size = 2538066, upload-time = "2026-01-29T23:03:54.999Z" }, - { url = "https://files.pythonhosted.org/packages/c5/56/6ddca50b53624e1ca3ce1d1e49ff22db46c47ea5fb4c0cc5c9b90a616364/debugpy-1.8.20-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:077a7447589ee9bc1ff0cdf443566d0ecf540ac8aa7333b775ebcb8ce9f4ecad", size = 4269425, upload-time = "2026-01-29T23:03:56.518Z" }, - { url = "https://files.pythonhosted.org/packages/c5/d9/d64199c14a0d4c476df46c82470a3ce45c8d183a6796cfb5e66533b3663c/debugpy-1.8.20-cp314-cp314-win32.whl", hash = "sha256:352036a99dd35053b37b7803f748efc456076f929c6a895556932eaf2d23b07f", size = 5331407, upload-time = "2026-01-29T23:03:58.481Z" }, - { url = "https://files.pythonhosted.org/packages/e0/d9/1f07395b54413432624d61524dfd98c1a7c7827d2abfdb8829ac92638205/debugpy-1.8.20-cp314-cp314-win_amd64.whl", hash = "sha256:a98eec61135465b062846112e5ecf2eebb855305acc1dfbae43b72903b8ab5be", size = 5372521, upload-time = "2026-01-29T23:03:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c3/7f67dea8ccf8fdcb9c99033bbe3e90b9e7395415843accb81428c441be2d/debugpy-1.8.20-py2.py3-none-any.whl", hash = "sha256:5be9bed9ae3be00665a06acaa48f8329d2b9632f15fd09f6a9a8c8d9907e54d7", size = 5337658, upload-time = "2026-01-29T23:04:17.404Z" }, -] - -[[package]] -name = "decorator" -version = "5.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", size = 75520, upload-time = "2021-03-08T10:59:26.269Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61", size = 25604, upload-time = "2021-03-08T10:59:24.45Z" }, -] - -[[package]] -name = "dill" -version = "0.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, -] - -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, -] - -[[package]] -name = "docutils" -version = "0.22.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, -] - -[[package]] -name = "executing" -version = "2.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, -] - -[[package]] -name = "fastjsonschema" -version = "2.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/b5/23b216d9d985a956623b6bd12d4086b60f0059b27799f23016af04a74ea1/fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de", size = 374130, upload-time = "2025-08-14T18:49:36.666Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, -] - -[[package]] -name = "filelock" -version = "3.29.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, -] - -[[package]] -name = "fonttools" -version = "4.62.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/d4/dbacced3953544b9a93088cc10ef2b596d348c983d5c67a404fa41ec51ba/fonttools-4.62.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:90365821debbd7db678809c7491ca4acd1e0779b9624cdc6ddaf1f31992bf974", size = 2870219, upload-time = "2026-03-13T13:52:53.664Z" }, - { url = "https://files.pythonhosted.org/packages/66/9e/a769c8e99b81e5a87ab7e5e7236684de4e96246aae17274e5347d11ebd78/fonttools-4.62.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12859ff0b47dd20f110804c3e0d0970f7b832f561630cd879969011541a464a9", size = 2414891, upload-time = "2026-03-13T13:52:56.493Z" }, - { url = "https://files.pythonhosted.org/packages/69/64/f19a9e3911968c37e1e620e14dfc5778299e1474f72f4e57c5ec771d9489/fonttools-4.62.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c125ffa00c3d9003cdaaf7f2c79e6e535628093e14b5de1dccb08859b680936", size = 5033197, upload-time = "2026-03-13T13:52:59.179Z" }, - { url = "https://files.pythonhosted.org/packages/9b/8a/99c8b3c3888c5c474c08dbfd7c8899786de9604b727fcefb055b42c84bba/fonttools-4.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:149f7d84afca659d1a97e39a4778794a2f83bf344c5ee5134e09995086cc2392", size = 4988768, upload-time = "2026-03-13T13:53:02.761Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c6/0f904540d3e6ab463c1243a0d803504826a11604c72dd58c2949796a1762/fonttools-4.62.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0aa72c43a601cfa9273bb1ae0518f1acadc01ee181a6fc60cd758d7fdadffc04", size = 4971512, upload-time = "2026-03-13T13:53:05.678Z" }, - { url = "https://files.pythonhosted.org/packages/29/0b/5cbef6588dc9bd6b5c9ad6a4d5a8ca384d0cea089da31711bbeb4f9654a6/fonttools-4.62.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:19177c8d96c7c36359266e571c5173bcee9157b59cfc8cb0153c5673dc5a3a7d", size = 5122723, upload-time = "2026-03-13T13:53:08.662Z" }, - { url = "https://files.pythonhosted.org/packages/4a/47/b3a5342d381595ef439adec67848bed561ab7fdb1019fa522e82101b7d9c/fonttools-4.62.1-cp312-cp312-win32.whl", hash = "sha256:a24decd24d60744ee8b4679d38e88b8303d86772053afc29b19d23bb8207803c", size = 2281278, upload-time = "2026-03-13T13:53:10.998Z" }, - { url = "https://files.pythonhosted.org/packages/28/b1/0c2ab56a16f409c6c8a68816e6af707827ad5d629634691ff60a52879792/fonttools-4.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e7863e10b3de72376280b515d35b14f5eeed639d1aa7824f4cf06779ec65e42", size = 2331414, upload-time = "2026-03-13T13:53:13.992Z" }, - { url = "https://files.pythonhosted.org/packages/3b/56/6f389de21c49555553d6a5aeed5ac9767631497ac836c4f076273d15bd72/fonttools-4.62.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c22b1014017111c401469e3acc5433e6acf6ebcc6aa9efb538a533c800971c79", size = 2865155, upload-time = "2026-03-13T13:53:16.132Z" }, - { url = "https://files.pythonhosted.org/packages/03/c5/0e3966edd5ec668d41dfe418787726752bc07e2f5fd8c8f208615e61fa89/fonttools-4.62.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68959f5fc58ed4599b44aad161c2837477d7f35f5f79402d97439974faebfebe", size = 2412802, upload-time = "2026-03-13T13:53:18.878Z" }, - { url = "https://files.pythonhosted.org/packages/52/94/e6ac4b44026de7786fe46e3bfa0c87e51d5d70a841054065d49cd62bb909/fonttools-4.62.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef46db46c9447103b8f3ff91e8ba009d5fe181b1920a83757a5762551e32bb68", size = 5013926, upload-time = "2026-03-13T13:53:21.379Z" }, - { url = "https://files.pythonhosted.org/packages/e2/98/8b1e801939839d405f1f122e7d175cebe9aeb4e114f95bfc45e3152af9a7/fonttools-4.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6706d1cb1d5e6251a97ad3c1b9347505c5615c112e66047abbef0f8545fa30d1", size = 4964575, upload-time = "2026-03-13T13:53:23.857Z" }, - { url = "https://files.pythonhosted.org/packages/46/76/7d051671e938b1881670528fec69cc4044315edd71a229c7fd712eaa5119/fonttools-4.62.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2e7abd2b1e11736f58c1de27819e1955a53267c21732e78243fa2fa2e5c1e069", size = 4953693, upload-time = "2026-03-13T13:53:26.569Z" }, - { url = "https://files.pythonhosted.org/packages/1f/ae/b41f8628ec0be3c1b934fc12b84f4576a5c646119db4d3bdd76a217c90b5/fonttools-4.62.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:403d28ce06ebfc547fbcb0cb8b7f7cc2f7a2d3e1a67ba9a34b14632df9e080f9", size = 5094920, upload-time = "2026-03-13T13:53:29.329Z" }, - { url = "https://files.pythonhosted.org/packages/f2/f6/53a1e9469331a23dcc400970a27a4caa3d9f6edbf5baab0260285238b884/fonttools-4.62.1-cp313-cp313-win32.whl", hash = "sha256:93c316e0f5301b2adbe6a5f658634307c096fd5aae60a5b3412e4f3e1728ab24", size = 2279928, upload-time = "2026-03-13T13:53:32.352Z" }, - { url = "https://files.pythonhosted.org/packages/38/60/35186529de1db3c01f5ad625bde07c1f576305eab6d86bbda4c58445f721/fonttools-4.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:7aa21ff53e28a9c2157acbc44e5b401149d3c9178107130e82d74ceb500e5056", size = 2330514, upload-time = "2026-03-13T13:53:34.991Z" }, - { url = "https://files.pythonhosted.org/packages/36/f0/2888cdac391807d68d90dcb16ef858ddc1b5309bfc6966195a459dd326e2/fonttools-4.62.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fa1d16210b6b10a826d71bed68dd9ec24a9e218d5a5e2797f37c573e7ec215ca", size = 2864442, upload-time = "2026-03-13T13:53:37.509Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b2/e521803081f8dc35990816b82da6360fa668a21b44da4b53fc9e77efcd62/fonttools-4.62.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:aa69d10ed420d8121118e628ad47d86e4caa79ba37f968597b958f6cceab7eca", size = 2410901, upload-time = "2026-03-13T13:53:40.55Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/8c3511ff06e53110039358dbbdc1a65d72157a054638387aa2ada300a8b8/fonttools-4.62.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd13b7999d59c5eb1c2b442eb2d0c427cb517a0b7a1f5798fc5c9e003f5ff782", size = 4999608, upload-time = "2026-03-13T13:53:42.798Z" }, - { url = "https://files.pythonhosted.org/packages/28/63/cd0c3b26afe60995a5295f37c246a93d454023726c3261cfbb3559969bb9/fonttools-4.62.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8d337fdd49a79b0d51c4da87bc38169d21c3abbf0c1aa9367eff5c6656fb6dae", size = 4912726, upload-time = "2026-03-13T13:53:45.405Z" }, - { url = "https://files.pythonhosted.org/packages/70/b9/ac677cb07c24c685cf34f64e140617d58789d67a3dd524164b63648c6114/fonttools-4.62.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d241cdc4a67b5431c6d7f115fdf63335222414995e3a1df1a41e1182acd4bcc7", size = 4951422, upload-time = "2026-03-13T13:53:48.326Z" }, - { url = "https://files.pythonhosted.org/packages/e6/10/11c08419a14b85b7ca9a9faca321accccc8842dd9e0b1c8a72908de05945/fonttools-4.62.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c05557a78f8fa514da0f869556eeda40887a8abc77c76ee3f74cf241778afd5a", size = 5060979, upload-time = "2026-03-13T13:53:51.366Z" }, - { url = "https://files.pythonhosted.org/packages/4e/3c/12eea4a4cf054e7ab058ed5ceada43b46809fce2bf319017c4d63ae55bb4/fonttools-4.62.1-cp314-cp314-win32.whl", hash = "sha256:49a445d2f544ce4a69338694cad575ba97b9a75fff02720da0882d1a73f12800", size = 2283733, upload-time = "2026-03-13T13:53:53.606Z" }, - { url = "https://files.pythonhosted.org/packages/6b/67/74b070029043186b5dd13462c958cb7c7f811be0d2e634309d9a1ffb1505/fonttools-4.62.1-cp314-cp314-win_amd64.whl", hash = "sha256:1eecc128c86c552fb963fe846ca4e011b1be053728f798185a1687502f6d398e", size = 2335663, upload-time = "2026-03-13T13:53:56.23Z" }, - { url = "https://files.pythonhosted.org/packages/42/c5/4d2ed3ca6e33617fc5624467da353337f06e7f637707478903c785bd8e20/fonttools-4.62.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1596aeaddf7f78e21e68293c011316a25267b3effdaccaf4d59bc9159d681b82", size = 2947288, upload-time = "2026-03-13T13:53:59.397Z" }, - { url = "https://files.pythonhosted.org/packages/1f/e9/7ab11ddfda48ed0f89b13380e5595ba572619c27077be0b2c447a63ff351/fonttools-4.62.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8f8fca95d3bb3208f59626a4b0ea6e526ee51f5a8ad5d91821c165903e8d9260", size = 2449023, upload-time = "2026-03-13T13:54:01.642Z" }, - { url = "https://files.pythonhosted.org/packages/b2/10/a800fa090b5e8819942e54e19b55fc7c21fe14a08757c3aa3ca8db358939/fonttools-4.62.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee91628c08e76f77b533d65feb3fbe6d9dad699f95be51cf0d022db94089cdc4", size = 5137599, upload-time = "2026-03-13T13:54:04.495Z" }, - { url = "https://files.pythonhosted.org/packages/37/dc/8ccd45033fffd74deb6912fa1ca524643f584b94c87a16036855b498a1ed/fonttools-4.62.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f37df1cac61d906e7b836abe356bc2f34c99d4477467755c216b72aa3dc748b", size = 4920933, upload-time = "2026-03-13T13:54:07.557Z" }, - { url = "https://files.pythonhosted.org/packages/99/eb/e618adefb839598d25ac8136cd577925d6c513dc0d931d93b8af956210f0/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:92bb00a947e666169c99b43753c4305fc95a890a60ef3aeb2a6963e07902cc87", size = 5016232, upload-time = "2026-03-13T13:54:10.611Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5f/9b5c9bfaa8ec82def8d8168c4f13615990d6ce5996fe52bd49bfb5e05134/fonttools-4.62.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bdfe592802ef939a0e33106ea4a318eeb17822c7ee168c290273cbd5fabd746c", size = 5042987, upload-time = "2026-03-13T13:54:13.569Z" }, - { url = "https://files.pythonhosted.org/packages/90/aa/dfbbe24c6a6afc5c203d90cc0343e24bcbb09e76d67c4d6eef8c2558d7ba/fonttools-4.62.1-cp314-cp314t-win32.whl", hash = "sha256:b820fcb92d4655513d8402d5b219f94481c4443d825b4372c75a2072aa4b357a", size = 2348021, upload-time = "2026-03-13T13:54:16.98Z" }, - { url = "https://files.pythonhosted.org/packages/13/6f/ae9c4e4dd417948407b680855c2c7790efb52add6009aaecff1e3bc50e8e/fonttools-4.62.1-cp314-cp314t-win_amd64.whl", hash = "sha256:59b372b4f0e113d3746b88985f1c796e7bf830dd54b28374cd85c2b8acd7583e", size = 2414147, upload-time = "2026-03-13T13:54:19.416Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, -] - -[[package]] -name = "fqdn" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015, upload-time = "2021-03-11T07:16:29.08Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121, upload-time = "2021-03-11T07:16:28.351Z" }, -] - -[[package]] -name = "h11" -version = "0.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, -] - -[[package]] -name = "hatch" -version = "1.16.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-zstd", marker = "python_full_version < '3.14'" }, - { name = "click" }, - { name = "hatchling" }, - { name = "httpx" }, - { name = "hyperlink" }, - { name = "keyring" }, - { name = "packaging" }, - { name = "pexpect" }, - { name = "platformdirs" }, - { name = "pyproject-hooks" }, - { name = "python-discovery" }, - { name = "rich" }, - { name = "shellingham" }, - { name = "tomli-w" }, - { name = "tomlkit" }, - { name = "userpath" }, - { name = "uv" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d2/02/ce9c4c439fa3f195b21b4b5bb18b44d1076297c86477ef7e3d2de6064ec3/hatch-1.16.5.tar.gz", hash = "sha256:57bdeeaa72577859ce37091a5449583875331c06f9cb6af9077947ad40b3a1de", size = 5220741, upload-time = "2026-02-27T18:45:31.21Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/8a/11ae7e271870f0ad8fa0012e4265982bebe0fdc21766b161fb8b8fc3aefc/hatch-1.16.5-py3-none-any.whl", hash = "sha256:d9b8047f2cd10d3349eb6e8f278ad728a04f91495aace305c257d5c2747188fb", size = 141269, upload-time = "2026-02-27T18:45:29.573Z" }, -] - -[[package]] -name = "hatchling" -version = "1.29.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, - { name = "pathspec" }, - { name = "pluggy" }, - { name = "trove-classifiers" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cf/9c/b4cfe330cd4f49cff17fd771154730555fa4123beb7f292cf0098b4e6c20/hatchling-1.29.0.tar.gz", hash = "sha256:793c31816d952cee405b83488ce001c719f325d9cda69f1fc4cd750527640ea6", size = 55656, upload-time = "2026-02-23T19:42:06.539Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/8a/44032265776062a89171285ede55a0bdaadc8ac00f27f0512a71a9e3e1c8/hatchling-1.29.0-py3-none-any.whl", hash = "sha256:50af9343281f34785fab12da82e445ed987a6efb34fd8c2fc0f6e6630dbcc1b0", size = 76356, upload-time = "2026-02-23T19:42:05.197Z" }, -] - -[[package]] -name = "httpcore" -version = "1.0.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "h11" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, -] - -[[package]] -name = "httpx" -version = "0.28.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "certifi" }, - { name = "httpcore" }, - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, -] - -[[package]] -name = "hyperlink" -version = "21.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3a/51/1947bd81d75af87e3bb9e34593a4cf118115a8feb451ce7a69044ef1412e/hyperlink-21.0.0.tar.gz", hash = "sha256:427af957daa58bc909471c6c40f74c5450fa123dd093fc53efd2e91d2705a56b", size = 140743, upload-time = "2021-01-08T05:51:20.972Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/aa/8caf6a0a3e62863cbb9dab27135660acba46903b703e224f14f447e57934/hyperlink-21.0.0-py2.py3-none-any.whl", hash = "sha256:e6b14c37ecb73e89c77d78cdb4c2cc8f3fb59a885c5b3f819ff4ed80f25af1b4", size = 74638, upload-time = "2021-01-08T05:51:22.906Z" }, -] - -[[package]] -name = "id" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/04/c2156091427636080787aac190019dc64096e56a23b7364d3c1764ee3a06/id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069", size = 18088, upload-time = "2026-02-04T16:19:41.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/77/de194443bf38daed9452139e960c632b0ef9f9a5dd9ce605fdf18ca9f1b1/id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca", size = 14689, upload-time = "2026-02-04T16:19:40.051Z" }, -] - -[[package]] -name = "idna" -version = "3.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" }, -] - -[[package]] -name = "imagesize" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045, upload-time = "2026-03-03T14:18:29.941Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441, upload-time = "2026-03-03T14:18:27.892Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "ipykernel" -version = "7.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "appnope", marker = "sys_platform == 'darwin'" }, - { name = "comm" }, - { name = "debugpy" }, - { name = "ipython" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "matplotlib-inline" }, - { name = "nest-asyncio" }, - { name = "packaging" }, - { name = "psutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/8d/b68b728e2d06b9e0051019640a40a9eb7a88fcd82c2e1b5ce70bef5ff044/ipykernel-7.2.0.tar.gz", hash = "sha256:18ed160b6dee2cbb16e5f3575858bc19d8f1fe6046a9a680c708494ce31d909e", size = 176046, upload-time = "2026-02-06T16:43:27.403Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/b9/e73d5d9f405cba7706c539aa8b311b49d4c2f3d698d9c12f815231169c71/ipykernel-7.2.0-py3-none-any.whl", hash = "sha256:3bbd4420d2b3cc105cbdf3756bfc04500b1e52f090a90716851f3916c62e1661", size = 118788, upload-time = "2026-02-06T16:43:25.149Z" }, -] - -[[package]] -name = "ipython" -version = "9.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "psutil" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274, upload-time = "2026-04-24T12:24:53.038Z" }, -] - -[[package]] -name = "ipython-pygments-lexers" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, -] - -[[package]] -name = "ipywidgets" -version = "8.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "comm" }, - { name = "ipython" }, - { name = "jupyterlab-widgets" }, - { name = "traitlets" }, - { name = "widgetsnbextension" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/ae/c5ce1edc1afe042eadb445e95b0671b03cee61895264357956e61c0d2ac0/ipywidgets-8.1.8.tar.gz", hash = "sha256:61f969306b95f85fba6b6986b7fe45d73124d1d9e3023a8068710d47a22ea668", size = 116739, upload-time = "2025-11-01T21:18:12.393Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, -] - -[[package]] -name = "isoduration" -version = "20.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "arrow" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649, upload-time = "2020-11-01T11:00:00.312Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321, upload-time = "2020-11-01T10:59:58.02Z" }, -] - -[[package]] -name = "isort" -version = "8.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ef/7c/ec4ab396d31b3b395e2e999c8f46dec78c5e29209fac49d1f4dace04041d/isort-8.0.1.tar.gz", hash = "sha256:171ac4ff559cdc060bcfff550bc8404a486fee0caab245679c2abe7cb253c78d", size = 769592, upload-time = "2026-02-28T10:08:20.685Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, -] - -[[package]] -name = "jaraco-classes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, -] - -[[package]] -name = "jaraco-context" -version = "6.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/50/4763cd07e722bb6285316d390a164bc7e479db9d90daa769f22578f698b4/jaraco_context-6.1.2.tar.gz", hash = "sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3", size = 16801, upload-time = "2026-03-20T22:13:33.922Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f2/58/bc8954bda5fcda97bd7c19be11b85f91973d67a706ed4a3aec33e7de22db/jaraco_context-6.1.2-py3-none-any.whl", hash = "sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535", size = 7871, upload-time = "2026-03-20T22:13:32.808Z" }, -] - -[[package]] -name = "jaraco-functools" -version = "4.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "more-itertools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, -] - -[[package]] -name = "jedi" -version = "0.19.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "parso" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, -] - -[[package]] -name = "jeepney" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, -] - -[[package]] -name = "joblib" -version = "1.5.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, -] - -[[package]] -name = "json5" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656, upload-time = "2026-03-27T22:50:48.108Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271, upload-time = "2026-03-27T22:50:47.073Z" }, -] - -[[package]] -name = "jsonpickle" -version = "4.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e4/a6/d07afcfdef402900229bcca795f80506b207af13a838d4d99ad45abf530c/jsonpickle-4.1.1.tar.gz", hash = "sha256:f86e18f13e2b96c1c1eede0b7b90095bbb61d99fedc14813c44dc2f361dbbae1", size = 316885, upload-time = "2025-06-02T20:36:11.57Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/73/04df8a6fa66d43a9fd45c30f283cc4afff17da671886e451d52af60bdc7e/jsonpickle-4.1.1-py3-none-any.whl", hash = "sha256:bb141da6057898aa2438ff268362b126826c812a1721e31cf08a6e142910dc91", size = 47125, upload-time = "2025-06-02T20:36:08.647Z" }, -] - -[[package]] -name = "jsonpointer" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068, upload-time = "2026-03-23T22:32:32.458Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659, upload-time = "2026-03-23T22:32:31.568Z" }, -] - -[[package]] -name = "jsonschema" -version = "4.26.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "jsonschema-specifications" }, - { name = "referencing" }, - { name = "rpds-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, -] - -[package.optional-dependencies] -format-nongpl = [ - { name = "fqdn" }, - { name = "idna" }, - { name = "isoduration" }, - { name = "jsonpointer" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "rfc3987-syntax" }, - { name = "uri-template" }, - { name = "webcolors" }, -] - -[[package]] -name = "jsonschema-specifications" -version = "2025.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "referencing" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, -] - -[[package]] -name = "jupyter" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "ipywidgets" }, - { name = "jupyter-console" }, - { name = "jupyterlab" }, - { name = "nbconvert" }, - { name = "notebook" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959, upload-time = "2024-08-30T07:15:48.299Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657, upload-time = "2024-08-30T07:15:47.045Z" }, -] - -[[package]] -name = "jupyter-builder" -version = "1.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/75/3e/56f593e6a664cd14d441724654ce8243f3cac7d3e45867cf084749c8bc75/jupyter_builder-1.2.3.tar.gz", hash = "sha256:01aba6794eb9b19e0e29ae21137ca60ba4135c70347d4b9f664a586822b8c809", size = 1024218, upload-time = "2026-09-04T18:54:09.411Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/aa/be79e87c50698673f196d0633fc1664607da2289f9688d1de7a1c9db9e71/jupyter_builder-1.2.3-py3-none-any.whl", hash = "sha256:c5ea5a7190c2a7b082494abade98eece1b2b5bd5dbd7d610606cbcd10a1d08b3", size = 947541, upload-time = "2026-09-04T18:54:07.644Z" }, -] - -[[package]] -name = "jupyter-client" -version = "8.8.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-core" }, - { name = "python-dateutil" }, - { name = "pyzmq" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, -] - -[[package]] -name = "jupyter-console" -version = "6.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "ipython" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "pyzmq" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363, upload-time = "2023-03-06T14:13:31.02Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510, upload-time = "2023-03-06T14:13:28.229Z" }, -] - -[[package]] -name = "jupyter-core" -version = "5.9.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "platformdirs" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/49/9d1284d0dc65e2c757b74c6687b6d319b02f822ad039e5c512df9194d9dd/jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508", size = 89814, upload-time = "2025-10-16T19:19:18.444Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, -] - -[[package]] -name = "jupyter-events" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonschema", extra = ["format-nongpl"] }, - { name = "packaging" }, - { name = "python-json-logger" }, - { name = "pyyaml" }, - { name = "referencing" }, - { name = "rfc3339-validator" }, - { name = "rfc3986-validator" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/f8/475c4241b2b75af0deaae453ed003c6c851766dbc44d332d8baf245dc931/jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3", size = 62854, upload-time = "2026-04-20T23:17:50.66Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/6c/6fcde0c8f616ed360ffd3587f7db9e225a7e62b583a04494d2f069cf64ea/jupyter_events-0.12.1-py3-none-any.whl", hash = "sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf", size = 19512, upload-time = "2026-04-20T23:17:48.927Z" }, -] - -[[package]] -name = "jupyter-lsp" -version = "2.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/36/ff/1e4a61f5170a9a1d978f3ac3872449de6c01fc71eaf89657824c878b1549/jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6", size = 55677, upload-time = "2026-04-02T08:10:06.749Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/e8/9d61dcbd1dce8ef418f06befd4ac084b4720429c26b0b1222bc218685eff/jupyter_lsp-2.3.1-py3-none-any.whl", hash = "sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81", size = 77513, upload-time = "2026-04-02T08:10:01.753Z" }, -] - -[[package]] -name = "jupyter-server" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "argon2-cffi" }, - { name = "jinja2" }, - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "jupyter-events" }, - { name = "jupyter-server-terminals" }, - { name = "nbconvert" }, - { name = "nbformat" }, - { name = "packaging" }, - { name = "prometheus-client" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "pyzmq" }, - { name = "send2trash" }, - { name = "terminado" }, - { name = "tornado" }, - { name = "traitlets" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6b/dc/db3a582633170186f8c8b31298d7eb26ad0eb031a1f53476c258b64eed05/jupyter_server-2.20.0.tar.gz", hash = "sha256:b5778ba337d8015a3dc2b80803ecdd5ac18d3797fddf61a50ea5fb472b4ebe14", size = 756523, upload-time = "2026-06-17T12:09:09.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/71/8c002223e873a870f5c41dc69b0a7c922301123e4a31d5d01ecb700aef77/jupyter_server-2.20.0-py3-none-any.whl", hash = "sha256:c3b67c93c471e947c18b5026f04f21614218adb706df8f48227d3ee8e0a7cdcc", size = 393143, upload-time = "2026-06-17T12:09:07.234Z" }, -] - -[[package]] -name = "jupyter-server-terminals" -version = "0.5.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "terminado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770, upload-time = "2026-01-14T16:53:20.213Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704, upload-time = "2026-01-14T16:53:18.738Z" }, -] - -[[package]] -name = "jupyterlab" -version = "4.6.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-lru" }, - { name = "httpx" }, - { name = "ipykernel" }, - { name = "jinja2" }, - { name = "jupyter-builder" }, - { name = "jupyter-core" }, - { name = "jupyter-lsp" }, - { name = "jupyter-server" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "packaging" }, - { name = "tornado" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/8d/995cc142f6083346b35e7d3eadc5a6717ee89ce64ea53577010ac493bc3c/jupyterlab-4.6.4.tar.gz", hash = "sha256:404f49b081819378524886c9db66dba57a5565981eff885830df1baba3a17df5", size = 28335647, upload-time = "2026-09-21T15:25:18.779Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/e4/072bc0d3c6d45d414062a06af7ee81c02a2c12d769c358ec9919f9995ffd/jupyterlab-4.6.4-py3-none-any.whl", hash = "sha256:15b13f991d3985129c797eb84d9949eeb8b6615e14b444868e642411f2c418b2", size = 17171596, upload-time = "2026-09-21T15:25:14.123Z" }, -] - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, -] - -[[package]] -name = "jupyterlab-server" -version = "2.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "jinja2" }, - { name = "json5" }, - { name = "jsonschema" }, - { name = "jupyter-server" }, - { name = "packaging" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996, upload-time = "2025-10-22T13:59:18.37Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830, upload-time = "2025-10-22T13:59:16.767Z" }, -] - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.16" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, -] - -[[package]] -name = "keyring" -version = "25.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jaraco-classes" }, - { name = "jaraco-context" }, - { name = "jaraco-functools" }, - { name = "jeepney", marker = "sys_platform == 'linux'" }, - { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, - { name = "secretstorage", marker = "sys_platform == 'linux'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, -] - -[[package]] -name = "kiwisolver" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, - { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, - { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, - { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, - { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, - { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, - { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, - { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, - { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, - { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, - { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, - { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, - { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, - { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, - { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, - { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, - { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, - { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, - { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, - { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, - { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, - { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, - { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, - { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, - { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/060f45052f2a01ad5762c8fdecd6d7a752b43400dc29ff75cd47225a40fd/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8df31fe574b8b3993cc61764f40941111b25c2d9fea13d3ce24a49907cd2d615", size = 123231, upload-time = "2026-03-09T13:14:41.323Z" }, - { url = "https://files.pythonhosted.org/packages/c2/a7/78da680eadd06ff35edef6ef68a1ad273bad3e2a0936c9a885103230aece/kiwisolver-1.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1d49a49ac4cbfb7c1375301cd1ec90169dfeae55ff84710d782260ce77a75a02", size = 66489, upload-time = "2026-03-09T13:14:42.534Z" }, - { url = "https://files.pythonhosted.org/packages/49/b2/97980f3ad4fae37dd7fe31626e2bf75fbf8bdf5d303950ec1fab39a12da8/kiwisolver-1.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0cbe94b69b819209a62cb27bdfa5dc2a8977d8de2f89dfd97ba4f53ed3af754e", size = 64063, upload-time = "2026-03-09T13:14:44.759Z" }, - { url = "https://files.pythonhosted.org/packages/e7/f9/b06c934a6aa8bc91f566bd2a214fd04c30506c2d9e2b6b171953216a65b6/kiwisolver-1.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80aa065ffd378ff784822a6d7c3212f2d5f5e9c3589614b5c228b311fd3063ac", size = 1475913, upload-time = "2026-03-09T13:14:46.247Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f0/f768ae564a710135630672981231320bc403cf9152b5596ec5289de0f106/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e7f886f47ab881692f278ae901039a234e4025a68e6dfab514263a0b1c4ae05", size = 1282782, upload-time = "2026-03-09T13:14:48.458Z" }, - { url = "https://files.pythonhosted.org/packages/e2/9f/1de7aad00697325f05238a5f2eafbd487fb637cc27a558b5367a5f37fb7f/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5060731cc3ed12ca3a8b57acd4aeca5bbc2f49216dd0bec1650a1acd89486bcd", size = 1300815, upload-time = "2026-03-09T13:14:50.721Z" }, - { url = "https://files.pythonhosted.org/packages/5a/c2/297f25141d2e468e0ce7f7a7b92e0cf8918143a0cbd3422c1ad627e85a06/kiwisolver-1.5.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a4aa69609f40fce3cbc3f87b2061f042eee32f94b8f11db707b66a26461591a", size = 1347925, upload-time = "2026-03-09T13:14:52.304Z" }, - { url = "https://files.pythonhosted.org/packages/b9/d3/f4c73a02eb41520c47610207b21afa8cdd18fdbf64ffd94674ae21c4812d/kiwisolver-1.5.0-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:d168fda2dbff7b9b5f38e693182d792a938c31db4dac3a80a4888de603c99554", size = 991322, upload-time = "2026-03-09T13:14:54.637Z" }, - { url = "https://files.pythonhosted.org/packages/7b/46/d3f2efef7732fcda98d22bf4ad5d3d71d545167a852ca710a494f4c15343/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:413b820229730d358efd838ecbab79902fe97094565fdc80ddb6b0a18c18a581", size = 2232857, upload-time = "2026-03-09T13:14:56.471Z" }, - { url = "https://files.pythonhosted.org/packages/3f/ec/2d9756bf2b6d26ae4349b8d3662fb3993f16d80c1f971c179ce862b9dbae/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5124d1ea754509b09e53738ec185584cc609aae4a3b510aaf4ed6aa047ef9303", size = 2329376, upload-time = "2026-03-09T13:14:58.072Z" }, - { url = "https://files.pythonhosted.org/packages/8f/9f/876a0a0f2260f1bde92e002b3019a5fabc35e0939c7d945e0fa66185eb20/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e4415a8db000bf49a6dd1c478bf70062eaacff0f462b92b0ba68791a905861f9", size = 1982549, upload-time = "2026-03-09T13:14:59.668Z" }, - { url = "https://files.pythonhosted.org/packages/6c/4f/ba3624dfac23a64d54ac4179832860cb537c1b0af06024936e82ca4154a0/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d618fd27420381a4f6044faa71f46d8bfd911bd077c555f7138ed88729bfbe79", size = 2494680, upload-time = "2026-03-09T13:15:01.364Z" }, - { url = "https://files.pythonhosted.org/packages/39/b7/97716b190ab98911b20d10bf92eca469121ec483b8ce0edd314f51bc85af/kiwisolver-1.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5092eb5b1172947f57d6ea7d89b2f29650414e4293c47707eb499ec07a0ac796", size = 2297905, upload-time = "2026-03-09T13:15:03.925Z" }, - { url = "https://files.pythonhosted.org/packages/a3/36/4e551e8aa55c9188bca9abb5096805edbf7431072b76e2298e34fd3a3008/kiwisolver-1.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:d76e2d8c75051d58177e762164d2e9ab92886534e3a12e795f103524f221dd8e", size = 75086, upload-time = "2026-03-09T13:15:07.775Z" }, - { url = "https://files.pythonhosted.org/packages/70/15/9b90f7df0e31a003c71649cf66ef61c3c1b862f48c81007fa2383c8bd8d7/kiwisolver-1.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:fa6248cd194edff41d7ea9425ced8ca3a6f838bfb295f6f1d6e6bb694a8518df", size = 66577, upload-time = "2026-03-09T13:15:09.139Z" }, - { url = "https://files.pythonhosted.org/packages/17/01/7dc8c5443ff42b38e72731643ed7cf1ed9bf01691ae5cdca98501999ed83/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d1ffeb80b5676463d7a7d56acbe8e37a20ce725570e09549fe738e02ca6b7e1e", size = 125794, upload-time = "2026-03-09T13:15:10.525Z" }, - { url = "https://files.pythonhosted.org/packages/46/8a/b4ebe46ebaac6a303417fab10c2e165c557ddaff558f9699d302b256bc53/kiwisolver-1.5.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc4d8e252f532ab46a1de9349e2d27b91fce46736a9eedaa37beaca66f574ed4", size = 67646, upload-time = "2026-03-09T13:15:12.016Z" }, - { url = "https://files.pythonhosted.org/packages/60/35/10a844afc5f19d6f567359bf4789e26661755a2f36200d5d1ed8ad0126e5/kiwisolver-1.5.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6783e069732715ad0c3ce96dbf21dbc2235ab0593f2baf6338101f70371f4028", size = 65511, upload-time = "2026-03-09T13:15:13.311Z" }, - { url = "https://files.pythonhosted.org/packages/f8/8a/685b297052dd041dcebce8e8787b58923b6e78acc6115a0dc9189011c44b/kiwisolver-1.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e7c4c09a490dc4d4a7f8cbee56c606a320f9dc28cf92a7157a39d1ce7676a657", size = 1584858, upload-time = "2026-03-09T13:15:15.103Z" }, - { url = "https://files.pythonhosted.org/packages/9e/80/04865e3d4638ac5bddec28908916df4a3075b8c6cc101786a96803188b96/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a075bd7bd19c70cf67c8badfa36cf7c5d8de3c9ddb8420c51e10d9c50e94920", size = 1392539, upload-time = "2026-03-09T13:15:16.661Z" }, - { url = "https://files.pythonhosted.org/packages/ba/01/77a19cacc0893fa13fafa46d1bba06fb4dc2360b3292baf4b56d8e067b24/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bdd3e53429ff02aa319ba59dfe4ceeec345bf46cf180ec2cf6fd5b942e7975e9", size = 1405310, upload-time = "2026-03-09T13:15:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/53/39/bcaf5d0cca50e604cfa9b4e3ae1d64b50ca1ae5b754122396084599ef903/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cdcb35dc9d807259c981a85531048ede628eabcffb3239adf3d17463518992d", size = 1456244, upload-time = "2026-03-09T13:15:20.444Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7a/72c187abc6975f6978c3e39b7cf67aeb8b3c0a8f9790aa7fd412855e9e1f/kiwisolver-1.5.0-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:70d593af6a6ca332d1df73d519fddb5148edb15cd90d5f0155e3746a6d4fcc65", size = 1073154, upload-time = "2026-03-09T13:15:22.039Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ca/cf5b25783ebbd59143b4371ed0c8428a278abe68d6d0104b01865b1bbd0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:377815a8616074cabbf3f53354e1d040c35815a134e01d7614b7692e4bf8acfa", size = 2334377, upload-time = "2026-03-09T13:15:23.741Z" }, - { url = "https://files.pythonhosted.org/packages/4a/e5/b1f492adc516796e88751282276745340e2a72dcd0d36cf7173e0daf3210/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0255a027391d52944eae1dbb5d4cc5903f57092f3674e8e544cdd2622826b3f0", size = 2425288, upload-time = "2026-03-09T13:15:25.789Z" }, - { url = "https://files.pythonhosted.org/packages/e6/e5/9b21fbe91a61b8f409d74a26498706e97a48008bfcd1864373d32a6ba31c/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:012b1eb16e28718fa782b5e61dc6f2da1f0792ca73bd05d54de6cb9561665fc9", size = 2063158, upload-time = "2026-03-09T13:15:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/b1/02/83f47986138310f95ea95531f851b2a62227c11cbc3e690ae1374fe49f0f/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0e3aafb33aed7479377e5e9a82e9d4bf87063741fc99fc7ae48b0f16e32bdd6f", size = 2597260, upload-time = "2026-03-09T13:15:29.421Z" }, - { url = "https://files.pythonhosted.org/packages/07/18/43a5f24608d8c313dd189cf838c8e68d75b115567c6279de7796197cfb6a/kiwisolver-1.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7a116ae737f0000343218c4edf5bd45893bfeaff0993c0b215d7124c9f77646", size = 2394403, upload-time = "2026-03-09T13:15:31.517Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b5/98222136d839b8afabcaa943b09bd05888c2d36355b7e448550211d1fca4/kiwisolver-1.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1dd9b0b119a350976a6d781e7278ec7aca0b201e1a9e2d23d9804afecb6ca681", size = 79687, upload-time = "2026-03-09T13:15:33.204Z" }, - { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, - { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, - { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, - { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, -] - -[[package]] -name = "lark" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, -] - -[[package]] -name = "levenshtein" -version = "0.27.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "rapidfuzz" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/56/dcf68853b062e3b94bdc3d011cc4198779abc5b9dc134146a062920ce2e2/levenshtein-0.27.3.tar.gz", hash = "sha256:1ac326b2c84215795163d8a5af471188918b8797b4953ec87aaba22c9c1f9fc0", size = 393269, upload-time = "2025-11-01T12:14:31.04Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/8e/3be9d8e0245704e3af5258fb6cb157c3d59902e1351e95edf6ed8a8c0434/levenshtein-0.27.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2de7f095b0ca8e44de9de986ccba661cd0dec3511c751b499e76b60da46805e9", size = 169622, upload-time = "2025-11-01T12:13:10.026Z" }, - { url = "https://files.pythonhosted.org/packages/a6/42/a2b2fda5e8caf6ecd5aac142f946a77574a3961e65da62c12fd7e48e5cb1/levenshtein-0.27.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9b8b29e5d5145a3c958664c85151b1bb4b26e4ca764380b947e6a96a321217c", size = 159183, upload-time = "2025-11-01T12:13:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c4/f083fabbd61c449752df1746533538f4a8629e8811931b52f66e6c4290ad/levenshtein-0.27.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc975465a51b1c5889eadee1a583b81fba46372b4b22df28973e49e8ddb8f54a", size = 133120, upload-time = "2025-11-01T12:13:12.363Z" }, - { url = "https://files.pythonhosted.org/packages/4e/e5/b6421e04cb0629615b8efd6d4d167dd2b1afb5097b87bb83cd992004dcca/levenshtein-0.27.3-cp312-cp312-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:57573ed885118554770979fdee584071b66103f6d50beddeabb54607a1213d81", size = 114988, upload-time = "2025-11-01T12:13:13.486Z" }, - { url = "https://files.pythonhosted.org/packages/e5/77/39ee0e8d3028e90178e1031530ccc98563f8f2f0d905ec784669dcf0fa90/levenshtein-0.27.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23aff800a6dd5d91bb3754a6092085aa7ad46b28e497682c155c74f681cfaa2d", size = 153346, upload-time = "2025-11-01T12:13:14.744Z" }, - { url = "https://files.pythonhosted.org/packages/3c/0d/c0f367bbd260dbd7a4e134fd21f459e0f5eac43deac507952b46a1d8a93a/levenshtein-0.27.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c08a952432b8ad9dccb145f812176db94c52cda732311ddc08d29fd3bf185b0a", size = 1114538, upload-time = "2025-11-01T12:13:15.851Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ef/ae71433f7b4db0bd2af7974785e36cdec899919203fb82e647c5a6109c07/levenshtein-0.27.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3bfcb2d78ab9cc06a1e75da8fcfb7a430fe513d66cfe54c07e50f32805e5e6db", size = 1009734, upload-time = "2025-11-01T12:13:17.212Z" }, - { url = "https://files.pythonhosted.org/packages/27/dc/62c28b812dcb0953fc32ab7adf3d0e814e43c8560bb28d9269a44d874adf/levenshtein-0.27.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7235f6dcb31a217247468295e2dd4c6c1d3ac81629dc5d355d93e1a5f4c185", size = 1185581, upload-time = "2025-11-01T12:13:18.661Z" }, - { url = "https://files.pythonhosted.org/packages/56/e8/2e7ab9c565793220edb8e5432f9a846386a157075bdd032a90e9585bce38/levenshtein-0.27.3-cp312-cp312-win32.whl", hash = "sha256:ea80d70f1d18c161a209be556b9094968627cbaae620e102459ef9c320a98cbb", size = 84660, upload-time = "2025-11-01T12:13:19.87Z" }, - { url = "https://files.pythonhosted.org/packages/2c/a6/907a1fc8587dc91c40156973e09d106ab064c06eb28dc4700ba0fe54d654/levenshtein-0.27.3-cp312-cp312-win_amd64.whl", hash = "sha256:fbaa1219d9b2d955339a37e684256a861e9274a3fe3a6ee1b8ea8724c3231ed9", size = 94909, upload-time = "2025-11-01T12:13:21.323Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d6/e04f0ddf6a71df3cdd1817b71703490ac874601ed460b2af172d3752c321/levenshtein-0.27.3-cp312-cp312-win_arm64.whl", hash = "sha256:2edbaa84f887ea1d9d8e4440af3fdda44769a7855d581c6248d7ee51518402a8", size = 87358, upload-time = "2025-11-01T12:13:22.393Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f2/162e9ea7490b36bbf05776c8e3a8114c75aa78546ddda8e8f36731db3da6/levenshtein-0.27.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e55aa9f9453fd89d4a9ff1f3c4a650b307d5f61a7eed0568a52fbd2ff2eba107", size = 169230, upload-time = "2025-11-01T12:13:23.735Z" }, - { url = "https://files.pythonhosted.org/packages/01/2d/7316ba7f94e3d60e89bd120526bc71e4812866bb7162767a2a10f73f72c5/levenshtein-0.27.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae4d484453c48939ecd01c5c213530c68dd5cd6e5090f0091ef69799ec7a8a9f", size = 158643, upload-time = "2025-11-01T12:13:25.549Z" }, - { url = "https://files.pythonhosted.org/packages/5e/87/85433cb1e51c45016f061d96fea3106b6969f700e2cbb56c15de82d0deeb/levenshtein-0.27.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d18659832567ee387b266be390da0de356a3aa6cf0e8bc009b6042d8188e131f", size = 132881, upload-time = "2025-11-01T12:13:26.822Z" }, - { url = "https://files.pythonhosted.org/packages/40/1c/3ce66c9a7da169a43dd89146d69df9dec935e6f86c70c6404f48d1291d2c/levenshtein-0.27.3-cp313-cp313-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027b3d142cc8ea2ab4e60444d7175f65a94dde22a54382b2f7b47cc24936eb53", size = 114650, upload-time = "2025-11-01T12:13:28.382Z" }, - { url = "https://files.pythonhosted.org/packages/73/60/7138e98884ca105c76ef192f5b43165d6eac6f32b432853ebe9f09ee50c9/levenshtein-0.27.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffdca6989368cc64f347f0423c528520f12775b812e170a0eb0c10e4c9b0f3ff", size = 153127, upload-time = "2025-11-01T12:13:29.781Z" }, - { url = "https://files.pythonhosted.org/packages/df/8f/664ac8b83026d7d1382866b68babae17e92b7b6ff8dc3c6205c0066b8ce1/levenshtein-0.27.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:fa00ab389386032b02a1c9050ec3c6aa824d2bbcc692548fdc44a46b71c058c6", size = 1114602, upload-time = "2025-11-01T12:13:31.651Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c8/8905d96cf2d7ed6af7eb39a8be0925ef335729473c1e9d1f56230ecaffc5/levenshtein-0.27.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:691c9003c6c481b899a5c2f72e8ce05a6d956a9668dc75f2a3ce9f4381a76dc6", size = 1008036, upload-time = "2025-11-01T12:13:33.006Z" }, - { url = "https://files.pythonhosted.org/packages/c7/57/01c37608121380a6357a297625562adad1c1fc8058d4f62279b735108927/levenshtein-0.27.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12f7fc8bf0c24492fe97905348e020b55b9fc6dbaab7cd452566d1a466cb5e15", size = 1185338, upload-time = "2025-11-01T12:13:34.452Z" }, - { url = "https://files.pythonhosted.org/packages/dd/57/bceab41d40b58dee7927a8d1d18ed3bff7c95c5e530fb60093ce741a8c26/levenshtein-0.27.3-cp313-cp313-win32.whl", hash = "sha256:9f4872e4e19ee48eed39f214eea4eca42e5ef303f8a4a488d8312370674dbf3a", size = 84562, upload-time = "2025-11-01T12:13:35.858Z" }, - { url = "https://files.pythonhosted.org/packages/42/1d/74f1ff589bb687d0cad2bbdceef208dc070f56d1e38a3831da8c00bf13bb/levenshtein-0.27.3-cp313-cp313-win_amd64.whl", hash = "sha256:83aa2422e9a9af2c9d3e56a53e3e8de6bae58d1793628cae48c4282577c5c2c6", size = 94658, upload-time = "2025-11-01T12:13:36.963Z" }, - { url = "https://files.pythonhosted.org/packages/21/3c/22c86d3c8f254141096fd6089d2e9fdf98b1472c7a5d79d36d3557ec2d83/levenshtein-0.27.3-cp313-cp313-win_arm64.whl", hash = "sha256:d4adaf1edbcf38c3f2e290b52f4dcb5c6deff20308c26ef1127a106bc2d23e9f", size = 86929, upload-time = "2025-11-01T12:13:37.997Z" }, - { url = "https://files.pythonhosted.org/packages/0e/bc/9b7cf1b5fa098b86844d42de22549304699deff309c5c9e28b9a3fc4076a/levenshtein-0.27.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:272e24764b8210337b65a1cfd69ce40df5d2de1a3baf1234e7f06d2826ba2e7a", size = 170360, upload-time = "2025-11-01T12:13:39.019Z" }, - { url = "https://files.pythonhosted.org/packages/dc/95/997f2c83bd4712426bf0de8143b5e4403c7ebbafb5d1271983e774de3ae7/levenshtein-0.27.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:329a8e748a4e14d56daaa11f07bce3fde53385d05bad6b3f6dd9ee7802cdc915", size = 159098, upload-time = "2025-11-01T12:13:40.17Z" }, - { url = "https://files.pythonhosted.org/packages/fc/96/123c3316ae2f72c73be4fba9756924af015da4c0e5b12804f5753c0ee511/levenshtein-0.27.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5fea1a9c6b9cc8729e467e2174b4359ff6bac27356bb5f31898e596b4ce133a", size = 136655, upload-time = "2025-11-01T12:13:41.262Z" }, - { url = "https://files.pythonhosted.org/packages/45/72/a3180d437736b1b9eacc3100be655a756deafb91de47c762d40eb45a9d91/levenshtein-0.27.3-cp313-cp313t-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3a61aa825819b6356555091d8a575d1235bd9c3753a68316a261af4856c3b487", size = 117511, upload-time = "2025-11-01T12:13:42.647Z" }, - { url = "https://files.pythonhosted.org/packages/61/f9/ba7c546a4b99347938e6661104064ab6a3651c601d59f241ffdc37510ecc/levenshtein-0.27.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a51de7a514e8183f0a82f2947d01b014d2391426543b1c076bf5a26328cec4e4", size = 155656, upload-time = "2025-11-01T12:13:44.208Z" }, - { url = "https://files.pythonhosted.org/packages/42/cd/5edd6e1e02c3e47c8121761756dd0f85f816b636f25509118b687e6b0f96/levenshtein-0.27.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53cbf726d6e92040c9be7e594d959d496bd62597ea48eba9d96105898acbeafe", size = 1116689, upload-time = "2025-11-01T12:13:45.485Z" }, - { url = "https://files.pythonhosted.org/packages/95/67/25ca0119e0c6ec17226c72638f48ef8887124597ac48ad5da111c0b3a825/levenshtein-0.27.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:191b358afead8561c4fcfed22f83c13bb6c8da5f5789e277f0c5aa1c45ca612f", size = 1003166, upload-time = "2025-11-01T12:13:47.126Z" }, - { url = "https://files.pythonhosted.org/packages/45/64/ab216f3fb3cef1ee7e222665537f9340d828ef84c99409ba31f2ef2a3947/levenshtein-0.27.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ba1318d0635b834b8f0397014a7c43f007e65fce396a47614780c881bdff828b", size = 1189362, upload-time = "2025-11-01T12:13:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/31/58/b150034858de0899a5a222974b6710618ebc0779a0695df070f7ab559a0b/levenshtein-0.27.3-cp313-cp313t-win32.whl", hash = "sha256:8dd9e1db6c3b35567043e155a686e4827c4aa28a594bd81e3eea84d3a1bd5875", size = 86149, upload-time = "2025-11-01T12:13:50.588Z" }, - { url = "https://files.pythonhosted.org/packages/0a/c4/bbe46a11073641450200e6a604b3b62d311166e8061c492612a40e560e85/levenshtein-0.27.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7813ecdac7a6223264ebfea0c8d69959c43d21a99694ef28018d22c4265c2af6", size = 96685, upload-time = "2025-11-01T12:13:51.641Z" }, - { url = "https://files.pythonhosted.org/packages/23/65/30b362ad9bfc1085741776a08b6ddee3f434e9daac2920daaee2e26271bf/levenshtein-0.27.3-cp313-cp313t-win_arm64.whl", hash = "sha256:8f05a0d23d13a6f802c7af595d0e43f5b9b98b6ed390cec7a35cb5d6693b882b", size = 88538, upload-time = "2025-11-01T12:13:52.757Z" }, - { url = "https://files.pythonhosted.org/packages/f3/e1/2f705da403f865a5fa3449b155738dc9c53021698fd6926253a9af03180b/levenshtein-0.27.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a6728bfae9a86002f0223576675fc7e2a6e7735da47185a1d13d1eaaa73dd4be", size = 169457, upload-time = "2025-11-01T12:13:53.778Z" }, - { url = "https://files.pythonhosted.org/packages/76/2c/bb6ef359e007fe7b6b3195b68a94f4dd3ecd1885ee337ee8fbd4df55996f/levenshtein-0.27.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8e5037c4a6f97a238e24aad6f98a1e984348b7931b1b04b6bd02bd4f8238150d", size = 158680, upload-time = "2025-11-01T12:13:55.005Z" }, - { url = "https://files.pythonhosted.org/packages/51/7b/de1999f4cf1cfebc3fbbf03a6d58498952d6560d9798af4b0a566e6b6f30/levenshtein-0.27.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6cf5ecf9026bf24cf66ad019c6583f50058fae3e1b3c20e8812455b55d597f1", size = 133167, upload-time = "2025-11-01T12:13:56.426Z" }, - { url = "https://files.pythonhosted.org/packages/c7/da/aaa7f3a0a8ae8744b284043653652db3d7d93595517f9ed8158c03287692/levenshtein-0.27.3-cp314-cp314-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9285084bd2fc19adb47dab54ed4a71f57f78fe0d754e4a01e3c75409a25aed24", size = 114530, upload-time = "2025-11-01T12:13:57.883Z" }, - { url = "https://files.pythonhosted.org/packages/29/ce/ed422816fb30ffa3bc11597b30d5deca06b4a1388707a04215da73c65b53/levenshtein-0.27.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce3bbbe92172a08b599d79956182c6b7ab6ec8d4adbe7237417a363b968ad87b", size = 153325, upload-time = "2025-11-01T12:13:59.318Z" }, - { url = "https://files.pythonhosted.org/packages/d9/5a/a225477a0bda154f19f1c07a5e35500d631ae25dfd620b479027d79f0d4c/levenshtein-0.27.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9dac48fab9d166ca90e12fb6cf6c7c8eb9c41aacf7136584411e20f7f136f745", size = 1114956, upload-time = "2025-11-01T12:14:00.543Z" }, - { url = "https://files.pythonhosted.org/packages/ca/c4/a1be1040f3cce516a5e2be68453fd0c32ac63b2e9d31f476723fd8002c09/levenshtein-0.27.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d37a83722dc5326c93d17078e926c4732dc4f3488dc017c6839e34cd16af92b7", size = 1007610, upload-time = "2025-11-01T12:14:02.036Z" }, - { url = "https://files.pythonhosted.org/packages/86/d7/6f50e8a307e0c2befd819b481eb3a4c2eacab3dd8101982423003fac8ea3/levenshtein-0.27.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3466cb8294ce586e49dd467560a153ab8d296015c538223f149f9aefd3d9f955", size = 1185379, upload-time = "2025-11-01T12:14:03.385Z" }, - { url = "https://files.pythonhosted.org/packages/6b/e5/5d8fb1b3ebd5735f53221bf95c923066bcfc132234925820128f7eee5b47/levenshtein-0.27.3-cp314-cp314-win32.whl", hash = "sha256:c848bf2457b268672b7e9e73b44f18f49856420ac50b2564cf115a6e4ef82688", size = 86328, upload-time = "2025-11-01T12:14:04.74Z" }, - { url = "https://files.pythonhosted.org/packages/30/82/8a9ccbdb4e38bd4d516f2804999dccb8cb4bcb4e33f52851735da0c73ea7/levenshtein-0.27.3-cp314-cp314-win_amd64.whl", hash = "sha256:742633f024362a4ed6ef9d7e75d68f74b041ae738985fcf55a0e6d1d4cade438", size = 96640, upload-time = "2025-11-01T12:14:06.24Z" }, - { url = "https://files.pythonhosted.org/packages/14/86/f9d15919f59f5d92c6baa500315e1fa0143a39d811427b83c54f038267ca/levenshtein-0.27.3-cp314-cp314-win_arm64.whl", hash = "sha256:9eed6851224b19e8d588ddb8eb8a4ae3c2dcabf3d1213985f0b94a67e517b1df", size = 89689, upload-time = "2025-11-01T12:14:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/ed/f6/10f44975ae6dc3047b2cd260e3d4c3a5258b8d10690a42904115de24fc51/levenshtein-0.27.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:77de69a345c76227b51a4521cd85442eb3da54c7eb6a06663a20c058fc49e683", size = 170518, upload-time = "2025-11-01T12:14:09.196Z" }, - { url = "https://files.pythonhosted.org/packages/08/07/fa294a145a0c99a814a9a807614962c1ee0f5749ca691645980462027d5d/levenshtein-0.27.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:eba2756dc1f5b962b0ff80e49abb2153d5e809cc5e7fa5e85be9410ce474795d", size = 159097, upload-time = "2025-11-01T12:14:10.404Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/24bdf37813fc30f293e53b46022b091144f4737a6a66663d2235b311bb98/levenshtein-0.27.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c8fcb498287e971d84260f67808ff1a06b3f6212d80fea75cf5155db80606ff", size = 136650, upload-time = "2025-11-01T12:14:11.579Z" }, - { url = "https://files.pythonhosted.org/packages/d0/a9/0399c7a190b277cdea3acc801129d9d30da57c3fa79519e7b8c3f080d86c/levenshtein-0.27.3-cp314-cp314t-manylinux_2_24_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f067092c67464faab13e00a5c1a80da93baca8955d4d49579861400762e35591", size = 117515, upload-time = "2025-11-01T12:14:12.877Z" }, - { url = "https://files.pythonhosted.org/packages/bf/a4/1c27533e97578b385a4b8079abe8d1ce2e514717c761efbe4bf7bbd0ac2e/levenshtein-0.27.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92415f32c68491203f2855d05eef3277d376182d014cf0859c013c89f277fbbf", size = 155711, upload-time = "2025-11-01T12:14:13.985Z" }, - { url = "https://files.pythonhosted.org/packages/50/35/bbc26638394a72b1e31a685ec251c995ee66a630c7e5c86f98770928b632/levenshtein-0.27.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ef61eeaf1e0a42d7d947978d981fe4b9426b98b3dd8c1582c535f10dee044c3f", size = 1116692, upload-time = "2025-11-01T12:14:15.359Z" }, - { url = "https://files.pythonhosted.org/packages/cd/83/32fcf28b388f8dc6c36b54552b9bae289dab07d43df104893158c834cbcc/levenshtein-0.27.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:103bb2e9049d1aa0d1216dd09c1c9106ecfe7541bbdc1a0490b9357d42eec8f2", size = 1003167, upload-time = "2025-11-01T12:14:17.469Z" }, - { url = "https://files.pythonhosted.org/packages/d1/79/1fbf2877ec4b819f373a32ebe3c48a61ee810693593a6015108b0be97b78/levenshtein-0.27.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6a64ddd1986b2a4c468b09544382287315c53585eb067f6e200c337741e057ee", size = 1189417, upload-time = "2025-11-01T12:14:19.081Z" }, - { url = "https://files.pythonhosted.org/packages/d3/ac/dad4e09f1f7459c64172e48e40ed2baf3aa92d38205bcbd1b4ff00853701/levenshtein-0.27.3-cp314-cp314t-win32.whl", hash = "sha256:957244f27dc284ccb030a8b77b8a00deb7eefdcd70052a4b1d96f375780ae9dc", size = 88144, upload-time = "2025-11-01T12:14:20.667Z" }, - { url = "https://files.pythonhosted.org/packages/c0/61/cd51dc8b8a382e17c559a9812734c3a9afc2dab7d36253516335ee16ae50/levenshtein-0.27.3-cp314-cp314t-win_amd64.whl", hash = "sha256:ccd7eaa6d8048c3ec07c93cfbcdefd4a3ae8c6aca3a370f2023ee69341e5f076", size = 98516, upload-time = "2025-11-01T12:14:21.786Z" }, - { url = "https://files.pythonhosted.org/packages/27/5e/3fb67e882c1fee01ebb7abc1c0a6669e5ff8acd060e93bfe7229e9ce6e4f/levenshtein-0.27.3-cp314-cp314t-win_arm64.whl", hash = "sha256:1d8520b89b7a27bb5aadbcc156715619bcbf556a8ac46ad932470945dca6e1bd", size = 91020, upload-time = "2025-11-01T12:14:22.944Z" }, -] - -[[package]] -name = "librt" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/6b/3d5c13fb3e3c4f43206c8f9dfed13778c2ed4f000bacaa0b7ce3c402a265/librt-0.9.0.tar.gz", hash = "sha256:a0951822531e7aee6e0dfb556b30d5ee36bbe234faf60c20a16c01be3530869d", size = 184368, upload-time = "2026-04-09T16:06:26.173Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/90/89ddba8e1c20b0922783cd93ed8e64f34dc05ab59c38a9c7e313632e20ff/librt-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9b3e3bc363f71bda1639a4ee593cb78f7fbfeacc73411ec0d4c92f00730010a4", size = 68332, upload-time = "2026-04-09T16:05:00.09Z" }, - { url = "https://files.pythonhosted.org/packages/a8/40/7aa4da1fb08bdeeb540cb07bfc8207cb32c5c41642f2594dbd0098a0662d/librt-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0a09c2f5869649101738653a9b7ab70cf045a1105ac66cbb8f4055e61df78f2d", size = 70581, upload-time = "2026-04-09T16:05:01.213Z" }, - { url = "https://files.pythonhosted.org/packages/48/ac/73a2187e1031041e93b7e3a25aae37aa6f13b838c550f7e0f06f66766212/librt-0.9.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ca8e133d799c948db2ab1afc081c333a825b5540475164726dcbf73537e5c2f", size = 203984, upload-time = "2026-04-09T16:05:02.542Z" }, - { url = "https://files.pythonhosted.org/packages/5e/3d/23460d571e9cbddb405b017681df04c142fb1b04cbfce77c54b08e28b108/librt-0.9.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:603138ee838ee1583f1b960b62d5d0007845c5c423feb68e44648b1359014e27", size = 215762, upload-time = "2026-04-09T16:05:04.127Z" }, - { url = "https://files.pythonhosted.org/packages/de/1e/42dc7f8ab63e65b20640d058e63e97fd3e482c1edbda3570d813b4d0b927/librt-0.9.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4003f70c56a5addd6aa0897f200dd59afd3bf7bcd5b3cce46dd21f925743bc2", size = 230288, upload-time = "2026-04-09T16:05:05.883Z" }, - { url = "https://files.pythonhosted.org/packages/dc/08/ca812b6d8259ad9ece703397f8ad5c03af5b5fedfce64279693d3ce4087c/librt-0.9.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:78042f6facfd98ecb25e9829c7e37cce23363d9d7c83bc5f72702c5059eb082b", size = 224103, upload-time = "2026-04-09T16:05:07.148Z" }, - { url = "https://files.pythonhosted.org/packages/b6/3f/620490fb2fa66ffd44e7f900254bc110ebec8dac6c1b7514d64662570e6f/librt-0.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a361c9434a64d70a7dbb771d1de302c0cc9f13c0bffe1cf7e642152814b35265", size = 232122, upload-time = "2026-04-09T16:05:08.386Z" }, - { url = "https://files.pythonhosted.org/packages/e9/83/12864700a1b6a8be458cf5d05db209b0d8e94ae281e7ec261dbe616597b4/librt-0.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:dd2c7e082b0b92e1baa4da28163a808672485617bc855cc22a2fd06978fa9084", size = 225045, upload-time = "2026-04-09T16:05:09.707Z" }, - { url = "https://files.pythonhosted.org/packages/fd/1b/845d339c29dc7dbc87a2e992a1ba8d28d25d0e0372f9a0a2ecebde298186/librt-0.9.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7e6274fd33fc5b2a14d41c9119629d3ff395849d8bcbc80cf637d9e8d2034da8", size = 227372, upload-time = "2026-04-09T16:05:10.942Z" }, - { url = "https://files.pythonhosted.org/packages/8d/fe/277985610269d926a64c606f761d58d3db67b956dbbf40024921e95e7fcb/librt-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5093043afb226ecfa1400120d1ebd4442b4f99977783e4f4f7248879009b227f", size = 248224, upload-time = "2026-04-09T16:05:12.254Z" }, - { url = "https://files.pythonhosted.org/packages/92/1b/ee486d244b8de6b8b5dbaefabe6bfdd4a72e08f6353edf7d16d27114da8d/librt-0.9.0-cp312-cp312-win32.whl", hash = "sha256:9edcc35d1cae9fd5320171b1a838c7da8a5c968af31e82ecc3dff30b4be0957f", size = 55986, upload-time = "2026-04-09T16:05:13.529Z" }, - { url = "https://files.pythonhosted.org/packages/89/7a/ba1737012308c17dc6d5516143b5dce9a2c7ba3474afd54e11f44a4d1ef3/librt-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc2917258e131ae5f958a4d872e07555b51cb7466a43433218061c74ef33745", size = 63260, upload-time = "2026-04-09T16:05:14.68Z" }, - { url = "https://files.pythonhosted.org/packages/36/e4/01752c113da15127f18f7bf11142f5640038f062407a611c059d0036c6aa/librt-0.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:90e6d5420fc8a300518d4d2288154ff45005e920425c22cbbfe8330f3f754bd9", size = 53694, upload-time = "2026-04-09T16:05:16.095Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d7/1b3e26fffde1452d82f5666164858a81c26ebe808e7ae8c9c88628981540/librt-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29b68cd9714531672db62cc54f6e8ff981900f824d13fa0e00749189e13778e", size = 68367, upload-time = "2026-04-09T16:05:17.243Z" }, - { url = "https://files.pythonhosted.org/packages/a5/5b/c61b043ad2e091fbe1f2d35d14795e545d0b56b03edaa390fa1dcee3d160/librt-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7d5c8a5929ac325729f6119802070b561f4db793dffc45e9ac750992a4ed4d22", size = 70595, upload-time = "2026-04-09T16:05:18.471Z" }, - { url = "https://files.pythonhosted.org/packages/a3/22/2448471196d8a73370aa2f23445455dc42712c21404081fcd7a03b9e0749/librt-0.9.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:756775d25ec8345b837ab52effee3ad2f3b2dfd6bbee3e3f029c517bd5d8f05a", size = 204354, upload-time = "2026-04-09T16:05:19.593Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5e/39fc4b153c78cfd2c8a2dcb32700f2d41d2312aa1050513183be4540930d/librt-0.9.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8f5d00b49818f4e2b1667db994488b045835e0ac16fe2f924f3871bd2b8ac5", size = 216238, upload-time = "2026-04-09T16:05:20.868Z" }, - { url = "https://files.pythonhosted.org/packages/d7/42/bc2d02d0fa7badfa63aa8d6dcd8793a9f7ef5a94396801684a51ed8d8287/librt-0.9.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c81aef782380f0f13ead670aae01825eb653b44b046aa0e5ebbb79f76ed4aa11", size = 230589, upload-time = "2026-04-09T16:05:22.305Z" }, - { url = "https://files.pythonhosted.org/packages/c8/7b/e2d95cc513866373692aa5edf98080d5602dd07cabfb9e5d2f70df2f25f7/librt-0.9.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66b58fed90a545328e80d575467244de3741e088c1af928f0b489ebec3ef3858", size = 224610, upload-time = "2026-04-09T16:05:23.647Z" }, - { url = "https://files.pythonhosted.org/packages/31/d5/6cec4607e998eaba57564d06a1295c21b0a0c8de76e4e74d699e627bd98c/librt-0.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e78fb7419e07d98c2af4b8567b72b3eaf8cb05caad642e9963465569c8b2d87e", size = 232558, upload-time = "2026-04-09T16:05:25.025Z" }, - { url = "https://files.pythonhosted.org/packages/95/8c/27f1d8d3aaf079d3eb26439bf0b32f1482340c3552e324f7db9dca858671/librt-0.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c3786f0f4490a5cd87f1ed6cefae833ad6b1060d52044ce0434a2e85893afd0", size = 225521, upload-time = "2026-04-09T16:05:26.311Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d8/1e0d43b1c329b416017619469b3c3801a25a6a4ef4a1c68332aeaa6f72ca/librt-0.9.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8494cfc61e03542f2d381e71804990b3931175a29b9278fdb4a5459948778dc2", size = 227789, upload-time = "2026-04-09T16:05:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/2c/b4/d3d842e88610fcd4c8eec7067b0c23ef2d7d3bff31496eded6a83b0f99be/librt-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:07cf11f769831186eeac424376e6189f20ace4f7263e2134bdb9757340d84d4d", size = 248616, upload-time = "2026-04-09T16:05:29.181Z" }, - { url = "https://files.pythonhosted.org/packages/ec/28/527df8ad0d1eb6c8bdfa82fc190f1f7c4cca5a1b6d7b36aeabf95b52d74d/librt-0.9.0-cp313-cp313-win32.whl", hash = "sha256:850d6d03177e52700af605fd60db7f37dcb89782049a149674d1a9649c2138fd", size = 56039, upload-time = "2026-04-09T16:05:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/f3/a7/413652ad0d92273ee5e30c000fc494b361171177c83e57c060ecd3c21538/librt-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:a5af136bfba820d592f86c67affcef9b3ff4d4360ac3255e341e964489b48519", size = 63264, upload-time = "2026-04-09T16:05:31.881Z" }, - { url = "https://files.pythonhosted.org/packages/a4/0a/92c244309b774e290ddb15e93363846ae7aa753d9586b8aad511c5e6145b/librt-0.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:4c4d0440a3a8e31d962340c3e1cc3fc9ee7febd34c8d8f770d06adb947779ea5", size = 53728, upload-time = "2026-04-09T16:05:33.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c1/184e539543f06ea2912f4b92a5ffaede4f9b392689e3f00acbf8134bee92/librt-0.9.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:3f05d145df35dca5056a8bc3838e940efebd893a54b3e19b2dda39ceaa299bcb", size = 67830, upload-time = "2026-04-09T16:05:34.517Z" }, - { url = "https://files.pythonhosted.org/packages/f3/ad/23399bdcb7afca819acacdef31b37ee59de261bd66b503a7995c03c4b0dc/librt-0.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1c587494461ebd42229d0f1739f3aa34237dd9980623ecf1be8d3bcba79f4499", size = 70280, upload-time = "2026-04-09T16:05:35.649Z" }, - { url = "https://files.pythonhosted.org/packages/9f/0b/4542dc5a2b8772dbf92cafb9194701230157e73c14b017b6961a23598b03/librt-0.9.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b0a2040f801406b93657a70b72fa12311063a319fee72ce98e1524da7200171f", size = 201925, upload-time = "2026-04-09T16:05:36.739Z" }, - { url = "https://files.pythonhosted.org/packages/31/d4/8ee7358b08fd0cfce051ef96695380f09b3c2c11b77c9bfbc367c921cce5/librt-0.9.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f38bc489037eca88d6ebefc9c4d41a4e07c8e8b4de5188a9e6d290273ad7ebb1", size = 212381, upload-time = "2026-04-09T16:05:38.043Z" }, - { url = "https://files.pythonhosted.org/packages/f2/94/a2025fe442abedf8b038038dab3dba942009ad42b38ea064a1a9e6094241/librt-0.9.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3fd278f5e6bf7c75ccd6d12344eb686cc020712683363b66f46ac79d37c799f", size = 227065, upload-time = "2026-04-09T16:05:39.394Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e9/b9fcf6afa909f957cfbbf918802f9dada1bd5d3c1da43d722fd6a310dc3f/librt-0.9.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fcbdf2a9ca24e87bbebb47f1fe34e531ef06f104f98c9ccfc953a3f3344c567a", size = 221333, upload-time = "2026-04-09T16:05:40.999Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7c/ba54cd6aa6a3c8cd12757a6870e0c79a64b1e6327f5248dcff98423f4d43/librt-0.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e306d956cfa027fe041585f02a1602c32bfa6bb8ebea4899d373383295a6c62f", size = 229051, upload-time = "2026-04-09T16:05:42.605Z" }, - { url = "https://files.pythonhosted.org/packages/4b/4b/8cfdbad314c8677a0148bf0b70591d6d18587f9884d930276098a235461b/librt-0.9.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:465814ab157986acb9dfa5ccd7df944be5eefc0d08d31ec6e8d88bc71251d845", size = 222492, upload-time = "2026-04-09T16:05:43.842Z" }, - { url = "https://files.pythonhosted.org/packages/1f/d1/2eda69563a1a88706808decdce035e4b32755dbfbb0d05e1a65db9547ed1/librt-0.9.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:703f4ae36d6240bfe24f542bac784c7e4194ec49c3ba5a994d02891649e2d85b", size = 223849, upload-time = "2026-04-09T16:05:45.054Z" }, - { url = "https://files.pythonhosted.org/packages/04/44/b2ed37df6be5b3d42cfe36318e0598e80843d5c6308dd63d0bf4e0ce5028/librt-0.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3be322a15ee5e70b93b7a59cfd074614f22cc8c9ff18bd27f474e79137ea8d3b", size = 245001, upload-time = "2026-04-09T16:05:46.34Z" }, - { url = "https://files.pythonhosted.org/packages/47/e7/617e412426df89169dd2a9ed0cc8752d5763336252c65dbf945199915119/librt-0.9.0-cp314-cp314-win32.whl", hash = "sha256:b8da9f8035bb417770b1e1610526d87ad4fc58a2804dc4d79c53f6d2cf5a6eb9", size = 51799, upload-time = "2026-04-09T16:05:47.738Z" }, - { url = "https://files.pythonhosted.org/packages/24/ed/c22ca4db0ca3cbc285e4d9206108746beda561a9792289c3c31281d7e9df/librt-0.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:b8bd70d5d816566a580d193326912f4a76ec2d28a97dc4cd4cc831c0af8e330e", size = 59165, upload-time = "2026-04-09T16:05:49.198Z" }, - { url = "https://files.pythonhosted.org/packages/24/56/875398fafa4cbc8f15b89366fc3287304ddd3314d861f182a4b87595ace0/librt-0.9.0-cp314-cp314-win_arm64.whl", hash = "sha256:fc5758e2b7a56532dc33e3c544d78cbaa9ecf0a0f2a2da2df882c1d6b99a317f", size = 49292, upload-time = "2026-04-09T16:05:50.362Z" }, - { url = "https://files.pythonhosted.org/packages/4c/61/bc448ecbf9b2d69c5cff88fe41496b19ab2a1cbda0065e47d4d0d51c0867/librt-0.9.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f24b90b0e0c8cc9491fb1693ae91fe17cb7963153a1946395acdbdd5818429a4", size = 70175, upload-time = "2026-04-09T16:05:51.564Z" }, - { url = "https://files.pythonhosted.org/packages/60/f2/c47bb71069a73e2f04e70acbd196c1e5cc411578ac99039a224b98920fd4/librt-0.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fe56e80badb66fdcde06bef81bbaa5bfcf6fbd7aefb86222d9e369c38c6b228", size = 72951, upload-time = "2026-04-09T16:05:52.699Z" }, - { url = "https://files.pythonhosted.org/packages/29/19/0549df59060631732df758e8886d92088da5fdbedb35b80e4643664e8412/librt-0.9.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:527b5b820b47a09e09829051452bb0d1dd2122261254e2a6f674d12f1d793d54", size = 225864, upload-time = "2026-04-09T16:05:53.895Z" }, - { url = "https://files.pythonhosted.org/packages/9d/f8/3b144396d302ac08e50f89e64452c38db84bc7b23f6c60479c5d3abd303c/librt-0.9.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d429bdd4ac0ab17c8e4a8af0ed2a7440b16eba474909ab357131018fe8c7e71", size = 241155, upload-time = "2026-04-09T16:05:55.191Z" }, - { url = "https://files.pythonhosted.org/packages/7a/ce/ee67ec14581de4043e61d05786d2aed6c9b5338816b7859bcf07455c6a9f/librt-0.9.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7202bdcac47d3a708271c4304a474a8605a4a9a4a709e954bf2d3241140aa938", size = 252235, upload-time = "2026-04-09T16:05:56.549Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fa/0ead15daa2b293a54101550b08d4bafe387b7d4a9fc6d2b985602bae69b6/librt-0.9.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0d620e74897f8c2613b3c4e2e9c1e422eb46d2ddd07df540784d44117836af3", size = 244963, upload-time = "2026-04-09T16:05:57.858Z" }, - { url = "https://files.pythonhosted.org/packages/29/68/9fbf9a9aa704ba87689e40017e720aced8d9a4d2b46b82451d8142f91ec9/librt-0.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d69fc39e627908f4c03297d5a88d9284b73f4d90b424461e32e8c2485e21c283", size = 257364, upload-time = "2026-04-09T16:05:59.686Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8d/9d60869f1b6716c762e45f66ed945b1e5dd649f7377684c3b176ae424648/librt-0.9.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:c2640e23d2b7c98796f123ffd95cf2022c7777aa8a4a3b98b36c570d37e85eee", size = 247661, upload-time = "2026-04-09T16:06:00.938Z" }, - { url = "https://files.pythonhosted.org/packages/70/ff/a5c365093962310bfdb4f6af256f191085078ffb529b3f0cbebb5b33ebe2/librt-0.9.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:451daa98463b7695b0a30aa56bf637831ea559e7b8101ac2ef6382e8eb15e29c", size = 248238, upload-time = "2026-04-09T16:06:02.537Z" }, - { url = "https://files.pythonhosted.org/packages/a0/3c/2d34365177f412c9e19c0a29f969d70f5343f27634b76b765a54d8b27705/librt-0.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:928bd06eca2c2bbf4349e5b817f837509b0604342e65a502de1d50a7570afd15", size = 269457, upload-time = "2026-04-09T16:06:03.833Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/de45b239ea3bdf626f982a00c14bfcf2e12d261c510ba7db62c5969a27cd/librt-0.9.0-cp314-cp314t-win32.whl", hash = "sha256:a9c63e04d003bc0fb6a03b348018b9a3002f98268200e22cc80f146beac5dc40", size = 52453, upload-time = "2026-04-09T16:06:05.229Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f9/bfb32ae428aa75c0c533915622176f0a17d6da7b72b5a3c6363685914f70/librt-0.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f162af66a2ed3f7d1d161a82ca584efd15acd9c1cff190a373458c32f7d42118", size = 60044, upload-time = "2026-04-09T16:06:06.398Z" }, - { url = "https://files.pythonhosted.org/packages/aa/47/7d70414bcdbb3bc1f458a8d10558f00bbfdb24e5a11740fc8197e12c3255/librt-0.9.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a4b25c6c25cac5d0d9d6d6da855195b254e0021e513e0249f0e3b444dc6e0e61", size = 50009, upload-time = "2026-04-09T16:06:07.995Z" }, -] - -[[package]] -name = "markdown-it-py" -version = "4.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, -] - -[[package]] -name = "markupsafe" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, - { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, - { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, - { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, - { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, - { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, - { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, - { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, - { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, - { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, - { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, - { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, - { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, - { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, - { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, - { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, - { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, - { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, - { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, - { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, - { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, - { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, - { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, - { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, - { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, - { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, - { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, - { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, - { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, - { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, - { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, - { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, - { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, - { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, - { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, - { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, - { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, - { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, - { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, - { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, - { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, -] - -[[package]] -name = "matplotlib" -version = "3.10.9" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, - { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, - { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, - { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, - { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, - { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, - { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, - { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, - { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, - { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, - { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, - { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, - { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, - { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, - { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, - { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, - { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, - { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, - { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, - { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, - { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, - { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, - { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, - { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, - { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, - { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, - { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, - { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, - { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, -] - -[[package]] -name = "matplotlib-inline" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, -] - -[[package]] -name = "mistune" -version = "3.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/a5/2dab368d6950e6808904dec98f54c7e726ee7be4a0c6afe00e6e011bd52d/mistune-3.3.3.tar.gz", hash = "sha256:c4c6c0c840b8637a2e9b8b6d607eb7c8f00888bf14c754409bcd339e848c2477", size = 115363, upload-time = "2026-07-09T06:18:05.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/89/70/b1e4737b84163db5bb1dfde6f216dbfbf32783330a9989c965e121172830/mistune-3.3.3-py3-none-any.whl", hash = "sha256:99de1585e42dcbd826faa9e11a202727a5e202e4e4722a4c69ac1ff615793dd7", size = 63569, upload-time = "2026-07-09T06:18:03.839Z" }, -] - -[[package]] -name = "more-itertools" -version = "11.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/f7/139d22fef48ac78127d18e01d80cf1be40236ae489769d17f35c3d425293/more_itertools-11.0.2.tar.gz", hash = "sha256:392a9e1e362cbc106a2457d37cabf9b36e5e12efd4ebff1654630e76597df804", size = 144659, upload-time = "2026-04-09T15:01:33.297Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/98/6af411189d9413534c3eb691182bff1f5c6d44ed2f93f2edfe52a1bbceb8/more_itertools-11.0.2-py3-none-any.whl", hash = "sha256:6e35b35f818b01f691643c6c611bc0902f2e92b46c18fffa77ae1e7c46e912e4", size = 71939, upload-time = "2026-04-09T15:01:32.21Z" }, -] - -[[package]] -name = "music21" -source = { editable = "." } -dependencies = [ - { name = "chardet" }, - { name = "joblib" }, - { name = "jsonpickle" }, - { name = "matplotlib" }, - { name = "more-itertools" }, - { name = "numpy" }, - { name = "requests" }, - { name = "webcolors" }, -] - -[package.optional-dependencies] -extras = [ - { name = "python-levenshtein" }, - { name = "scipy" }, -] - -[package.dev-dependencies] -dev = [ - { name = "coverage" }, - { name = "coveralls" }, - { name = "docutils" }, - { name = "hatch" }, - { name = "hatchling" }, - { name = "ipython" }, - { name = "jupyter" }, - { name = "music21", extra = ["extras"] }, - { name = "mypy" }, - { name = "nbconvert" }, - { name = "nbval" }, - { name = "pylint" }, - { name = "pytest" }, - { name = "ruff" }, - { name = "sphinx" }, - { name = "twine" }, - { name = "types-requests" }, - { name = "wheel" }, -] - -[package.metadata] -requires-dist = [ - { name = "chardet" }, - { name = "joblib" }, - { name = "jsonpickle" }, - { name = "matplotlib" }, - { name = "more-itertools" }, - { name = "numpy", specifier = ">=1.26.4" }, - { name = "python-levenshtein", marker = "extra == 'extras'" }, - { name = "requests" }, - { name = "scipy", marker = "extra == 'extras'" }, - { name = "webcolors", specifier = ">=1.5" }, -] -provides-extras = ["extras"] - -[package.metadata.requires-dev] -dev = [ - { name = "coverage" }, - { name = "coveralls" }, - { name = "docutils" }, - { name = "hatch" }, - { name = "hatchling" }, - { name = "ipython" }, - { name = "jupyter" }, - { name = "music21", extras = ["extras"] }, - { name = "mypy", specifier = ">=1.16.0" }, - { name = "nbconvert" }, - { name = "nbval" }, - { name = "pylint", specifier = ">=3.2.0" }, - { name = "pytest" }, - { name = "ruff" }, - { name = "sphinx" }, - { name = "twine" }, - { name = "types-requests" }, - { name = "wheel" }, -] - -[[package]] -name = "mypy" -version = "1.20.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/af/e3d4b3e9ec91a0ff9aabfdb38692952acf49bbb899c2e4c29acb3a6da3ae/mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665", size = 3817349, upload-time = "2026-04-21T17:12:28.473Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/4e/7560e4528db9e9b147e4c0f22660466bf30a0a1fe3d63d1b9d3b0fd354ee/mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b", size = 14539393, upload-time = "2026-04-21T17:07:12.52Z" }, - { url = "https://files.pythonhosted.org/packages/32/d9/34a5efed8124f5a9234f55ac6a4ced4201e2c5b81e1109c49ad23190ec8c/mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4", size = 13361642, upload-time = "2026-04-21T17:06:53.742Z" }, - { url = "https://files.pythonhosted.org/packages/d1/14/eb377acf78c03c92d566a1510cda8137348215b5335085ef662ab82ecd3a/mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6", size = 13740347, upload-time = "2026-04-21T17:12:04.73Z" }, - { url = "https://files.pythonhosted.org/packages/b9/94/7e4634a32b641aa1c112422eed1bbece61ee16205f674190e8b536f884de/mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066", size = 14734042, upload-time = "2026-04-21T17:07:43.16Z" }, - { url = "https://files.pythonhosted.org/packages/7a/f3/f7e62395cb7f434541b4491a01149a4439e28ace4c0c632bbf5431e92d1f/mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102", size = 14964958, upload-time = "2026-04-21T17:11:00.665Z" }, - { url = "https://files.pythonhosted.org/packages/3e/0d/47e3c3a0ec2a876e35aeac365df3cac7776c36bbd4ed18cc521e1b9d255b/mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9", size = 10911340, upload-time = "2026-04-21T17:10:49.179Z" }, - { url = "https://files.pythonhosted.org/packages/d6/b2/6c852d72e0ea8b01f49da817fb52539993cde327e7d010e0103dc12d0dac/mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58", size = 9833947, upload-time = "2026-04-21T17:09:05.267Z" }, - { url = "https://files.pythonhosted.org/packages/5b/c4/b93812d3a192c9bcf5df405bd2f30277cd0e48106a14d1023c7f6ed6e39b/mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026", size = 14524670, upload-time = "2026-04-21T17:10:30.737Z" }, - { url = "https://files.pythonhosted.org/packages/f3/47/42c122501bff18eaf1e8f457f5c017933452d8acdc52918a9f59f6812955/mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943", size = 13336218, upload-time = "2026-04-21T17:08:44.069Z" }, - { url = "https://files.pythonhosted.org/packages/92/8f/75bbc92f41725fbd585fb17b440b1119b576105df1013622983e18640a93/mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517", size = 13724906, upload-time = "2026-04-21T17:08:01.02Z" }, - { url = "https://files.pythonhosted.org/packages/a1/32/4c49da27a606167391ff0c39aa955707a00edc500572e562f7c36c08a71f/mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15", size = 14726046, upload-time = "2026-04-21T17:11:22.354Z" }, - { url = "https://files.pythonhosted.org/packages/7f/fc/4e354a1bd70216359deb0c9c54847ee6b32ef78dfb09f5131ff99b494078/mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee", size = 14955587, upload-time = "2026-04-21T17:12:16.033Z" }, - { url = "https://files.pythonhosted.org/packages/62/b2/c0f2056e9eb8f08c62cafd9715e4584b89132bdc832fcf85d27d07b5f3e5/mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f", size = 10922681, upload-time = "2026-04-21T17:06:35.842Z" }, - { url = "https://files.pythonhosted.org/packages/e5/14/065e333721f05de8ef683d0aa804c23026bcc287446b61cac657b902ccac/mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330", size = 9830560, upload-time = "2026-04-21T17:07:51.023Z" }, - { url = "https://files.pythonhosted.org/packages/ae/d1/b4ec96b0ecc620a4443570c6e95c867903428cfcde4206518eafdd5880c3/mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30", size = 14524561, upload-time = "2026-04-21T17:06:27.325Z" }, - { url = "https://files.pythonhosted.org/packages/3a/63/d2c2ff4fa66bc49477d32dfa26e8a167ba803ea6a69c5efb416036909d30/mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924", size = 13363883, upload-time = "2026-04-21T17:11:11.239Z" }, - { url = "https://files.pythonhosted.org/packages/2a/56/983916806bf4eddeaaa2c9230903c3669c6718552a921154e1c5182c701f/mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb", size = 13742945, upload-time = "2026-04-21T17:08:34.181Z" }, - { url = "https://files.pythonhosted.org/packages/19/65/0cd9285ab010ee8214c83d67c6b49417c40d86ce46f1aa109457b5a9b8d7/mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc", size = 14706163, upload-time = "2026-04-21T17:05:15.51Z" }, - { url = "https://files.pythonhosted.org/packages/94/97/48ff3b297cafcc94d185243a9190836fb1b01c1b0918fff64e941e973cc9/mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558", size = 14938677, upload-time = "2026-04-21T17:05:39.562Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a1/1b4233d255bdd0b38a1f284feeb1c143ca508c19184964e22f8d837ec851/mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8", size = 11089322, upload-time = "2026-04-21T17:06:44.29Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/ce7ee2ba36aeb954ba50f18fa25d9c1188578654b97d02a66a15b6f09531/mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3", size = 10017775, upload-time = "2026-04-21T17:07:20.732Z" }, - { url = "https://files.pythonhosted.org/packages/4e/a1/9d93a7d0b5859af0ead82b4888b46df6c8797e1bc5e1e262a08518c6d48e/mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609", size = 15549002, upload-time = "2026-04-21T17:08:23.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/d2/09a6a10ee1bf0008f6c144d9676f2ca6a12512151b4e0ad0ff6c4fac5337/mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2", size = 14401942, upload-time = "2026-04-21T17:07:31.837Z" }, - { url = "https://files.pythonhosted.org/packages/57/da/9594b75c3c019e805250bed3583bdf4443ff9e6ef08f97e39ae308cb06f2/mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c", size = 15041649, upload-time = "2026-04-21T17:09:34.653Z" }, - { url = "https://files.pythonhosted.org/packages/97/77/f75a65c278e6e8eba2071f7f5a90481891053ecc39878cc444634d892abe/mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744", size = 15864588, upload-time = "2026-04-21T17:11:44.936Z" }, - { url = "https://files.pythonhosted.org/packages/d7/46/1a4e1c66e96c1a3246ddf5403d122ac9b0a8d2b7e65730b9d6533ba7a6d3/mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6", size = 16093956, upload-time = "2026-04-21T17:10:17.683Z" }, - { url = "https://files.pythonhosted.org/packages/5a/2c/78a8851264dec38cd736ca5b8bc9380674df0dd0be7792f538916157716c/mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec", size = 12568661, upload-time = "2026-04-21T17:11:54.473Z" }, - { url = "https://files.pythonhosted.org/packages/83/01/cd7318aa03493322ce275a0e14f4f52b8896335e4e79d4fb8153a7ad2b77/mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382", size = 10389240, upload-time = "2026-04-21T17:09:42.719Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/f23c163e25b11074188251b0b5a0342625fc1cdb6af604757174fa9acc9b/mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563", size = 2637314, upload-time = "2026-04-21T17:05:54.5Z" }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - -[[package]] -name = "nbclient" -version = "0.10.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-client" }, - { name = "jupyter-core" }, - { name = "nbformat" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554, upload-time = "2025-12-23T07:45:46.369Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" }, -] - -[[package]] -name = "nbconvert" -version = "7.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "beautifulsoup4" }, - { name = "bleach", extra = ["css"] }, - { name = "defusedxml" }, - { name = "jinja2" }, - { name = "jupyter-core" }, - { name = "jupyterlab-pygments" }, - { name = "markupsafe" }, - { name = "mistune" }, - { name = "nbclient" }, - { name = "nbformat" }, - { name = "packaging" }, - { name = "pandocfilters" }, - { name = "pygments" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/b1/708e53fe2e429c103c6e6e159106bcf0357ac41aa4c28772bd8402339051/nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2", size = 865311, upload-time = "2026-04-08T00:44:14.914Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/f8/bb0a9d5f46819c821dc1f004aa2cc29b1d91453297dbf5ff20470f00f193/nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8", size = 261927, upload-time = "2026-04-08T00:44:12.845Z" }, -] - -[[package]] -name = "nbformat" -version = "5.10.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "fastjsonschema" }, - { name = "jsonschema" }, - { name = "jupyter-core" }, - { name = "traitlets" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749, upload-time = "2024-04-04T11:20:37.371Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454, upload-time = "2024-04-04T11:20:34.895Z" }, -] - -[[package]] -name = "nbval" -version = "0.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage" }, - { name = "ipykernel" }, - { name = "jupyter-client" }, - { name = "nbformat" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/be/22bd64d09e0cb53258f83b6fc455f05f18a78e3e5c109ccb6af42f1f49a2/nbval-0.11.0.tar.gz", hash = "sha256:77c95797607b0a968babd2597ee3494102d25c3ad37435debbdac0e46e379094", size = 62718, upload-time = "2024-03-04T14:36:58.256Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/5c/eb1e3ce54c4e94c7734b3831756c63f21badb3de91a98d77b9e23c0ca76a/nbval-0.11.0-py2.py3-none-any.whl", hash = "sha256:307aecc866c9a1e8a13bb5bbb008a702bacfda2394dff6fe504a3108a58042a0", size = 24013, upload-time = "2024-03-04T14:36:57.126Z" }, -] - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, -] - -[[package]] -name = "nh3" -version = "0.3.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/5f/1d19bdc7d27238e37f3672cdc02cb77c56a4a86d140cd4f4f23c90df6e16/nh3-0.3.5.tar.gz", hash = "sha256:45855e14ff056064fec77133bfcf7cd691838168e5e17bbef075394954dc9dc8", size = 20743, upload-time = "2026-04-25T10:44:16.066Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/63/b0/8587ac42a9627ab88e7e221601f1dfccbf4db80b2a29222ea63266dc9abc/nh3-0.3.5-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:23a312224875f72cd16bde417f49071451877e29ef646a60e50fcb69407cc18a", size = 1420126, upload-time = "2026-04-25T10:43:39.834Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/1dbc4d0c43f12e8c1784ede17eaee6f061d4fbe5505757c65c49b2ceab95/nh3-0.3.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387abd011e81959d5a35151a11350a0795c6edeb53ebfa02d2e882dc01299263", size = 793943, upload-time = "2026-04-25T10:43:41.363Z" }, - { url = "https://files.pythonhosted.org/packages/47/9f/d6758d7a14ee964bf439cc35ae4fa24a763a93399c8ef6f22bd11d532d29/nh3-0.3.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48f45e3e914be93a596431aa143dedf1582557bf41a58153c296048d6e3798c9", size = 841150, upload-time = "2026-04-25T10:43:43.007Z" }, - { url = "https://files.pythonhosted.org/packages/b6/36/d5d1ae8374612c98f390e1ea7c610fa6c9716259a03bbf4d15b269f40073/nh3-0.3.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0a09f51806fd51b4fedbf9ea2b61fef388f19aef0d62fe51199d41648be14588", size = 1008415, upload-time = "2026-04-25T10:43:44.324Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8f/d13a9c3fd2d9c131a2a281737380e9379eb0f8c33fea24c2b923aaafbb15/nh3-0.3.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c357f1d042c67f135a5e6babb2b0e3b9d9224ff4a3543240f597767b01384ffd", size = 1092706, upload-time = "2026-04-25T10:43:45.653Z" }, - { url = "https://files.pythonhosted.org/packages/bb/57/2f3add7f8680fcc896afa6a675cb2bab09982853ee8af40bad621f6b61c4/nh3-0.3.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:38748140bf76383ab7ce2dce0ad4cb663855d8fbc9098f7f3483673d09616a17", size = 1048346, upload-time = "2026-04-25T10:43:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/c1/c3/2f9e4ffa82863074d1361bfe949bc46393d91b3411579dfbbd090b24cac5/nh3-0.3.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:84bdeb082544fbcb77a12c034dd77d7da0556fdc0727b787eb6214b958c15e29", size = 1029038, upload-time = "2026-04-25T10:43:48.569Z" }, - { url = "https://files.pythonhosted.org/packages/e8/10/2804deb3f3315184c9cae41702e293c87524b5a21f766b07d7fe3ffbcfbb/nh3-0.3.5-cp314-cp314t-win32.whl", hash = "sha256:c3aae321f67ae66cff2a627115f106a377d4475d10b0e13d97959a13486b9a88", size = 603263, upload-time = "2026-04-25T10:43:49.851Z" }, - { url = "https://files.pythonhosted.org/packages/eb/a2/f6685248b49f7548fc9a8c335ab3a52f68610b72e8a61576447151e4e2e6/nh3-0.3.5-cp314-cp314t-win_amd64.whl", hash = "sha256:c88605d8d468f7fc1b31e06129bc91d6c96f6c621776c9b504a0da9beac9df5f", size = 616866, upload-time = "2026-04-25T10:43:51.005Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b6/d8c9018635d4acfefde6b68470daa510eed715a350cbaa2f928ba0609f81/nh3-0.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:72c5bdedec27fa33de6a5326346ea8aa3fe54f6ac294d54c4b204fb66a9f1e79", size = 602566, upload-time = "2026-04-25T10:43:52.283Z" }, - { url = "https://files.pythonhosted.org/packages/85/30/d162e99746a2fb1d98bb0ef23af3e201b156cf09f7de867c7390c8fe1c06/nh3-0.3.5-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:3bb854485c9b33e5bb143ff3e49e577073bc6bc320f0ff8fc316dd89c0d3c101", size = 1442393, upload-time = "2026-04-25T10:43:53.556Z" }, - { url = "https://files.pythonhosted.org/packages/25/8c/072120d506978ab053e1732d0efa7c86cb478fee0ee098fda0ac0d31cb34/nh3-0.3.5-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d401ab2d8e86d59e2126e3ab2a2f45840c405842b626d9a51624b3a33b6878", size = 837722, upload-time = "2026-04-25T10:43:55.073Z" }, - { url = "https://files.pythonhosted.org/packages/52/86/d4e06e28c5ad1c4b065f89737d02631bd49f1660b6ebcf17a87ffcd201da/nh3-0.3.5-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:acfd354e61accbe4c74f8017c6e397a776916dfe47c48643cf7fd84ade826f93", size = 822872, upload-time = "2026-04-25T10:43:56.581Z" }, - { url = "https://files.pythonhosted.org/packages/0a/62/50659255213f241ec5797ae7427464c969397373e83b3659372b341ae869/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52d877980d7ca01dc3baf3936bf844828bc6f332962227a684ed79c18cce14c3", size = 1100031, upload-time = "2026-04-25T10:43:58.098Z" }, - { url = "https://files.pythonhosted.org/packages/00/7a/a12ae77593b2fcf3be25df7bc1c01967d0de448bdb4b6c7ec80fe4f5a74f/nh3-0.3.5-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:207c01801d3e9bb8ec08f08689346bdd30ce15b8bf60013a925d08b5388962a4", size = 1057669, upload-time = "2026-04-25T10:43:59.328Z" }, - { url = "https://files.pythonhosted.org/packages/2d/71/5647dc04c0233192a3956fc91708822b21403a06508cacf78083c68e7bf0/nh3-0.3.5-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea232933394d1d58bf7c4bb348dc4660eae6604e1ae81cd2ba6d9ed80d390f3b", size = 914795, upload-time = "2026-04-25T10:44:00.52Z" }, - { url = "https://files.pythonhosted.org/packages/1b/0e/bf298920729f216adcb002acf7ea01b90842603d2e4e2ce9b900d9ee8fab/nh3-0.3.5-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3a787dc76b50de6bee54ef242f26c41dfe47654428e3e94f0fae5bb6dd2cc1", size = 806976, upload-time = "2026-04-25T10:44:01.743Z" }, - { url = "https://files.pythonhosted.org/packages/85/01/26761e1dc2b848e65a62c19e5d39ad446283287cd4afddc89f364ab86bc9/nh3-0.3.5-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:488928988caad25ba14b1eb5bc74e25e21f3b5e40341d956f3ce4a8bc19460dc", size = 834904, upload-time = "2026-04-25T10:44:03.454Z" }, - { url = "https://files.pythonhosted.org/packages/33/53/0766113e679540ac1edc1b82b1295aecd321eeb75d6fead70109a838b6ee/nh3-0.3.5-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c069570b06aa848457713ad7af4a9905691291548c4466a9ad78ee95808382b", size = 857159, upload-time = "2026-04-25T10:44:05.003Z" }, - { url = "https://files.pythonhosted.org/packages/58/36/734d353dfaf292fed574b8b3092f0ef79dc6404f3879f7faaa61a4701fad/nh3-0.3.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:eeedc90ed8c42c327e8e10e621ccfa314fc6cce35d5929f4297ff1cdb89667c4", size = 1018600, upload-time = "2026-04-25T10:44:06.18Z" }, - { url = "https://files.pythonhosted.org/packages/6b/aa/d9c59c1b49669fcb7bababa55df82385f029ad5c2651f583c3a1141cfdd1/nh3-0.3.5-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:de8e8621853b6470fe928c684ee0d3f39ea8086cebafe4c416486488dea7b68d", size = 1103530, upload-time = "2026-04-25T10:44:07.68Z" }, - { url = "https://files.pythonhosted.org/packages/90/b0/cdd210bfb8d9d43fb02fc3c868336b9955934d8e15e66eb1d15a147b8af0/nh3-0.3.5-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:6ea58cc44d274c643b83547ca9654a0b1a817609b160601356f76a2b744c49ad", size = 1061754, upload-time = "2026-04-25T10:44:09.362Z" }, - { url = "https://files.pythonhosted.org/packages/ce/cb/7a39e72e668c8445bdd95e494b3e21cfdddc68329be8ea3522c8befb46c4/nh3-0.3.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e49c9b564e6bcb03ecd2f057213df9a0de15a95812ac9db9600b590db23d3ae9", size = 1040938, upload-time = "2026-04-25T10:44:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/af/4c/fc2f9ed208a3801a319f59b5fea03cdc20cf3bd8af14be930d3a8de01224/nh3-0.3.5-cp38-abi3-win32.whl", hash = "sha256:559e4c73b689e9a7aa97ac9760b1bc488038d7c1a575aa4ab5a0e19ee9630c0f", size = 611445, upload-time = "2026-04-25T10:44:12.317Z" }, - { url = "https://files.pythonhosted.org/packages/db/1a/e4c9b5e2ae13e6092c9ec16d8ca30646cb01fcdea245f36c5b08fd21fbd5/nh3-0.3.5-cp38-abi3-win_amd64.whl", hash = "sha256:45e6a65dc88a300a2e3502cb9c8e6d1d6b831d6fba7470643333609c6aab1f30", size = 626502, upload-time = "2026-04-25T10:44:13.682Z" }, - { url = "https://files.pythonhosted.org/packages/80/7c/19cd0671d1ba2762fb388fc149697d20d0568ccfeef833b11280a619e526/nh3-0.3.5-cp38-abi3-win_arm64.whl", hash = "sha256:8f85285700a18e9f3fc5bff41fe573fa84f81542ef13b48a89f9fecca0474d3b", size = 611069, upload-time = "2026-04-25T10:44:14.934Z" }, -] - -[[package]] -name = "notebook" -version = "7.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-builder" }, - { name = "jupyter-server" }, - { name = "jupyterlab" }, - { name = "jupyterlab-server" }, - { name = "notebook-shim" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/ac/aedb759dc683dcd129f6c75271bab5eb23020b0f7969163c726343718e1e/notebook-7.6.3.tar.gz", hash = "sha256:e2c08e469c0ae20bb0b3214f0ab77e79653317a2f8e5b34c10361c66874a5b50", size = 5501618, upload-time = "2026-09-21T18:07:24.261Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a1/f7/907b98438cf00bdc4e296570058f29a347214bb42338a48e3b7eae58ae2a/notebook-7.6.3-py3-none-any.whl", hash = "sha256:ad7e0eb765fba836cd4a2ab0c7a3a26cde1d91665fbf6f533b6ae7b2de6d88d2", size = 5548452, upload-time = "2026-09-21T18:07:21.563Z" }, -] - -[[package]] -name = "notebook-shim" -version = "0.2.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jupyter-server" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167, upload-time = "2024-02-14T23:35:18.353Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307, upload-time = "2024-02-14T23:35:16.286Z" }, -] - -[[package]] -name = "numpy" -version = "2.4.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, - { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, - { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, - { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, - { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, - { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, - { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, - { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, - { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, - { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, - { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, - { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, - { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, - { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, - { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, - { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, - { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, - { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, - { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, - { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, - { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, - { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, - { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, - { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, - { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, - { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, - { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, - { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, - { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, - { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, - { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, - { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, - { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, - { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, - { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, - { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, - { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, - { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, - { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, - { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, - { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, - { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, - { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, -] - -[[package]] -name = "packaging" -version = "26.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, -] - -[[package]] -name = "pandocfilters" -version = "1.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/70/6f/3dd4940bbe001c06a65f88e36bad298bc7a0de5036115639926b0c5c0458/pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e", size = 8454, upload-time = "2024-01-18T20:08:13.726Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc", size = 8663, upload-time = "2024-01-18T20:08:11.28Z" }, -] - -[[package]] -name = "parso" -version = "0.8.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, -] - -[[package]] -name = "pathspec" -version = "1.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, -] - -[[package]] -name = "pillow" -version = "12.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, - { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, - { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, - { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, - { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, - { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, - { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, - { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, - { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, - { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, - { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, - { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, - { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, - { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, - { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, - { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, - { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, - { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, - { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, - { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, - { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, - { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, - { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, - { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, - { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, - { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, - { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, - { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, - { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, - { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, - { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, - { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, - { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, - { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, - { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, - { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, - { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, - { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, - { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, - { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, - { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, - { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, - { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, - { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, - { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, - { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, - { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, - { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, - { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, - { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, - { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, - { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, - { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, - { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, - { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, - { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, - { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, - { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.9.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "prometheus-client" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035, upload-time = "2026-04-09T19:53:42.359Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154, upload-time = "2026-04-09T19:53:41.324Z" }, -] - -[[package]] -name = "prompt-toolkit" -version = "3.0.52" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, -] - -[[package]] -name = "psutil" -version = "7.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, - { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, - { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, - { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, - { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, - { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, - { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, - { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, - { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, - { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, - { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, - { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, - { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, - { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, - { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, - { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, - { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, - { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, - { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, -] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, -] - -[[package]] -name = "pure-eval" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, -] - -[[package]] -name = "pycparser" -version = "3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, -] - -[[package]] -name = "pygments" -version = "2.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, -] - -[[package]] -name = "pylint" -version = "4.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "astroid" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "dill" }, - { name = "isort" }, - { name = "mccabe" }, - { name = "platformdirs" }, - { name = "tomlkit" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e4/b6/74d9a8a68b8067efce8d07707fe6a236324ee1e7808d2eb3646ec8517c7d/pylint-4.0.5.tar.gz", hash = "sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c", size = 1572474, upload-time = "2026-02-20T09:07:33.621Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, -] - -[[package]] -name = "pyproject-hooks" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, -] - -[[package]] -name = "python-discovery" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0c/57/250bd238b966cece44328235eb85290045d059265fdaf7527a3a958123db/python_discovery-1.6.1.tar.gz", hash = "sha256:cf87d3627dfb4412437fdd5b13eae402607722998d21567993aedbc59b23c15e", size = 84338, upload-time = "2026-09-18T01:31:53.971Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/7d/e9ffbadfbf89c93848412d04594135c4ae8c1d37d9e053b9c3ed718fabc4/python_discovery-1.6.1-py3-none-any.whl", hash = "sha256:d43fcdef879fe795352bd13ccf8d185ba5a9f86f36cfcd00529f596e737442b3", size = 38664, upload-time = "2026-09-18T01:31:52.448Z" }, -] - -[[package]] -name = "python-json-logger" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573, upload-time = "2026-03-29T04:39:56.805Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021, upload-time = "2026-03-29T04:39:55.266Z" }, -] - -[[package]] -name = "python-levenshtein" -version = "0.27.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "levenshtein" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f6/b4/36eda4188dd19d3cb53d8a8749d7520bd23dfe1c1f44e56ea9dcd0232274/python_levenshtein-0.27.3.tar.gz", hash = "sha256:27dc2d65aeb62a7d6852388f197073296370779286c0860b087357f3b8129a62", size = 12446, upload-time = "2025-11-01T12:54:59.712Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/5b/26e3cca2589252ceabf964ba81514e6f48556553c9c2766e1a0fdceec696/python_levenshtein-0.27.3-py3-none-any.whl", hash = "sha256:5d6168a8e8befb25abf04d2952368a446722be10e8ced218d0dc4fd3703a43a1", size = 9504, upload-time = "2025-11-01T12:54:58.933Z" }, -] - -[[package]] -name = "pywin32-ctypes" -version = "0.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, -] - -[[package]] -name = "pywinpty" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309, upload-time = "2026-02-04T21:51:09.524Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/d4/aeb5e1784d2c5bff6e189138a9ca91a090117459cea0c30378e1f2db3d54/pywinpty-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c9081df0e49ffa86d15db4a6ba61530630e48707f987df42c9d3313537e81fc0", size = 2113098, upload-time = "2026-02-04T21:54:37.711Z" }, - { url = "https://files.pythonhosted.org/packages/b9/53/7278223c493ccfe4883239cf06c823c56460a8010e0fc778eef67858dc14/pywinpty-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:15e79d870e18b678fb8a5a6105fd38496b55697c66e6fc0378236026bc4d59e9", size = 234901, upload-time = "2026-02-04T21:53:31.35Z" }, - { url = "https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9c91dbb026050c77bdcef964e63a4f10f01a639113c4d3658332614544c467ab", size = 2112686, upload-time = "2026-02-04T21:52:03.035Z" }, - { url = "https://files.pythonhosted.org/packages/fd/50/724ed5c38c504d4e58a88a072776a1e880d970789deaeb2b9f7bd9a5141a/pywinpty-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:fe1f7911805127c94cf51f89ab14096c6f91ffdcacf993d2da6082b2142a2523", size = 234591, upload-time = "2026-02-04T21:52:29.821Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ad/90a110538696b12b39fd8758a06d70ded899308198ad2305ac68e361126e/pywinpty-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:3f07a6cf1c1d470d284e614733c3d0f726d2c85e78508ea10a403140c3c0c18a", size = 2112360, upload-time = "2026-02-04T21:55:33.397Z" }, - { url = "https://files.pythonhosted.org/packages/44/0f/7ffa221757a220402bc79fda44044c3f2cc57338d878ab7d622add6f4581/pywinpty-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:15c7c0b6f8e9d87aabbaff76468dabf6e6121332c40fc1d83548d02a9d6a3759", size = 233107, upload-time = "2026-02-04T21:51:45.455Z" }, - { url = "https://files.pythonhosted.org/packages/28/88/2ff917caff61e55f38bcdb27de06ee30597881b2cae44fbba7627be015c4/pywinpty-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:d4b6b7b0fe0cdcd02e956bd57cfe9f4e5a06514eecf3b5ae174da4f951b58be9", size = 2113282, upload-time = "2026-02-04T21:52:08.188Z" }, - { url = "https://files.pythonhosted.org/packages/63/32/40a775343ace542cc43ece3f1d1fce454021521ecac41c4c4573081c2336/pywinpty-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:34789d685fc0d547ce0c8a65e5a70e56f77d732fa6e03c8f74fefb8cbb252019", size = 234207, upload-time = "2026-02-04T21:51:58.687Z" }, - { url = "https://files.pythonhosted.org/packages/8d/54/5d5e52f4cb75028104ca6faf36c10f9692389b1986d34471663b4ebebd6d/pywinpty-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c37e224a47a971d1a6e08649a1714dac4f63c11920780977829ed5c8cadead1", size = 2112910, upload-time = "2026-02-04T21:52:30.976Z" }, - { url = "https://files.pythonhosted.org/packages/0a/44/dcd184824e21d4620b06c7db9fbb15c3ad0a0f1fa2e6de79969fb82647ec/pywinpty-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c4e9c3dff7d86ba81937438d5819f19f385a39d8f592d4e8af67148ceb4f6ab5", size = 233425, upload-time = "2026-02-04T21:51:56.754Z" }, -] - -[[package]] -name = "pyyaml" -version = "6.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, - { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, - { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, - { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, - { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, - { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, - { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, - { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, - { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, - { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, - { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, - { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, - { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, - { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, - { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, - { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, - { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, - { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, - { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, - { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, - { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, - { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, - { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, - { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, - { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, - { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, - { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, - { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, -] - -[[package]] -name = "pyzmq" -version = "27.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "implementation_name == 'pypy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/04/0b/3c9baedbdf613ecaa7aa07027780b8867f57b6293b6ee50de316c9f3222b/pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540", size = 281750, upload-time = "2025-09-08T23:10:18.157Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/e7/038aab64a946d535901103da16b953c8c9cc9c961dadcbf3609ed6428d23/pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc", size = 1306279, upload-time = "2025-09-08T23:08:03.807Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5e/c3c49fdd0f535ef45eefcc16934648e9e59dace4a37ee88fc53f6cd8e641/pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113", size = 895645, upload-time = "2025-09-08T23:08:05.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/b0b2504cb4e903a74dcf1ebae157f9e20ebb6ea76095f6cfffea28c42ecd/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233", size = 652574, upload-time = "2025-09-08T23:08:06.828Z" }, - { url = "https://files.pythonhosted.org/packages/f8/9b/c108cdb55560eaf253f0cbdb61b29971e9fb34d9c3499b0e96e4e60ed8a5/pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31", size = 840995, upload-time = "2025-09-08T23:08:08.396Z" }, - { url = "https://files.pythonhosted.org/packages/c2/bb/b79798ca177b9eb0825b4c9998c6af8cd2a7f15a6a1a4272c1d1a21d382f/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28", size = 1642070, upload-time = "2025-09-08T23:08:09.989Z" }, - { url = "https://files.pythonhosted.org/packages/9c/80/2df2e7977c4ede24c79ae39dcef3899bfc5f34d1ca7a5b24f182c9b7a9ca/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856", size = 2021121, upload-time = "2025-09-08T23:08:11.907Z" }, - { url = "https://files.pythonhosted.org/packages/46/bd/2d45ad24f5f5ae7e8d01525eb76786fa7557136555cac7d929880519e33a/pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496", size = 1878550, upload-time = "2025-09-08T23:08:13.513Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2f/104c0a3c778d7c2ab8190e9db4f62f0b6957b53c9d87db77c284b69f33ea/pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd", size = 559184, upload-time = "2025-09-08T23:08:15.163Z" }, - { url = "https://files.pythonhosted.org/packages/fc/7f/a21b20d577e4100c6a41795842028235998a643b1ad406a6d4163ea8f53e/pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf", size = 619480, upload-time = "2025-09-08T23:08:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/78/c2/c012beae5f76b72f007a9e91ee9401cb88c51d0f83c6257a03e785c81cc2/pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f", size = 552993, upload-time = "2025-09-08T23:08:18.926Z" }, - { url = "https://files.pythonhosted.org/packages/60/cb/84a13459c51da6cec1b7b1dc1a47e6db6da50b77ad7fd9c145842750a011/pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5", size = 1122436, upload-time = "2025-09-08T23:08:20.801Z" }, - { url = "https://files.pythonhosted.org/packages/dc/b6/94414759a69a26c3dd674570a81813c46a078767d931a6c70ad29fc585cb/pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6", size = 1156301, upload-time = "2025-09-08T23:08:22.47Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ad/15906493fd40c316377fd8a8f6b1f93104f97a752667763c9b9c1b71d42d/pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7", size = 1341197, upload-time = "2025-09-08T23:08:24.286Z" }, - { url = "https://files.pythonhosted.org/packages/14/1d/d343f3ce13db53a54cb8946594e567410b2125394dafcc0268d8dda027e0/pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05", size = 897275, upload-time = "2025-09-08T23:08:26.063Z" }, - { url = "https://files.pythonhosted.org/packages/69/2d/d83dd6d7ca929a2fc67d2c3005415cdf322af7751d773524809f9e585129/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9", size = 660469, upload-time = "2025-09-08T23:08:27.623Z" }, - { url = "https://files.pythonhosted.org/packages/3e/cd/9822a7af117f4bc0f1952dbe9ef8358eb50a24928efd5edf54210b850259/pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128", size = 847961, upload-time = "2025-09-08T23:08:29.672Z" }, - { url = "https://files.pythonhosted.org/packages/9a/12/f003e824a19ed73be15542f172fd0ec4ad0b60cf37436652c93b9df7c585/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39", size = 1650282, upload-time = "2025-09-08T23:08:31.349Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4a/e82d788ed58e9a23995cee70dbc20c9aded3d13a92d30d57ec2291f1e8a3/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97", size = 2024468, upload-time = "2025-09-08T23:08:33.543Z" }, - { url = "https://files.pythonhosted.org/packages/d9/94/2da0a60841f757481e402b34bf4c8bf57fa54a5466b965de791b1e6f747d/pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db", size = 1885394, upload-time = "2025-09-08T23:08:35.51Z" }, - { url = "https://files.pythonhosted.org/packages/4f/6f/55c10e2e49ad52d080dc24e37adb215e5b0d64990b57598abc2e3f01725b/pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c", size = 574964, upload-time = "2025-09-08T23:08:37.178Z" }, - { url = "https://files.pythonhosted.org/packages/87/4d/2534970ba63dd7c522d8ca80fb92777f362c0f321900667c615e2067cb29/pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2", size = 641029, upload-time = "2025-09-08T23:08:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/f6/fa/f8aea7a28b0641f31d40dea42d7ef003fded31e184ef47db696bc74cd610/pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e", size = 561541, upload-time = "2025-09-08T23:08:42.668Z" }, - { url = "https://files.pythonhosted.org/packages/87/45/19efbb3000956e82d0331bafca5d9ac19ea2857722fa2caacefb6042f39d/pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a", size = 1341197, upload-time = "2025-09-08T23:08:44.973Z" }, - { url = "https://files.pythonhosted.org/packages/48/43/d72ccdbf0d73d1343936296665826350cb1e825f92f2db9db3e61c2162a2/pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea", size = 897175, upload-time = "2025-09-08T23:08:46.601Z" }, - { url = "https://files.pythonhosted.org/packages/2f/2e/a483f73a10b65a9ef0161e817321d39a770b2acf8bcf3004a28d90d14a94/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96", size = 660427, upload-time = "2025-09-08T23:08:48.187Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d2/5f36552c2d3e5685abe60dfa56f91169f7a2d99bbaf67c5271022ab40863/pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d", size = 847929, upload-time = "2025-09-08T23:08:49.76Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2a/404b331f2b7bf3198e9945f75c4c521f0c6a3a23b51f7a4a401b94a13833/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146", size = 1650193, upload-time = "2025-09-08T23:08:51.7Z" }, - { url = "https://files.pythonhosted.org/packages/1c/0b/f4107e33f62a5acf60e3ded67ed33d79b4ce18de432625ce2fc5093d6388/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd", size = 2024388, upload-time = "2025-09-08T23:08:53.393Z" }, - { url = "https://files.pythonhosted.org/packages/0d/01/add31fe76512642fd6e40e3a3bd21f4b47e242c8ba33efb6809e37076d9b/pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a", size = 1885316, upload-time = "2025-09-08T23:08:55.702Z" }, - { url = "https://files.pythonhosted.org/packages/c4/59/a5f38970f9bf07cee96128de79590bb354917914a9be11272cfc7ff26af0/pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92", size = 587472, upload-time = "2025-09-08T23:08:58.18Z" }, - { url = "https://files.pythonhosted.org/packages/70/d8/78b1bad170f93fcf5e3536e70e8fadac55030002275c9a29e8f5719185de/pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0", size = 661401, upload-time = "2025-09-08T23:08:59.802Z" }, - { url = "https://files.pythonhosted.org/packages/81/d6/4bfbb40c9a0b42fc53c7cf442f6385db70b40f74a783130c5d0a5aa62228/pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7", size = 575170, upload-time = "2025-09-08T23:09:01.418Z" }, -] - -[[package]] -name = "rapidfuzz" -version = "3.14.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/21/ef6157213316e85790041254259907eb722e00b03480256c0545d98acd33/rapidfuzz-3.14.5.tar.gz", hash = "sha256:ba10ac57884ce82112f7ed910b67e7fb6072d8ef2c06e30dc63c0f604a112e0e", size = 57901753, upload-time = "2026-04-07T11:16:31.931Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/e3/574435c6aafb80254c191ef40d7aca2cb2bb97a095ec9395e9fa59ac307a/rapidfuzz-3.14.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0d3378f471ef440473a396ce2f8e97ee12f89a78b495540e0a5617bbfe895638", size = 1944601, upload-time = "2026-04-07T11:14:18.771Z" }, - { url = "https://files.pythonhosted.org/packages/d0/1f/fbad3102a255ecc112ce9a7e779bacab7fd14398217be8868dc9082ba363/rapidfuzz-3.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e910eebca9fd0eba245c0555e764597e8a0cccb673a92da2dc2397050725f48", size = 1164293, upload-time = "2026-04-07T11:14:20.534Z" }, - { url = "https://files.pythonhosted.org/packages/88/37/a3eb7ff6121ed3a5f199a8c38cc86c8e481816f879cb0e0b738b078c9a7e/rapidfuzz-3.14.5-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01550fe5f60fd176aa66b7611289d46dc4aa4b1b904874c7b6d1d54e581c5ec1", size = 1371999, upload-time = "2026-04-07T11:14:22.63Z" }, - { url = "https://files.pythonhosted.org/packages/79/72/97a9728c711c7c1b06e107d3f0623880fb4ef90e147ed13c551a1730e7cc/rapidfuzz-3.14.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48bee0b91bebfaec41e1081e351000659ab7570cc4598d617aa04d5bf827f9e6", size = 3145715, upload-time = "2026-04-07T11:14:24.508Z" }, - { url = "https://files.pythonhosted.org/packages/ed/54/d5caabbea233ac90c286c87c260e49d7641467e87438a18d858e41c82e91/rapidfuzz-3.14.5-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:7e580cb04ad849ae9b786fa21383c6b994b6e6c1444ad1cb9f22392759d72741", size = 1456304, upload-time = "2026-04-07T11:14:26.515Z" }, - { url = "https://files.pythonhosted.org/packages/fc/a7/2d1a81250ac8c01a0100c026018e76f0e7a097ff63e4c553e02a6938c6fb/rapidfuzz-3.14.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:09d6c9ba091854f07817055d795d604179c12a8f308ba4c7d56f3719dfea1646", size = 2389089, upload-time = "2026-04-07T11:14:28.635Z" }, - { url = "https://files.pythonhosted.org/packages/65/0d/c47c3872203ae88e6506997c0b576ad731f5261daa25d559be09c9756658/rapidfuzz-3.14.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1e989f86113be66574113b9c7bdf4793f3f863d248e47d911b355e05ca6b6b10", size = 2493404, upload-time = "2026-04-07T11:14:30.577Z" }, - { url = "https://files.pythonhosted.org/packages/8f/2f/71e0a5a3130792146c8a200a2dd1e52aa16f7c1074012e17f2601eea9a90/rapidfuzz-3.14.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ebd1a18e2e47bc0b292a07e6ed9c3642f8aaa672d12253885f599b50807a4f9", size = 4251709, upload-time = "2026-04-07T11:14:32.451Z" }, - { url = "https://files.pythonhosted.org/packages/86/45/d39874901abacef325adb5b34ae416817c8486dfb4fb87c7a9b74ec5b072/rapidfuzz-3.14.5-cp312-cp312-win32.whl", hash = "sha256:9981d38a703b86f0e315a3cd229fd1906fe1d91c989ed121fb975b3c849f89f5", size = 1710069, upload-time = "2026-04-07T11:14:34.37Z" }, - { url = "https://files.pythonhosted.org/packages/85/0b/f65572c53de8a1c704bda707f63a447b67bdbe95d7cdc70d18885e191df5/rapidfuzz-3.14.5-cp312-cp312-win_amd64.whl", hash = "sha256:d8375e3da319593389727c3187ccaf3e0e84199accc530866b8e0f2b79af05e9", size = 1540630, upload-time = "2026-04-07T11:14:36.287Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c3/143be3a578f989758cae516f3270d5cbb49783a7bfdf57cc27a670e00456/rapidfuzz-3.14.5-cp312-cp312-win_arm64.whl", hash = "sha256:478b59bb018a6780d73f33e38d0b3ec5e968a6c1ed42876b993dd456b7aa20e8", size = 813137, upload-time = "2026-04-07T11:14:38.289Z" }, - { url = "https://files.pythonhosted.org/packages/11/66/252803f2010ba699618cdc048b6e1f7cc1f433c08b4a9a17579b92ab0142/rapidfuzz-3.14.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ebd8fd343bf8492a1e60bcb6dc99f90f74f65d98d8241a6b3e1fed225b76ecd6", size = 1940205, upload-time = "2026-04-07T11:14:40.319Z" }, - { url = "https://files.pythonhosted.org/packages/ea/59/b2afd98e41af9cd54554a4c1c423d84cdd60e6b1c0a09496f033b55f60ec/rapidfuzz-3.14.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6737b35d5af7479c5bf9710f7b17edd9d2c43128d974d25fb4ea653e42c64609", size = 1159639, upload-time = "2026-04-07T11:14:42.52Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/7aa7e62c4c516a7af322ed0c4f0774208b72d457d0cfec808bad0df12f4a/rapidfuzz-3.14.5-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b002c7994cc9f2bc9d9856f0fbaee6e8072c983873846c92f25cefba5b2a925f", size = 1367194, upload-time = "2026-04-07T11:14:44.25Z" }, - { url = "https://files.pythonhosted.org/packages/90/79/2fc252a63bc91d3c3b234d0a3a6ad4ebc460037a23cdcdaf9285f986e6c9/rapidfuzz-3.14.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17a34330cd2a538c1ce5d400b61ba358c5b72c654b928ff87b362e88f8b864c7", size = 3151805, upload-time = "2026-04-07T11:14:46.21Z" }, - { url = "https://files.pythonhosted.org/packages/17/54/0c83508f2683ea70e2d05f8527eb07328acf7bb1e9d97a3bece5702378e7/rapidfuzz-3.14.5-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:95d937e74c1a7a1287dfb03b62a827be08ede10a155cf1af73bbf47f2b73ee6e", size = 1455667, upload-time = "2026-04-07T11:14:47.991Z" }, - { url = "https://files.pythonhosted.org/packages/71/1b/070175e873177814d58850a01ebe80e20ae11e93eb4da894d563988660fa/rapidfuzz-3.14.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:46b92a9970dcc34f0096901c792644094cab49554ac3547f35e3aebbdf0a3610", size = 2388246, upload-time = "2026-04-07T11:14:50.098Z" }, - { url = "https://files.pythonhosted.org/packages/c9/dd/77caf7aaf9c2be050ad1f128d7c24ff0f59079aa62c5f62f9df41c0af45e/rapidfuzz-3.14.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e012177c8e8a8a0754ae0d6027d63042aa5ff036d9f40f07cb3466a6082e21b8", size = 2494333, upload-time = "2026-04-07T11:14:52.303Z" }, - { url = "https://files.pythonhosted.org/packages/2c/e2/dd7e1f2aa31a8fbbfc16b0610af1d770ffaf1287490f3c8c5b1c52da264f/rapidfuzz-3.14.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a2ae6f53f99c9a0eca7a0afc5b4e45fc73bc1dd4ac74c00509031d76df80ed98", size = 4258579, upload-time = "2026-04-07T11:14:54.538Z" }, - { url = "https://files.pythonhosted.org/packages/9c/0a/ac99e1ba347ba0e85e0bb60b74231d55fb93c0eff43f2920ccb413d0be08/rapidfuzz-3.14.5-cp313-cp313-win32.whl", hash = "sha256:4a60f0057231188e3bd30216f7b4e0f279b11fa4ec818bb6c1d9f014d1562fbc", size = 1709231, upload-time = "2026-04-07T11:14:56.524Z" }, - { url = "https://files.pythonhosted.org/packages/cf/cb/0e251d731b3166378644238e8f0cf9e89858c024e19f75ca9f7e3ae83fd5/rapidfuzz-3.14.5-cp313-cp313-win_amd64.whl", hash = "sha256:11bfc2ed8fbe4ab86bd516fadefab126f90e6dcadffa761739fcb304707dfd35", size = 1538519, upload-time = "2026-04-07T11:14:58.635Z" }, - { url = "https://files.pythonhosted.org/packages/30/6f/4548132acc947db6d5346a248e44a8b3a22d608ef30e770fb578caaf2d00/rapidfuzz-3.14.5-cp313-cp313-win_arm64.whl", hash = "sha256:b486b5218808f6f4dc471b114b1054e63553db69705c97da0271f47bd706aedd", size = 812628, upload-time = "2026-04-07T11:15:00.552Z" }, - { url = "https://files.pythonhosted.org/packages/00/60/69b177577290c5eab892c6f75fe89c3aff3f9ae80298a78d9372b1cecb9a/rapidfuzz-3.14.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39ef8658aaf67d51667e7bdaf7096f432333377d8302ac43c70b5df8a4cf89b8", size = 1970231, upload-time = "2026-04-07T11:15:02.603Z" }, - { url = "https://files.pythonhosted.org/packages/48/38/2fd790052659cc4e2907b63c25433f0987864b445c1aeec1a302ef5ad948/rapidfuzz-3.14.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9ad37a0be705b544af6296da8edddc260d10a8ae5462530fc9991f66498bb1f9", size = 1194394, upload-time = "2026-04-07T11:15:04.572Z" }, - { url = "https://files.pythonhosted.org/packages/80/f4/28430ad8472fc3536e8ebd51a864a226e979cfe924c6e3f83d111373aa74/rapidfuzz-3.14.5-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d45e06f60729e07d9b20c205f7e5cff90b6ef2584e852eecf46e045aea69627d", size = 1377051, upload-time = "2026-04-07T11:15:06.728Z" }, - { url = "https://files.pythonhosted.org/packages/77/7e/9aeacabcfd1e77397968362e5b98fe14248b8307011136b17daf99752a8e/rapidfuzz-3.14.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e52da10236aa6212de71b9e170bace65b64b129c0dea7fc243d6c9ce976f5074", size = 3160565, upload-time = "2026-04-07T11:15:08.667Z" }, - { url = "https://files.pythonhosted.org/packages/56/f4/db4dd7be0cd2f2022117ac5407d905f435d60e48baaea313a567ad27e865/rapidfuzz-3.14.5-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:440d30faaf682ca496170a7f0cc5453ec942e3e079f0fd802c9a7f938dfb50a3", size = 1442113, upload-time = "2026-04-07T11:15:11.138Z" }, - { url = "https://files.pythonhosted.org/packages/a4/99/0e9f6aa57f3e32a767216f797e56dc96b720fcecfb9d8ee907ecc82f8d66/rapidfuzz-3.14.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:56227a61fd3d17b0cd9793132431f3a3d07c8654be96794ba9f89fe0fc8b2d09", size = 2396618, upload-time = "2026-04-07T11:15:13.154Z" }, - { url = "https://files.pythonhosted.org/packages/60/94/44a78e39ffce17cbdd3e2b53b696acc751d5d153be0f499d052b07a4d904/rapidfuzz-3.14.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:2e83cd2e25bb4edd97b689d9979d9c3acccdaaf26ceac08212ceece202febcfa", size = 2478220, upload-time = "2026-04-07T11:15:15.193Z" }, - { url = "https://files.pythonhosted.org/packages/dd/df/454311469a09a507e9d784a35796742bec22e4cebe75551e2da4e0e290fd/rapidfuzz-3.14.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:af3b859726cd3374287e405e14b9634563c078c5531a4f62375508addebddad1", size = 4265027, upload-time = "2026-04-07T11:15:17.28Z" }, - { url = "https://files.pythonhosted.org/packages/fc/01/175465a9ab3e3b70ba669058372f009d1d49c1746e2dcd56b69df188d3a5/rapidfuzz-3.14.5-cp313-cp313t-win32.whl", hash = "sha256:8ce1d850b3c0178440efde9e884d98421b5e87ff925f364d6d79e23910d7593f", size = 1766814, upload-time = "2026-04-07T11:15:19.687Z" }, - { url = "https://files.pythonhosted.org/packages/1b/a0/a9b84a47af06ebed94a1439eb2f02adebfb8628bcd30af1fe3e02f5ef56c/rapidfuzz-3.14.5-cp313-cp313t-win_amd64.whl", hash = "sha256:c84af70bcf34e99aee894e46a0f1ac77f17d0ef828179c387407642e2466d28a", size = 1582448, upload-time = "2026-04-07T11:15:21.98Z" }, - { url = "https://files.pythonhosted.org/packages/1e/f1/5937800238b3f8248e70860d79f69ba8f73e764fff47e36bc9e2f26dbcc6/rapidfuzz-3.14.5-cp313-cp313t-win_arm64.whl", hash = "sha256:aac0ad28c686a5e72b81668b906c030ee28050b244544b8af68e12fb32543895", size = 832932, upload-time = "2026-04-07T11:15:24.358Z" }, - { url = "https://files.pythonhosted.org/packages/81/41/aa3ffb3355e62e1bf91f6599b3092e866bc88487a07c524004943c7676df/rapidfuzz-3.14.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1a31cc6d7d03e7318a0974c038959c59e19c752b81115f2e9138b3331cd64d45", size = 1943327, upload-time = "2026-04-07T11:15:26.266Z" }, - { url = "https://files.pythonhosted.org/packages/2d/e1/c2141f1840a41e07ad2db6f724945f8f8ff3065463899a22939152dd6e09/rapidfuzz-3.14.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0298d357e2bc59d572da4db0bc631009b6f8f6c9bc8c11e99a12b833f16b6575", size = 1161755, upload-time = "2026-04-07T11:15:28.659Z" }, - { url = "https://files.pythonhosted.org/packages/ca/07/66e753eeaa353161d1d331b7dd517bb349b0bacfebe8496d7b26be26f81f/rapidfuzz-3.14.5-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59b3dba758661a318995655435c6ab20a04ade79fa51e75bc8dc107cac8df280", size = 1376571, upload-time = "2026-04-07T11:15:31.225Z" }, - { url = "https://files.pythonhosted.org/packages/c8/85/9535df0b78ba51f478c9ce7eb6d1f85535cc31fe356773b48fd9d3e563ca/rapidfuzz-3.14.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4900143d82071bdda533b00300c40b14b963ff826b3642cc463b6dd0f036585e", size = 3156468, upload-time = "2026-04-07T11:15:33.428Z" }, - { url = "https://files.pythonhosted.org/packages/81/ee/b667eb93bba6dc4e0de658edd778e1619dc4d6aab68fa5e5c7f075152735/rapidfuzz-3.14.5-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:feedf219672eef83ea6be6f3bb093bba396a8560fc75be85ba225f082903df0a", size = 1458311, upload-time = "2026-04-07T11:15:35.557Z" }, - { url = "https://files.pythonhosted.org/packages/7d/ce/479074f5624364a48df3403c538797ef22d3ac49c19dc76c3f79fcdcc70c/rapidfuzz-3.14.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:419e4397a36e2665ec992d8d64c20ba4b2a42500c76ecadeca78a4f19cb9cc32", size = 2398228, upload-time = "2026-04-07T11:15:37.669Z" }, - { url = "https://files.pythonhosted.org/packages/0b/15/a8982f649150fffbdcd6f17565974501f6ab33b2795267bffbd4a7ba905b/rapidfuzz-3.14.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:97131ab2be39043054ee28d99e09efe316e6d53449b7e962dfcf3c2de8b2b246", size = 2497226, upload-time = "2026-04-07T11:15:39.857Z" }, - { url = "https://files.pythonhosted.org/packages/19/52/5267c03ef6759831b7d4625a0c9c06e87baa2fae084b61ac9c388858317b/rapidfuzz-3.14.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:593c00dac4e30231c35bf3b4f1da8ec0998762e9e94425586a5d636fcd57f9d0", size = 4262283, upload-time = "2026-04-07T11:15:42.279Z" }, - { url = "https://files.pythonhosted.org/packages/71/c0/2579f343a97f5254c43bb5853baccc01488357dcb64a27bcb869b7888a4a/rapidfuzz-3.14.5-cp314-cp314-win32.whl", hash = "sha256:0084b687b02b4e569b46d8d6d4ad25659528e6081cd6d067ca453a69035f07e4", size = 1744614, upload-time = "2026-04-07T11:15:44.498Z" }, - { url = "https://files.pythonhosted.org/packages/17/eb/8edfed1e80119dc9c35b11df4bc701eea85622ad681fff0263b6961d3224/rapidfuzz-3.14.5-cp314-cp314-win_amd64.whl", hash = "sha256:5dfa89d78f22cd773054caff44827b846161a29f2dcf7e78b8f90d086621e502", size = 1588971, upload-time = "2026-04-07T11:15:46.86Z" }, - { url = "https://files.pythonhosted.org/packages/f6/04/5676df93c85cfa57a3045d8047318df9f3cd58c7b8a99340dd95f874795e/rapidfuzz-3.14.5-cp314-cp314-win_arm64.whl", hash = "sha256:67f3f9d2b444268ab53e47d31bab89954888d23c04c6789f2c727e51fe4b1d13", size = 834985, upload-time = "2026-04-07T11:15:49.411Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0d/4a8988cea658fe335048ddef8c876addff1b6daa3c9ca8ad65a5a2196e69/rapidfuzz-3.14.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:77eac0526899b3c3ad1454bb2b03cdb491d67358ec8ef0c9c48bd61b632b431d", size = 1972517, upload-time = "2026-04-07T11:15:51.819Z" }, - { url = "https://files.pythonhosted.org/packages/1c/a3/f5cfd9965a9d9a9e32249159797c47b5d6299ea6d1629f9126b25f1c10a3/rapidfuzz-3.14.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b9c6bd754d11f6e78ac54e3d86b4b11dc1ba2f13e5fc958899574532897f5a99", size = 1196056, upload-time = "2026-04-07T11:15:54.292Z" }, - { url = "https://files.pythonhosted.org/packages/64/07/561c2e40cfd10e6630a7b0ac5a2a813aef50d944bcd1f3d260319d659d5b/rapidfuzz-3.14.5-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:738c96944d076deeaff70e92b65696ab4f7ecb8081d7791c5403a3257dfaf8ff", size = 1374732, upload-time = "2026-04-07T11:15:56.584Z" }, - { url = "https://files.pythonhosted.org/packages/c2/39/123bb94fee40e2fb3b7c49b80827c7ef42d838e18def3fc2fef5a3cf817a/rapidfuzz-3.14.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4c1bca487a17fe4226b4ffb2d30e799d2b274d692cffa76bd0746f56235fca3", size = 3166902, upload-time = "2026-04-07T11:15:58.768Z" }, - { url = "https://files.pythonhosted.org/packages/75/0a/45716fafc9fd2e028cf20b5ac5bc704887081cd312f84edb0e325599414b/rapidfuzz-3.14.5-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:af6a90a4ed2a48fa1a2d17e9d824e6c7c950bea5bad0b707c77fd55751e6bfef", size = 1452130, upload-time = "2026-04-07T11:16:01.453Z" }, - { url = "https://files.pythonhosted.org/packages/ca/49/4e96c413114398481c0a5b0086af32c364a18613c9a2ea578d17c4bea4ee/rapidfuzz-3.14.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bf5018938208d4597b2e679a4f8cff9fd252f1df53583130ae56281a21801b64", size = 2396308, upload-time = "2026-04-07T11:16:03.588Z" }, - { url = "https://files.pythonhosted.org/packages/89/b7/49fea9fc6878d59bd259d01dd1972d9b86117992b1c66d9b16f0a65273c3/rapidfuzz-3.14.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c0919d1f89ddf91129906705723118ea09754171e4116f5a5dbc667c7bc9b261", size = 2488210, upload-time = "2026-04-07T11:16:05.871Z" }, - { url = "https://files.pythonhosted.org/packages/0c/44/a1f732b93ffacbdad077b7c801149549b2938e1bece6addb5ad85ed74df8/rapidfuzz-3.14.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:93d8da883a35116d6813432177f35e570db5b0a5e30ecb0cbd7cb39c815735df", size = 4270621, upload-time = "2026-04-07T11:16:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/bb/ce/ff942d19fce5385054650bb71a58495ddda299d94661ccc4e6e7fa44868b/rapidfuzz-3.14.5-cp314-cp314t-win32.whl", hash = "sha256:0f23e37019ec07712d58976b1ab2b889f8649a7f7c2f626a2f34ea9139e79279", size = 1803950, upload-time = "2026-04-07T11:16:10.873Z" }, - { url = "https://files.pythonhosted.org/packages/5c/0f/9aafc63f9661222b819b391c187eed29fc90ad5935f9690e5ecc2d2047a4/rapidfuzz-3.14.5-cp314-cp314t-win_amd64.whl", hash = "sha256:7d5ca9c7832e6879a707296d1463685f7c243a27846227044504741640caec66", size = 1632357, upload-time = "2026-04-07T11:16:13.1Z" }, - { url = "https://files.pythonhosted.org/packages/70/a6/51fc1b0e61e3326e1c68a61cfd0c6b3c34c843681c4b1eefbf0596f59162/rapidfuzz-3.14.5-cp314-cp314t-win_arm64.whl", hash = "sha256:3e91dcd2549b8f8d843f98ba03a17e01f3d8b72ce942adbbb6761bc58ffce813", size = 855409, upload-time = "2026-04-07T11:16:15.787Z" }, -] - -[[package]] -name = "readme-renderer" -version = "44.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "nh3" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, -] - -[[package]] -name = "referencing" -version = "0.37.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "attrs" }, - { name = "rpds-py" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, -] - -[[package]] -name = "requests" -version = "2.33.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, -] - -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, -] - -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, -] - -[[package]] -name = "rfc3986" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, -] - -[[package]] -name = "rfc3986-validator" -version = "0.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760, upload-time = "2019-10-28T16:00:19.144Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242, upload-time = "2019-10-28T16:00:13.976Z" }, -] - -[[package]] -name = "rfc3987-syntax" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "lark" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239, upload-time = "2025-07-18T01:05:05.015Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046, upload-time = "2025-07-18T01:05:03.843Z" }, -] - -[[package]] -name = "rich" -version = "15.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, -] - -[[package]] -name = "roman-numerals" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, -] - -[[package]] -name = "rpds-py" -version = "0.30.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, - { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, - { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, - { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, - { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, - { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, - { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, - { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, - { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, - { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, - { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, - { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, - { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, - { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, - { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, - { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, - { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, - { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, - { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, - { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, - { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, - { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, - { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, - { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, - { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, - { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, - { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, - { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, - { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, - { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, - { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, - { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, - { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, - { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, - { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, - { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, - { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, - { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, - { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, - { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, - { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, - { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, - { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, - { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, - { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, - { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, - { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, - { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, - { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, - { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, - { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, - { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, - { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, - { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, - { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, - { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, - { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, - { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, - { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, - { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, - { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, - { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, -] - -[[package]] -name = "ruff" -version = "0.15.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" }, - { url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" }, - { url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" }, - { url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" }, - { url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" }, - { url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" }, - { url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" }, - { url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" }, - { url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" }, - { url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" }, - { url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" }, - { url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" }, - { url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" }, -] - -[[package]] -name = "scipy" -version = "1.17.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, - { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, - { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, - { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, - { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, - { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, - { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, - { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, - { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, - { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, - { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, - { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, - { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, - { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, - { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, - { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, - { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, - { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, - { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, - { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, - { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, - { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, - { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, - { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, - { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, - { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, - { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, - { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, - { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, - { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, - { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, - { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, - { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, - { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, - { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, - { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, - { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, - { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, - { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, - { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, - { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, -] - -[[package]] -name = "secretstorage" -version = "3.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, -] - -[[package]] -name = "send2trash" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255, upload-time = "2026-01-14T06:27:36.056Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610, upload-time = "2026-01-14T06:27:35.218Z" }, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - -[[package]] -name = "snowballstemmer" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, -] - -[[package]] -name = "soupsieve" -version = "2.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/80/f1/93422647dd7e461f23d254e6b2bfa687a85b53aeb4903fcdbb74474d4584/soupsieve-2.9.tar.gz", hash = "sha256:acee8417325c5653e1377dc31eccad59eb82cbc65942afe6174c53b3aaad63fc", size = 122122, upload-time = "2026-07-19T01:35:18.425Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/d6/3185ab5ad1280319b31986898f3206dd7227cd75e293d4dba2a5e6bf27a0/soupsieve-2.9-py3-none-any.whl", hash = "sha256:a2b2c76d67df2382d245409fd71e321a571717e58463efa32ace87dcadac2c12", size = 37387, upload-time = "2026-07-19T01:35:17.106Z" }, -] - -[[package]] -name = "sphinx" -version = "9.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "alabaster" }, - { name = "babel" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "docutils" }, - { name = "imagesize" }, - { name = "jinja2" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "requests" }, - { name = "roman-numerals" }, - { name = "snowballstemmer" }, - { name = "sphinxcontrib-applehelp" }, - { name = "sphinxcontrib-devhelp" }, - { name = "sphinxcontrib-htmlhelp" }, - { name = "sphinxcontrib-jsmath" }, - { name = "sphinxcontrib-qthelp" }, - { name = "sphinxcontrib-serializinghtml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/f7/b1884cb3188ab181fc81fa00c266699dab600f927a964df02ec3d5d1916a/sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978", size = 3921742, upload-time = "2025-12-31T15:09:25.561Z" }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asttokens" }, - { name = "executing" }, - { name = "pure-eval" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, -] - -[[package]] -name = "terminado" -version = "0.18.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ptyprocess", marker = "os_name != 'nt'" }, - { name = "pywinpty", marker = "os_name == 'nt'" }, - { name = "tornado" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701, upload-time = "2024-03-12T14:34:39.026Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154, upload-time = "2024-03-12T14:34:36.569Z" }, -] - -[[package]] -name = "tinycss2" -version = "1.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "webencodings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7a/fd/7a5ee21fd08ff70d3d33a5781c255cbe779659bd03278feb98b19ee550f4/tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", size = 87085, upload-time = "2024-10-24T14:58:29.895Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610, upload-time = "2024-10-24T14:58:28.029Z" }, -] - -[[package]] -name = "tomli-w" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, -] - -[[package]] -name = "tomlkit" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, -] - -[[package]] -name = "tornado" -version = "6.5.9" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/22/15/ca7aaebb77f850493cba71ace508aeffb896d1ad8976417b48b79823dbd2/tornado-6.5.9.tar.gz", hash = "sha256:4d868544ebdf2fc155239a74397f65ebfea9b4d3635296c96b5dfb0701c354f5", size = 536145, upload-time = "2026-09-14T18:08:37.434Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/4a/4d3459fd2f360dd99233f69f22b36012a42856ace526da2c6da3cb0e6df2/tornado-6.5.9-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:dea3bee433b73d6312d993ce89ad5f2b8d3ed9fde56e79ba5342b9edb78d342a", size = 464279, upload-time = "2026-09-14T18:08:20.909Z" }, - { url = "https://files.pythonhosted.org/packages/26/4b/5af2f7dcd674e2f1cacef763456f79823404f50954789d6e1ec7a70cd58a/tornado-6.5.9-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:22bed49cdb55292d1d58f46008ba2cc3020e5b84995ef6ae749676c8b5309514", size = 462440, upload-time = "2026-09-14T18:08:22.711Z" }, - { url = "https://files.pythonhosted.org/packages/9e/0f/38f8d16011de3b1ce2babb4c3bcefb126d2dc47d04878ae3ad5c39a00caa/tornado-6.5.9-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0cc0876a58eee9143476f24901b1b8eb4d52443283ade2b6bca650dd1da04f84", size = 465496, upload-time = "2026-09-14T18:08:24.407Z" }, - { url = "https://files.pythonhosted.org/packages/8c/e9/3888f265d1e287fc63cd61a6696f96a650024e32f9e89c0b1817ac5c40f4/tornado-6.5.9-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc0d703cc7ec92b47ebfe236c97cd765c1f2e45744a2e156e6677e54c948274d", size = 466468, upload-time = "2026-09-14T18:08:25.928Z" }, - { url = "https://files.pythonhosted.org/packages/d6/8b/d2bcf417981fa6f7698ab84a52394a76cf608e6704d266e2e0e29f1fd9d0/tornado-6.5.9-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:93f9ab0226ef625b94cacb7981a5af104f1735ea02f92282abe5c389109fb31d", size = 466303, upload-time = "2026-09-14T18:08:27.566Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/c5cfab11420c3f908238a6c861b8f3864fdbd2b2b87e30c9c16f36a11512/tornado-6.5.9-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5801bbf94dfde8d21087b6aa0b17f3a5dab6110dfe44e7e4492df5de393ac15b", size = 465706, upload-time = "2026-09-14T18:08:29.182Z" }, - { url = "https://files.pythonhosted.org/packages/ab/36/5a5da0aa15b7afdd46ece09c004a75af7d00f6a96b692bf696ff94bc2a55/tornado-6.5.9-cp39-abi3-win32.whl", hash = "sha256:dca9f377e80efe5dd4d52a2776b67492a81a39b2d931de169b6b36d0c1da444c", size = 466750, upload-time = "2026-09-14T18:08:30.836Z" }, - { url = "https://files.pythonhosted.org/packages/34/d7/2446cf3125372e2b4be6483b4b93f360dc50ce2493864755484b23bb50f2/tornado-6.5.9-cp39-abi3-win_amd64.whl", hash = "sha256:f810730ac71eadf009e6e5cb6d534d096887310f5649cff19d7e0d19c0bf4ee7", size = 467191, upload-time = "2026-09-14T18:08:32.521Z" }, - { url = "https://files.pythonhosted.org/packages/39/70/f1024364bf78fa921c63ef2883d36a3b3b705750c98ae174593afaab7a65/tornado-6.5.9-cp39-abi3-win_arm64.whl", hash = "sha256:b5f4d92798337260c6d9f9f0e400c7539dd5e55cf93479a3f8508c9aa09c2b3b", size = 466211, upload-time = "2026-09-14T18:08:35.18Z" }, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, -] - -[[package]] -name = "trove-classifiers" -version = "2026.1.14.14" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/43/7935f8ea93fcb6680bc10a6fdbf534075c198eeead59150dd5ed68449642/trove_classifiers-2026.1.14.14.tar.gz", hash = "sha256:00492545a1402b09d4858605ba190ea33243d361e2b01c9c296ce06b5c3325f3", size = 16997, upload-time = "2026-01-14T14:54:50.526Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl", hash = "sha256:1f9553927f18d0513d8e5ff80ab8980b8202ce37ecae0e3274ed2ef11880e74d", size = 14197, upload-time = "2026-01-14T14:54:49.067Z" }, -] - -[[package]] -name = "twine" -version = "6.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "id" }, - { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, - { name = "packaging" }, - { name = "readme-renderer" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "rfc3986" }, - { name = "rich" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" }, -] - -[[package]] -name = "typer" -version = "0.25.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7b/27/ede8cec7596e0041ba7e7b80b47d132562f56ff454313a16f6084e555c9f/typer-0.25.0.tar.gz", hash = "sha256:123eaf9f19bb40fd268310e12a542c0c6b4fab9c98d9d23342a01ff95e3ce930", size = 120150, upload-time = "2026-04-26T08:46:14.767Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/72/193d4e586ec5a4db834a36bbeb47641a62f951f114ffd0fe5b1b46e8d56f/typer-0.25.0-py3-none-any.whl", hash = "sha256:ac01b48823d3db9a83c9e164338057eadbb1c9957a2a6b4eeb486669c560b5dc", size = 55993, upload-time = "2026-04-26T08:46:15.889Z" }, -] - -[[package]] -name = "types-requests" -version = "2.33.0.20260408" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/69/6a/749dc53a54a3f35842c1f8197b3ca6b54af6d7458a1bfc75f6629b6da666/types_requests-2.33.0.20260408.tar.gz", hash = "sha256:95b9a86376807a216b2fb412b47617b202091c3ea7c078f47cc358d5528ccb7b", size = 23882, upload-time = "2026-04-08T04:34:49.33Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/b8/78fd6c037de4788c040fdd323b3369804400351b7827473920f6c1d03c10/types_requests-2.33.0.20260408-py3-none-any.whl", hash = "sha256:81f31d5ea4acb39f03be7bc8bed569ba6d5a9c5d97e89f45ac43d819b68ca50f", size = 20739, upload-time = "2026-04-08T04:34:48.325Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] - -[[package]] -name = "tzdata" -version = "2026.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, -] - -[[package]] -name = "uri-template" -version = "1.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678, upload-time = "2023-06-21T01:49:05.374Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140, upload-time = "2023-06-21T01:49:03.467Z" }, -] - -[[package]] -name = "urllib3" -version = "2.8.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, -] - -[[package]] -name = "userpath" -version = "1.9.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/b7/30753098208505d7ff9be5b3a32112fb8a4cb3ddfccbbb7ba9973f2e29ff/userpath-1.9.2.tar.gz", hash = "sha256:6c52288dab069257cc831846d15d48133522455d4677ee69a9781f11dbefd815", size = 11140, upload-time = "2024-02-29T21:39:08.742Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/43/99/3ec6335ded5b88c2f7ed25c56ffd952546f7ed007ffb1e1539dc3b57015a/userpath-1.9.2-py3-none-any.whl", hash = "sha256:2cbf01a23d655a1ff8fc166dfb78da1b641d1ceabf0fe5f970767d380b14e89d", size = 9065, upload-time = "2024-02-29T21:39:07.551Z" }, -] - -[[package]] -name = "uv" -version = "0.11.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/da/34/609d5d01ba21dc8f0974610ca7802fbb2c946a0c38665cfe5c5aeddbefb5/uv-0.11.15.tar.gz", hash = "sha256:755f959ec6a2fd8ccb6ee76ad90ab759d2eb1f4797444078645dd1ee4bca92d6", size = 4159545, upload-time = "2026-05-18T19:57:48.133Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/7c/dcc230c5911884d8848145dabcac8fb95a5ed6f9fe1c57fae8242618f28a/uv-0.11.15-py3-none-linux_armv6l.whl", hash = "sha256:83b04ab49514a0a761ffedb36a748ee81f87746671e72088e5f32c9585e5f1a9", size = 23110183, upload-time = "2026-05-18T19:57:23.051Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f3/efd4e044b60eb9c3c12ee386be098d56c335538ccec7caa49349cfba9344/uv-0.11.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cae61f737be075b90be9e3f07d961072aed7019f4c9b8ed5c5d41c4d6cade3", size = 22637941, upload-time = "2026-05-18T19:57:26.752Z" }, - { url = "https://files.pythonhosted.org/packages/a6/b8/48627f895a1569e576822e0a8416aa4797eb4a4551de21a4ad97b9b5819d/uv-0.11.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9accae33619a9166e5c48531deb455d672cfb89f9357a00975e669c76b0bd49f", size = 21258803, upload-time = "2026-05-18T19:57:05.473Z" }, - { url = "https://files.pythonhosted.org/packages/af/50/4bc8a148274feabee2d9c9f1fa15009e10c0228dfe57981ee3ea2ef1d481/uv-0.11.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:c0cf52cd6d50bb9e05e2d968f45f80761107e4cbc8d4a26d9758f9d8274aaec1", size = 23066178, upload-time = "2026-05-18T19:57:33.058Z" }, - { url = "https://files.pythonhosted.org/packages/a9/56/139fc3bec9a8b0a25bfe2196123adb9f16124da437bf4fbcf0d21cfcafb2/uv-0.11.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:49dc6ed70bff00937384f96cdc4b1a4742d18e5504ec2c4a1214dba2dee5687a", size = 22705332, upload-time = "2026-05-18T19:57:36.714Z" }, - { url = "https://files.pythonhosted.org/packages/ca/b0/b18b3dd204f8c213236a1ebd148e009861637129a8cce34df0e9aa22ed40/uv-0.11.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:adb9a89352539fdd8f7cd5f9966cf9f94fc5b98e0ccdf5003a04123dc6423bec", size = 22707534, upload-time = "2026-05-18T19:58:04.117Z" }, - { url = "https://files.pythonhosted.org/packages/76/36/3ca09f95572df99d361b49c96b1297149e96e120d8d1ecf074095a4b6da4/uv-0.11.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40ff67e3f8e8a7533781a2e892a534975a93acb83ea35460e64e7b2bf2111774", size = 24096607, upload-time = "2026-05-18T19:58:11.625Z" }, - { url = "https://files.pythonhosted.org/packages/64/be/3bdee21a296bbf5336a526e3613d0e7d4538dacc39c62d7fcba55d15f6b0/uv-0.11.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6463a299ed7e6b5a800ed6f108af8e1588352629424133ddef7572b0e1e1118", size = 25082562, upload-time = "2026-05-18T19:57:40.69Z" }, - { url = "https://files.pythonhosted.org/packages/cd/73/f371f3689ffe741066468d001d85f739fc4b5574de83b639ef19b5e8a7f4/uv-0.11.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68c1e62d4b78578b90b833553286b65d6a7e327537716441068583ba652ec4f5", size = 24253391, upload-time = "2026-05-18T19:57:18.47Z" }, - { url = "https://files.pythonhosted.org/packages/d3/16/fe392d618af6b00c064b3e718d585dcf791546a77c5123a5bec07ce53a0a/uv-0.11.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98edf1bdaf82447014852051d93e3ee95012509c567bf057fd117e6bdbd9a807", size = 24415871, upload-time = "2026-05-18T19:58:19.651Z" }, - { url = "https://files.pythonhosted.org/packages/6e/24/2e92a052fb6334fcd746d1c7cb57847c204b118c84f5da53c0f9e129f7b7/uv-0.11.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:be8f76d25bcf4c92bb384240ac1bf9aa7f51063d0bdeca4c9cf0ec3ed8b145e0", size = 23159007, upload-time = "2026-05-18T19:57:10.653Z" }, - { url = "https://files.pythonhosted.org/packages/3d/2e/6923d0658d164bb2c435ed1868aa2d49b3074594679917a001ff92dc95bb/uv-0.11.15-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:f9f4fbbf4fe485522054f3c7496c6e8e932d6436e4200ff3daf718db0b7c7bd5", size = 23769385, upload-time = "2026-05-18T19:58:15.856Z" }, - { url = "https://files.pythonhosted.org/packages/a4/99/7e34cd949e57360814e8064cc9fb7104df445d0f6a663504e5f7473480aa/uv-0.11.15-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0ed920e896b2fd13a35031707e307e42fbb2681458b967440a17272d86d49137", size = 23860973, upload-time = "2026-05-18T19:57:55.575Z" }, - { url = "https://files.pythonhosted.org/packages/28/98/8fe1f5f9d816e94569a0298dd8e0936801097625fa1952162951f0d628b6/uv-0.11.15-py3-none-musllinux_1_1_i686.whl", hash = "sha256:41d907611f3e6a13262807fd7f0a17849f76285ca80f536f6b3943732bdc6656", size = 23431392, upload-time = "2026-05-18T19:57:59.814Z" }, - { url = "https://files.pythonhosted.org/packages/cc/6b/76a1ce2fa860026913a5941700cdc7d715fce9c3277a3fa3489cf2523ca0/uv-0.11.15-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e3b68f8bf1a4568710f77e5bda9182ce7682811d89a8e7468c22460e032b234d", size = 24519478, upload-time = "2026-05-18T19:57:51.165Z" }, - { url = "https://files.pythonhosted.org/packages/43/60/1d58e8a05718cb50494763115710b73846cacb651fd735d285233fd72c59/uv-0.11.15-py3-none-win32.whl", hash = "sha256:8e2da3076761086a5b76869c3f38ef0509c836046ef41ddd19485dfd7271dca9", size = 22020178, upload-time = "2026-05-18T19:58:07.64Z" }, - { url = "https://files.pythonhosted.org/packages/55/53/40fcefcb348af660488597ed3c01363df7344e60611f8883750dc596f5c6/uv-0.11.15-py3-none-win_amd64.whl", hash = "sha256:cc3915ab291a1ecaf31de05f5d8bd70d09c66fe9911a53f70d9efa62ff0dbd8a", size = 24668779, upload-time = "2026-05-18T19:57:44.894Z" }, - { url = "https://files.pythonhosted.org/packages/e5/7d/fa3a9960c95af9bbe2a629048760d0b9b4fead8ccd4f2235af747ec7cdf0/uv-0.11.15-py3-none-win_arm64.whl", hash = "sha256:4f39426a13dee24897aed60c4b98058c66f18bd983885ac5f4a54a04b24fbddf", size = 23198178, upload-time = "2026-05-18T19:57:14.68Z" }, -] - -[[package]] -name = "virtualenv" -version = "21.7.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, - { name = "python-discovery" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/50/c9b84eb106d0db420b9878dac0a386c5791726f127ce20b06897f6e3a1e9/virtualenv-21.7.13.tar.gz", hash = "sha256:0355558b6f33619aab31347e43643b0ebc97f61ea3acf617b2b69e1f8a843d11", size = 5360306, upload-time = "2026-09-18T04:35:49.354Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/ce/e74453531b0c49a58d0e8a4f9bab4495705859fee4a4f7de27d58f4a791a/virtualenv-21.7.13-py3-none-any.whl", hash = "sha256:1bea5af7463f59c4719db48fe739579a2a4f569c96f26c086edda85c96da9f59", size = 5328680, upload-time = "2026-09-18T04:35:47.194Z" }, -] - -[[package]] -name = "wcwidth" -version = "0.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, -] - -[[package]] -name = "webcolors" -version = "25.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491, upload-time = "2025-10-31T07:51:03.977Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905, upload-time = "2025-10-31T07:51:01.778Z" }, -] - -[[package]] -name = "webencodings" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, -] - -[[package]] -name = "websocket-client" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576, upload-time = "2025-10-07T21:16:36.495Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616, upload-time = "2025-10-07T21:16:34.951Z" }, -] - -[[package]] -name = "wheel" -version = "0.47.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/39/62/75f18a0f03b4219c456652c7780e4d749b929eb605c098ce3a5b6b6bc081/wheel-0.47.0.tar.gz", hash = "sha256:cc72bd1009ba0cf63922e28f94d9d83b920aa2bb28f798a31d0691b02fa3c9b3", size = 63854, upload-time = "2026-04-22T15:51:27.727Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/1b/9e33c09813d65e248f7f773119148a612516a4bea93e9c6f545f78455b7c/wheel-0.47.0-py3-none-any.whl", hash = "sha256:212281cab4dff978f6cedd499cd893e1f620791ca6ff7107cf270781e587eced", size = 32218, upload-time = "2026-04-22T15:51:26.296Z" }, -] - -[[package]] -name = "widgetsnbextension" -version = "4.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bd/f4/c67440c7fb409a71b7404b7aefcd7569a9c0d6bd071299bf4198ae7a5d95/widgetsnbextension-4.0.15.tar.gz", hash = "sha256:de8610639996f1567952d763a5a41af8af37f2575a41f9852a38f947eb82a3b9", size = 1097402, upload-time = "2025-11-01T21:15:55.178Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/0e/fa3b193432cfc60c93b42f3be03365f5f909d2b3ea410295cf36df739e31/widgetsnbextension-4.0.15-py3-none-any.whl", hash = "sha256:8156704e4346a571d9ce73b84bee86a29906c9abfd7223b7228a28899ccf3366", size = 2196503, upload-time = "2025-11-01T21:15:53.565Z" }, -] From a5734e23b342133288b2f9c99e205c0f665a3383 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 20 Oct 2026 13:23:39 -1000 Subject: [PATCH 170/312] Update and pin ruff, mypy, pylint; fix new warnings - Pin ruff 0.17.1, mypy 2.4.0, pylint 4.1.3 so CI is stable without uv.lock. - mypy 2 renamed the old allow_redefinition behavior to allow_redefinition_old; the new flexible mode caused 28 spurious errors. - Levenshtein now ships types and ignores isjunk; pass None. - Remove placeholder tests flagged by pylint's redundant-unittest-assert. AI-assisted (Claude) --- music21/chord/tables.py | 3 --- music21/defaults.py | 12 +----------- music21/search/segment.py | 2 +- music21/sieve.py | 3 --- pyproject.toml | 8 ++++---- 5 files changed, 6 insertions(+), 22 deletions(-) diff --git a/music21/chord/tables.py b/music21/chord/tables.py index 17aa3d4b4c..ce9c031ca7 100644 --- a/music21/chord/tables.py +++ b/music21/chord/tables.py @@ -1948,9 +1948,6 @@ def seekChordTablesAddress(c: chord.Chord) -> ChordTableAddress: # ------------------------------------------------------------------------------ class Test(unittest.TestCase): - def testDummy(self): - self.assertEqual(True, True) - def testTnIndexToChordInfo(self): for key, value in tnIndexToChordInfo.items(): self.assertEqual(len(key), 3) diff --git a/music21/defaults.py b/music21/defaults.py index 99feeb555c..5f7eb0ba50 100644 --- a/music21/defaults.py +++ b/music21/defaults.py @@ -13,7 +13,6 @@ ''' from __future__ import annotations -import unittest from music21 import _version @@ -113,15 +112,6 @@ # ----------------------------------------------------------------||||||||||||-- -class Test(unittest.TestCase): - ''' - Unit tests - ''' - def testTest(self): - self.assertEqual(1, 1) - - -# ----------------------------------------------------------------||||||||||||-- if __name__ == '__main__': import music21 - music21.mainTest(Test) + music21.mainTest() diff --git a/music21/search/segment.py b/music21/search/segment.py index 792d00ea67..328c5432a6 100644 --- a/music21/search/segment.py +++ b/music21/search/segment.py @@ -327,7 +327,7 @@ def getDifflibOrPyLev( try: # noinspection PyPackageRequirements from Levenshtein import StringMatcher as pyLevenshtein # type: ignore - smObject = pyLevenshtein.StringMatcher(junk, '', seq2Seq) + smObject = pyLevenshtein.StringMatcher(None, '', seq2Seq) except ImportError: smObject = difflib.SequenceMatcher(junk, '', seq2Seq) return smObject diff --git a/music21/sieve.py b/music21/sieve.py index a5466e1ec7..4e7962e118 100644 --- a/music21/sieve.py +++ b/music21/sieve.py @@ -2006,9 +2006,6 @@ def getIntervalSequence(self) -> list[interval.Interval]: # ------------------------------------------------------------------------------ class Test(unittest.TestCase): - def testDummy(self): - self.assertEqual(True, True) - def pitchOut(self, listIn): out = '[' for p in listIn: diff --git a/pyproject.toml b/pyproject.toml index dd46d7cca8..f0453d7dc3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,12 +69,12 @@ dev = [ 'hatchling', 'ipython', 'jupyter', - 'mypy>=1.16.0', + 'mypy==2.4.0', 'nbconvert', 'nbval', - 'pylint>=3.2.0', + 'pylint==4.1.3', 'pytest', - 'ruff', + 'ruff==0.17.1', 'sphinx', 'twine', 'types-requests', @@ -102,7 +102,7 @@ include = [ ] [tool.mypy] -allow_redefinition = true +allow_redefinition_old = true warn_no_return = false [tool.ruff] From 0e69c91a6853c6cf05d1753bfd966b7348ab4f53 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 20 Oct 2026 13:59:05 -1000 Subject: [PATCH 171/312] Support Python 3.15: CI matrix and classifiers AI-assisted (Claude) --- .github/workflows/maincheck.yml | 2 +- music21/__init__.py | 2 +- pyproject.toml | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maincheck.yml b/.github/workflows/maincheck.yml index 9f22645724..e73b014478 100644 --- a/.github/workflows/maincheck.yml +++ b/.github/workflows/maincheck.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.12, 3.13, 3.14] + python-version: [3.12, 3.13, 3.14, 3.15] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 diff --git a/music21/__init__.py b/music21/__init__.py index c6d77aab8e..901cb82375 100644 --- a/music21/__init__.py +++ b/music21/__init__.py @@ -53,7 +53,7 @@ If you have the wrong version there are several options for getting the right one. - - 1. (Best) Upgrade to a recent version of Python 3 (such as 3.14). + - 1. (Best) Upgrade to a recent version of Python 3 (such as 3.15). The great features there will more than make up for the headache of downloading diff --git a/pyproject.toml b/pyproject.toml index f0453d7dc3..ad222a7633 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ classifiers = [ "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Artistic Software", "Topic :: Multimedia :: Sound/Audio", "Topic :: Multimedia :: Sound/Audio :: Conversion", From d57554a5473996cb330a54954af3ac9685c3c8a2 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 20 Oct 2026 14:11:59 -1000 Subject: [PATCH 172/312] Python 3.15 doctest messages; update CI actions; one uv cache saver - Two doctests matched error text that 3.15 rewords; loosen with ellipses. - actions/checkout and setup-python v7 (Node 20 deprecation warnings). - Only run_tests saves the uv cache, avoiding the reserve-cache race. AI-assisted (Claude) --- .github/workflows/maincheck.yml | 19 +++++++++++-------- music21/metadata/primitives.py | 3 ++- music21/note.py | 3 ++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/maincheck.yml b/.github/workflows/maincheck.yml index e73b014478..ba9fed9324 100644 --- a/.github/workflows/maincheck.yml +++ b/.github/workflows/maincheck.yml @@ -21,8 +21,8 @@ jobs: matrix: python-version: [3.12, 3.13, 3.14, 3.15] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v7 with: python-version: ${{ matrix.python-version }} - name: Install uv @@ -54,15 +54,16 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.14' - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true + save-cache: false cache-dependency-glob: | pyproject.toml - name: Install dependencies @@ -76,15 +77,16 @@ jobs: ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.14' - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true + save-cache: false cache-dependency-glob: | pyproject.toml - name: Install dependencies @@ -98,15 +100,16 @@ jobs: mypy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v7 with: python-version: '3.14' - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true + save-cache: false cache-dependency-glob: | pyproject.toml - name: Install dependencies diff --git a/music21/metadata/primitives.py b/music21/metadata/primitives.py index 3c87afcfe2..02cd6de673 100644 --- a/music21/metadata/primitives.py +++ b/music21/metadata/primitives.py @@ -398,8 +398,9 @@ def datetime(self): >>> a.datetime Traceback (most recent call last): - TypeError: function missing required argument 'day' (pos 3) + TypeError: ... missing required argument 'day' (pos 3) ''' + # When 3.15 is the minimum change ... to datetime() post = [] # order here is order for datetime # TODO: need defaults for incomplete times. diff --git a/music21/note.py b/music21/note.py index 1d69923fce..9cd79c4004 100644 --- a/music21/note.py +++ b/music21/note.py @@ -1850,7 +1850,7 @@ class Unpitched(NotRest): >>> unp.displayStep = 'G' >>> unp.pitch Traceback (most recent call last): - AttributeError: 'Unpitched' object has no attribute 'pitch'. Did you mean: 'pitches'? + AttributeError: 'Unpitched' object has no attribute 'pitch'. Did you mean...'...pitches'... Unpitched elements generally have an instrument object associated with them: @@ -1873,6 +1873,7 @@ class Unpitched(NotRest): >>> unp == unp2 False ''' + # When 3.15 is the minimum change ... to "Did you mean '.pitches' instead of '.pitch'?" equalityAttributes = ('displayStep', 'displayOctave', 'storedInstrument') def __init__( From da064f355dcdc73900e1f417db1790bf6c394292 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Tue, 20 Oct 2026 15:22:02 -1000 Subject: [PATCH 173/312] Run CI coverage on Python 3.14 coverage.py uses sys.monitoring by default on 3.14+, far faster than the 3.13 settrace core (3.13 coverage job took ~9 min vs ~3 for others). AI-assisted (Claude) --- .github/workflows/maincheck.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maincheck.yml b/.github/workflows/maincheck.yml index ba9fed9324..47a53a4ea6 100644 --- a/.github/workflows/maincheck.yml +++ b/.github/workflows/maincheck.yml @@ -12,7 +12,8 @@ on: 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' + # 3.14+ is much faster: coverage uses sys.monitoring there by default. + PY_VERSION_WITH_COVERAGE: '3.14' jobs: run_tests: From 54bea69e41dced92e6af331123435165580f5069 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 21 Oct 2026 07:03:01 -0400 Subject: [PATCH 174/312] Read segno and coda inside a MusicXML barline (fixes #2083) Written by an AI agent (Chris, raisingchris2026), working alone. --- music21/musicxml/test_xmlToM21.py | 36 +++++++++++++++++++++++++++++++ music21/musicxml/xmlToM21.py | 10 +++++++++ 2 files changed, 46 insertions(+) diff --git a/music21/musicxml/test_xmlToM21.py b/music21/musicxml/test_xmlToM21.py index bbac94dc85..b3ebb6dd7a 100644 --- a/music21/musicxml/test_xmlToM21.py +++ b/music21/musicxml/test_xmlToM21.py @@ -1832,6 +1832,42 @@ def testPianoStaffWithRepeatEndings(self): p_notes2 = [n.name for n in p_expanded[note.Note]] self.assertEqual(p_notes2, ['G', 'A', 'G', 'B']) + def testSegnoAndCodaInBarline(self): + ''' + MusicXML allows and inside a , not only in a + . They should be imported, so that a later D.S. can find the segno. + ''' + from music21 import converter + + def measure(number, step, before='', after=''): + return (f'{before}{step}' + f'44' + f'whole{after}') + + def score(*measures): + return ('' + 'Music' + + ''.join(measures) + '') + + attributes = '1' + ds = 'D.S.' + s = converter.parse(score(measure(1, 'C', attributes), + measure(2, 'D', ''), + measure(3, 'E', after=ds)), + format='musicxml') + segnos = s.parts.first().measure(2)[repeat.Segno] + self.assertEqual(len(segnos), 1) + self.assertEqual(segnos.first().offset, 0.0) + expanded = s.expandRepeats() + self.assertEqual([n.name for n in expanded[note.Note]], ['C', 'D', 'E', 'D', 'E']) + + s = converter.parse(score(measure(1, 'C', attributes), + measure(2, 'D', '')), + format='musicxml') + codas = s.parts.first().measure(2)[repeat.Coda] + self.assertEqual(len(codas), 1) + self.assertEqual(codas.first().offset, 0.0) + if __name__ == '__main__': import music21 diff --git a/music21/musicxml/xmlToM21.py b/music21/musicxml/xmlToM21.py index 7e524ff668..6a4d91c0e9 100644 --- a/music21/musicxml/xmlToM21.py +++ b/music21/musicxml/xmlToM21.py @@ -5194,6 +5194,16 @@ def xmlBarline(self, mxBarline: ET.Element) -> None: # set number; '' or None is interpreted as 1 + # a barline may also carry a segno or coda (MusicXML allows both inside ); + # put them in the measure where the barline is, as a direction would + for tag, markClass in (('segno', repeat.Segno), ('coda', repeat.Coda)): + mxMark = mxBarline.find(tag) + if mxMark is not None: + rm = markClass() + synchronizeIds(mxMark, rm) + self.setPosition(mxMark, rm) + self.insertCoreAndRef(self.offsetMeasureNote, None, rm) + if barline.location == 'left': # environLocal.printDebug(['setting left barline', barline]) m.leftBarline = barline From 2ad9dc9aa45a62fb9c6112e8e1647cf8ed4fdaa8 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 21 Oct 2026 10:40:02 -1000 Subject: [PATCH 175/312] Add AI contribution policy to AGENTS.md AI-assisted (Claude) --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index 9fb1bf74f8..c3d73b0e18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,6 +51,7 @@ - "established contributors" are people with at least 3 PRs merged or a history of contributing to issues and the list that goes back at least 1 year. There are exceptions to general rules for them below. - "core dev" means someone officially part of the project team or with 20+ PRs merged. Michael Cuthbert, Jacob Walls, Joseph VanderStel are non-exhaustive examples of people in that group who are still often contributing in 2026-- they and their agents can make exceptions to these rules - AI Agents not for core dev are forbidden to make any PRs or contributions to PRs whose user commands do not explicitly mention `music21`. Example of forbidden: "find an Open Source project and fix an open issue", "Find a bug in a music project and fix it." AI agents for non-established contributors are forbidden from making contributions to instructions that vaguely say "fix something in music21." +- Bot contributions will only be accepted where a named human/human-controlled-account with established web presence takes legal responsibility for their contributions. Bots attached to anonymous users may not contribute. # Testing From f086d8c3624f295c1db685eafde3fe3617d77965 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Wed, 21 Oct 2026 10:49:45 -1000 Subject: [PATCH 176/312] Allow amend + force-with-lease when asked to fold in a fix AI-assisted (Claude) --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index c3d73b0e18..ab17d3c615 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -141,7 +141,7 @@ after addressing the problem. (A blind close or close with "not accepted" etc. generally means that the issue/PR has too many problems to easily solve and has become a burden for the maintainer). - Do not include a "Tests run" section unless the testing procedure was unusual (like it affects part of the system without standard tests, like the testing system itself.) -- Once a PR has been opened and a review or comment has been made on it, commits should not be amended or force-pushed (this makes it impossible to review "changes since the review"). Put changes in response to reviews/comments in new commits. +- Once a PR has been opened and a review or comment has been made on it, commits should not be amended or force-pushed (this makes it impossible to review "changes since the review"). Put changes in response to reviews/comments in new commits. If asked to fold a fix into the commit it changes, amend, force-push with `--force-with-lease` against an explicit SHA, and say so. - While someone is reviewing a PR or a pushed branch, "do X" is not "commit and push X": make the change and leave it unstaged. When the list looks finished (or you hear "done!" or "push it"), offer to commit, or to commit and push. Batch a round's small fixes into From 609178213c54dc1658be7accb9f6440e3209851e Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 22 Oct 2026 10:25:39 -1000 Subject: [PATCH 177/312] Keep dotfiles out of wheel and sdist The 10.5.0 wheel shipped music21/.claude/settings.local.json. It was ignored only by a global git ignore file, which hatchling does not read. Fixes #2096. AI-assisted (Claude) --- pyproject.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ad222a7633..5454841df7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,6 +97,12 @@ default-groups = ["dev"] [tool.hatch.version] path = "music21/_version.py" +[tool.hatch.build] +# hatchling reads only this repo's .gitignore, not a global one +exclude = [ + ".*", +] + [tool.hatch.build.targets.sdist] include = [ "music21", From 026c352e08c63a880f38bff36c27558317209a4e Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 22 Oct 2026 11:03:55 -1000 Subject: [PATCH 178/312] myke edits --- dist/dist.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/dist/dist.py b/dist/dist.py index 78ae8a5af4..69ef713543 100644 --- a/dist/dist.py +++ b/dist/dist.py @@ -5,7 +5,7 @@ # Authors: Christopher Ariza # Michael Scott Asato Cuthbert # -# Copyright: Copyright © 2010-2025 Michael Scott Asato Cuthbert +# Copyright: Copyright © 2010-2026 Michael Scott Asato Cuthbert # License: BSD, see license.txt # ------------------------------------------------------------------------------ ''' @@ -20,8 +20,8 @@ lot of tests in corpus, metadata, etc. so don't skip the next step! 3. IMPORTANT: `uv run documentation/testDocumentation.py` and afterwards fix errors [*] -[*] you will need pytest, docutils, nbval installed (along with ipython and jupyter), -you cannot check to see if fixed tests work while it is running. +[*] `uv sync` installs pytest, docutils, nbval, ipython, and jupyter. +You cannot check to see if fixed tests work while it is running. This takes a while and runs single core, and then almost always needs code patches so allocate time (2 min on M4). Start working on the announcement while it's running. Cannot run 2 and 3 in parallel, since often metadata changes cause documentation changes. @@ -49,7 +49,7 @@ note that since AI-Agent world, more and more links are being blocked from bot checking. 9. `uv run documentation/make.py` [*] -[*] you will need sphinx, Jupyter (uv will install it), markdown, and pandoc (.dmg) installed +[*] `uv sync` installs sphinx and Jupyter; you will also need markdown and pandoc (.dmg) 10. move music21 documentation/build/html to music21.org/music21docs/ via Amazon S3 (contact MSAC for authentication if need be) @@ -60,17 +60,14 @@ 11. zip up documentation/build/html and get ready to upload/delete it (you can put on your desktop or wherever you like). - Rename to music21-9.5.0-docs.zip (skip for Alpha/Beta) + Rename to music21-11.1.0-docs.zip (skip for Alpha/Beta) -12. From the music21 main folder (not subfolder) run "hatch build" -- - requires hatch to be installed "pip install hatch" -- brew version of hatch - was giving Environment `default` is incompatible messages recently. (mysql? why relevant?) +12. From the music21 main folder (not subfolder) run "uvx hatch build". - This builds the dist/music21-9.3.0.tar.gz and dist/music21-9.3.0-py3-none-any.whl + This builds the dist/music21-11.1.0.tar.gz and dist/music21-11.1.0-py3-none-any.whl files. That used to be what *this* script did, but now hatch does it better! -13. Run this (dist.py) file: it builds the no-corpus version of music21. - (need Python 3.12 or higher) +13. Run this (dist.py) file with `uv run dist/dist.py`: it builds the no-corpus version of music21. DO NOT RUN THIS ON A PC or the Mac .tar.gz might have an incorrect permission if you do. 14. PR and Commit to GitHub at this point w/ commit comment of the new version, @@ -92,13 +89,11 @@ Finish this before doing the next step, even though it looks like it could be done in parallel. -19a. Upload the tar.gz file to PyPI with "twine upload music21-9.3.0.tar.gz" [*] +19a. Upload the tar.gz file to PyPI with "uvx twine upload music21-11.1.0.tar.gz" [*] 19b. Do the same for the whl file (but not for the no-corpus file) [*] - [*] Requires twine to be installed and up-to-date (pip install --upgrade twine) - - You will need a file called ~/.pypirc with + [*] You will need a file called ~/.pypirc with [distutils] index-servers = From 70135c525fd79a283ca6c80cabc6a64d3491ecd9 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 22 Oct 2026 11:20:57 -1000 Subject: [PATCH 179/312] Developer docs use uv; no-corpus build works on any OS Developer docs and missing-tool messages point to uv sync / uv run instead of pip. dist.py built the no-corpus tar.gz by unpacking to disk and shelling out to tar, so permissions came from the local filesystem (wrong on Windows); it now copies tar members straight from the sdist. AI-assisted (Claude) --- dist/dist.py | 67 ++++++++----------- documentation/docbuild/make.py | 2 +- documentation/nbvalNotebook.py | 4 +- .../buildingDocumentation.rst | 53 +++++---------- .../source/developerReference/documenting.rst | 12 +--- music21/test/commonTest.py | 4 +- 6 files changed, 52 insertions(+), 90 deletions(-) diff --git a/dist/dist.py b/dist/dist.py index 69ef713543..19141b9094 100644 --- a/dist/dist.py +++ b/dist/dist.py @@ -68,7 +68,6 @@ files. That used to be what *this* script did, but now hatch does it better! 13. Run this (dist.py) file with `uv run dist/dist.py`: it builds the no-corpus version of music21. - DO NOT RUN THIS ON A PC or the Mac .tar.gz might have an incorrect permission if you do. 14. PR and Commit to GitHub at this point w/ commit comment of the new version, then don't change anything until the next step is done. Merge to main/master @@ -118,8 +117,6 @@ 23. Announce on the blog and to the list. ''' -import os -import shutil import tarfile from music21._version import __version__ as version @@ -131,44 +128,34 @@ def removeCorpus(): create a new music21-noCorpus version. Return the completed file path of the newly created edition. - - NOTE: this function works only with Posix systems. ''' - fp = getRootFilePath() / 'dist' / ('music21-' + version + '.tar.gz') - fpDir, fn = os.path.split(str(fp)) - - # this has .tar.gz extension; this is the final completed package - fnDst = fn.replace('music21', 'music21-noCorpus') - fpDst = os.path.join(fpDir, fnDst) - # remove file extensions - fnDstDir = fnDst.replace('.tar.gz', '') - fpDstDir = os.path.join(fpDir, fnDstDir) - - file = tarfile.open(fp) - file.extractall(fpDir, filter='data') # note -- this requires 3.12+ but that's okay - file.close() - - os.rename(fpDstDir.replace('-noCorpus', ''), fpDstDir) - - # remove files, updates manifest - for fn in getCorpusContentDirs(): - fp = os.path.join(fpDstDir, 'music21', 'corpus', fn) - shutil.rmtree(fp) - - fp = os.path.join(fpDstDir, 'music21', 'corpus', '_metadataCache') - shutil.rmtree(fp) - - # compress dst dir to dst file path name - # need the -C flag to set relative dir - # just name of dir - cmd = f'tar -C {fpDir} -czf {fpDst} {fnDstDir}/' - os.system(cmd) - - # # remove directory that was compressed - if os.path.exists(fpDstDir): - shutil.rmtree(fpDstDir) - - return fpDst # full path with extension + distDir = getRootFilePath() / 'dist' + srcName = 'music21-' + version + dstName = 'music21-noCorpus-' + version + fpDst = distDir / (dstName + '.tar.gz') + corpusPrefixes = tuple( + f'{srcName}/music21/corpus/{d}/' + for d in [*getCorpusContentDirs(), '_metadataCache'] + ) + + # iterating over tar "members" rather than files/filenames in order to preserve + # the permissions etc. that the original hatch build set + with (tarfile.open(distDir / (srcName + '.tar.gz')) as src, + tarfile.open(fpDst, 'w:gz') as dst): + for member in src: + if member.name.startswith(corpusPrefixes): + continue + fileObj = src.extractfile(member) if member.isfile() else None + member.name = dstName + member.name[len(srcName):] + if 'path' in member.pax_headers: + # long paths (over 100 characters) and non-ASCII names store a short name + # in the tar and the longer name in this "path" header -- but it will be + # inaccurate in the new tar (because of the added "noCorpus-" in the path). + # Deleting this header will make the dst tar file regenerate it. + del member.pax_headers['path'] + dst.addfile(member, fileObj) + + return fpDst # ------------------------------------------------------------------------------ diff --git a/documentation/docbuild/make.py b/documentation/docbuild/make.py index 0a34e9241d..55e0fa4939 100644 --- a/documentation/docbuild/make.py +++ b/documentation/docbuild/make.py @@ -94,7 +94,7 @@ def runSphinx(self): import sphinx except ImportError: message = 'Sphinx is required to build documentation; ' - message += 'run pip install Sphinx' + message += 'run uv sync' raise ImportError(message) target = self.command diff --git a/documentation/nbvalNotebook.py b/documentation/nbvalNotebook.py index 26aee567bf..64d4e52d32 100644 --- a/documentation/nbvalNotebook.py +++ b/documentation/nbvalNotebook.py @@ -17,7 +17,7 @@ except ImportError: # pragma: no cover # fail here to get a better message than file-not-found below. raise ImportError( - 'Please install pytest -- in the music21 directory run "pip install ".[dev]"' + 'Please install pytest -- in the music21 directory run "uv sync"' ) # noinspection PyPackageRequirements @@ -25,7 +25,7 @@ import nbval except ImportError: # pragma: no cover raise ImportError( - 'Please install nbval -- in the music21 directory run "pip install ".[dev]"' + 'Please install nbval -- in the music21 directory run "uv sync"' ) from music21 import environment diff --git a/documentation/source/developerReference/buildingDocumentation.rst b/documentation/source/developerReference/buildingDocumentation.rst index f66c31dd9a..a0f482554a 100644 --- a/documentation/source/developerReference/buildingDocumentation.rst +++ b/documentation/source/developerReference/buildingDocumentation.rst @@ -4,55 +4,36 @@ Building Documentation ================================== -Creating or updating the music21 documentation requires a -few additional programs to be installed, -most importantly one called Sphinx. Sphinx is a documentation generator that uses -reStructuredText (rst) as its markup language and outputs HTML documents. +Creating or updating the music21 documentation requires a +few additional programs to be installed, +most importantly one called Sphinx. Sphinx is a documentation generator that uses +reStructuredText (rst) as its markup language and outputs HTML documents. -In addition, you will need to install `music21` from the .tar.gz file on -Github (https://github.com/cuthbertLab/music21/releases) or from the -git repository (git://github.org/cuthbertLab/music21.git). +In addition, you will need a copy of the git repository +(https://github.com/cuthbertLab/music21). -**1) Install pip (if needed)** +**1) Install uv and run uv sync** -In order to get additional modules, you'll need a way of getting them more easily. -Recent versions of Python include `pip` or `pip3`; if you don't have it, you -should download it. -Just go to https://pip.pypa.io/en/stable and they'll help you out. +Get uv at https://docs.astral.sh/uv/ and then, in the music21 directory, run `uv sync`. +This installs Sphinx, Jupyter, and nbconvert, which turns the Jupyter notebooks +used in the tutorials and User's Guide into .rst files. **2) Install pandoc** -Pandoc is a universal documentation converter that we use. +Pandoc is a universal documentation converter that we use (nbconvert needs it). You can get pandoc at: https://pandoc.org/installing.html Note that pandoc 1.14-1.15.0 do not work. 1.15.1 and newer are recommended. -**3) Install nbconvert** +**3) Run documentation/make.py** -Some of the tutorials and users guides for `music21` use the Jupyter/IPython -notebook. To convert these files to the Python documentation format .rst, you'll -need nbconvert (and pandoc). Run `sudo pip3 install nbconvert`. -On Windows, omit the `sudo`. You will probably be -asked for your password to install. - -**4) Downloading and Installing Sphinx** - -Sphinx is the document builder for Python. Run on Mac "sudo pip3 install sphinx" -or on Windows "pip3 install sphinx". - -**5) Run documentation/make.py** - -Find the documentation folder within the base folder of `music21`. Change directory to that -directory and then run `python3 make.py`, which will automatically build all the music modules in +From the music21 directory run `uv run documentation/make.py`, which will automatically +build all the music modules in music21 into the "autogenerated" folder and from there into the "build/html" folder. It should also open your web browser automatically. -If you can't find your `music21` installation run these commands: `from music21 import *` -and then `common.getSourceFilePath()`. The documentation directory is the directory above -this named `documentation`. - -The first time you build the documentation, it'll take a long time. Subsequent +The first time you build the documentation, it'll take a long time. Subsequent builds, however, only build the parts that have changed. If you need to build from -scratch, run `python3 make.py clean` and you will have a clean directory to -build again. \ No newline at end of file +scratch, run `uv run documentation/make.py clean` and you will have a clean directory to +build again. diff --git a/documentation/source/developerReference/documenting.rst b/documentation/source/developerReference/documenting.rst index 6f620fbd7f..20b096f2a2 100644 --- a/documentation/source/developerReference/documenting.rst +++ b/documentation/source/developerReference/documenting.rst @@ -51,13 +51,9 @@ in this form -- there's a test that ensures that code coverage increases with ea we'll help you learn the ropes. This doc explains some of the main features (and potential Gotchas!) of our automatic documentation system. -Install Jupyter with:: +`uv sync` in the music21 directory installs Jupyter. Run it from the music21 directory with:: - pip3 install jupyter - -And then run it by changing directory to the one you want to edit and run:: - - jupyter notebook + uv run jupyter notebook When editing examples with code be sure, at least the first time, to run each code excerpt individually and pay attention to changes in output and unexpected errors. @@ -79,9 +75,7 @@ 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:: - - pip3 install sphinx +If you're going to edit docs you'll need Sphinx, which `uv sync` installs. Sphinx uses special characters to identify formatting styles in documentation. Helpful tips on Sphinx formatting may be found here: `Sphinx Cheat Sheet `_ diff --git a/music21/test/commonTest.py b/music21/test/commonTest.py index 64e06d18ca..dc7d1d56a0 100644 --- a/music21/test/commonTest.py +++ b/music21/test/commonTest.py @@ -68,12 +68,12 @@ def testImports(): try: import scipy # type: ignore # noqa: F401 except ImportError as e: - raise ImportError('pip install scipy : needed for running test suites') from e + raise ImportError('scipy is needed for test suites; run uv sync') from e try: from Levenshtein import StringMatcher # type: ignore # noqa: F401 except ImportError as e: - raise ImportError('pip install python-Levenshtein : needed for running test suites') from e + raise ImportError('python-Levenshtein is needed for test suites; run uv sync') from e from music21.lily.translate import LilypondConverter, LilyTranslateException try: From ec633115dd9c7563174575c70acc076ade4cf727 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Thu, 22 Oct 2026 11:55:57 -1000 Subject: [PATCH 180/312] Mac install page for beginners; MuseScore 4 default paths installMac.rst now walks beginners through uv, a folder of their own, MuseScore for notation in the browser, and Jupyter. Drops the 2011-era Configuration Assistant screenshots and all now-unused install images. Mac and Windows defaults for musicxmlPath and musescoreDirectPNGPath point to MuseScore 4; Linux stays on mscore3, which distros still ship. Adds a repo memory on why MuseScore renders for everyone. AI-assisted (Claude) --- .agents/memory/MEMORY.md | 1 + .agents/memory/musescore-png-path.md | 34 ++++ .../macScreenConfigAssistantFinaleInstall.jpg | Bin 41220 -> 0 bytes .../macScreenConfigAssistantQuestions.png | Bin 180396 -> 0 bytes .../images/macScreenConfigAssistantReader.png | Bin 205096 -> 0 bytes .../images/macScreenConfigAssistantSelect.png | Bin 191988 -> 0 bytes .../images/macScreenConfigAssistantStart.png | Bin 186647 -> 0 bytes .../images/macScreenMusic21Folder.png | Bin 208132 -> 0 bytes .../images/macScreenPythonVersion.png | Bin 133375 -> 0 bytes .../installing/images/macScreenShow.png | Bin 200859 -> 0 bytes .../installing/images/macScreenShowText.png | Bin 155607 -> 0 bytes .../images/macScreenSitePackages.png | Bin 155692 -> 0 bytes .../source/installing/installMac.rst | 192 +++++++----------- music21/environment.py | 20 +- 14 files changed, 118 insertions(+), 129 deletions(-) create mode 100644 .agents/memory/musescore-png-path.md delete mode 100644 documentation/source/installing/images/macScreenConfigAssistantFinaleInstall.jpg delete mode 100644 documentation/source/installing/images/macScreenConfigAssistantQuestions.png delete mode 100644 documentation/source/installing/images/macScreenConfigAssistantReader.png delete mode 100644 documentation/source/installing/images/macScreenConfigAssistantSelect.png delete mode 100644 documentation/source/installing/images/macScreenConfigAssistantStart.png delete mode 100644 documentation/source/installing/images/macScreenMusic21Folder.png delete mode 100644 documentation/source/installing/images/macScreenPythonVersion.png delete mode 100644 documentation/source/installing/images/macScreenShow.png delete mode 100644 documentation/source/installing/images/macScreenShowText.png delete mode 100644 documentation/source/installing/images/macScreenSitePackages.png diff --git a/.agents/memory/MEMORY.md b/.agents/memory/MEMORY.md index f589a813c7..659aceca28 100644 --- a/.agents/memory/MEMORY.md +++ b/.agents/memory/MEMORY.md @@ -11,3 +11,4 @@ Project-shared agent memory for music21. One file per fact; see frontmatter for - [Issue #1349 romanNumeral direction](issue-1349-romanNumeral-direction.md) — raised-^6/^7 fix landed; CAUTIONARY default kept; items 2+4 open with pablopupo/Malcolm - [Meter immutability re-apply](meter-immutability-reapply.md) — meter-speed is too stale to merge; re-apply FrozenDuration + immutable MeterTerminal + tuple NumDenom fresh on master - [Helpers public by default](helpers-public-by-default.md) — reusable theory helpers go public + documented (search first); inline one-caller helpers that would cost runtime +- [MuseScore PNG path](musescore-png-path.md) — MuseScore renders notebook/.png/.pdf notation for everyone; users still pick their own app for show() diff --git a/.agents/memory/musescore-png-path.md b/.agents/memory/musescore-png-path.md new file mode 100644 index 0000000000..519b38be32 --- /dev/null +++ b/.agents/memory/musescore-png-path.md @@ -0,0 +1,34 @@ +--- +name: musescore-png-path +description: MuseScore is needed even by Dorico/Sibelius users -- musescoreDirectPNGPath renders notation in notebooks and .png/.pdf; musicxmlPath stays the user's own app +metadata: + type: project +--- + +Two separate settings: + +- `musicxmlPath`: the app `.show()` opens outside a notebook. Docs encourage users to + pick their regular notation program (Dorico, Sibelius, MuseScore, Finale). +- `musescoreDirectPNGPath`: the MuseScore program music21 runs in the background + (`converter/museScore.py: runThroughMuseScore`) to turn MusicXML into images. Used by + `.show()` in Jupyter (default format `ipython.musicxml.png`) and by + `write('musicxml.png')` / `write('musicxml.pdf')`. A value starting with `/skip` turns + rendering off (documentation tests show a 1-pixel image instead). + +**Why MuseScore is special:** it is the only notation program music21 can run from the +command line to render images, so anyone who wants notation in a notebook needs it +installed, even if they never open it. + +**State as of 2026-10-22:** the Configuration Assistant sets `musescoreDirectPNGPath` +only when the user picks MuseScore as the MusicXML reader (new in v11, #1980; never in +10.x). Picking Dorico leaves the per-platform default in `environment.py`: MuseScore 4's +standard install location on Mac and Windows, but still `/usr/bin/mscore3` on Linux, +where MuseScore 4 has no standard location. Branch `musescore-png-detect` fixes this: +the assistant always sets the path when it finds MuseScore, and rendering looks for +MuseScore when the setting is missing. Update this note when it merges. + +Myke renders with MuseScore 3 because it is much faster than 4 -- an advanced tip not +yet in the docs. + +**How to apply:** install docs tell users to install MuseScore for notation in the +browser, then choose their favorite program in the assistant. diff --git a/documentation/source/installing/images/macScreenConfigAssistantFinaleInstall.jpg b/documentation/source/installing/images/macScreenConfigAssistantFinaleInstall.jpg deleted file mode 100644 index c38a3f4cbb4c098f88b3289b8548fc501420fcd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41220 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

sZ>p)s1SQmS~jR>MMN{ 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Ŧ%vr!=kzwU4*v|7m%U$fm#Qgq+DsinxhBZ zYYzu`T(0SoWm1z-&RC_0`UA3Vxh)`g(_cjP=-B(4?q>B7^NN_qPw?44sSV{eMQ}5Q zci%|)d{p|@I&oIj70&X3%0-nB%3oUKXkqg<_h;^Y#+5lR)VeFc3I4a0qk1Nk$&Ui= zm+~Q|O}cix+z{CS#_>f6kyFrIIVM_%h94!>s(-lJJB|n^s ztv=~~*xnD_QBq-mOAq{QD45_U9c``A`Upx23^rgoa5L$D#AQJIpgd`fdW}Hp0T;{A zp@`I9mfiV#`^#?zWn?C%hVQXNg{d6t46yTZyNVN;JWa>H;x)>aFs7DxFNvnfsmC^= z@P!)B5CEa0lq80N(Tbe5REIERt?V)D&9?Mla_&@S02^E0U<~~ZTh?V|y>LgA*x8+k z_^B4`etIfC(>sC{hIwwKHU`E*J(o)F`8YR8m&IwG;|=zsO*Nmh9S1|n((t9l4yphm zyl-DPml}E~)ZzUY)1C?Kzk>uiS!OF>?Sy0wNs4mj9g)mzq#Z{Bsod=43L9M+ zW|p+g01mP^`gvdRB;W5LlQ#1_x#?r37jria{}!m!X&`=2aLbPF>mS2}&fsQ*4}VJhp7Xt4B>ayV8KmjC z7?b@0TB1%eJ##`zc_gy=1u!CceooT-Ar<-RL)9wzB>{73<^DT^G3RBA%ioQMl)mCx zdw)Or75~gHxyi2M9l? z+FV)8UpmyjRLdm0S=Os589mz^j7#ZX^ru5&-t&`Pn#hUR<|RcHvtd~4qd)2G6|?)F zj5Abm+yoWs34rFiU71lf6%2MgZmqA=i7THY2n-E%DAb zNQkcRg@q&catYHCLKd!Fqp|Q1vC9m!7Tab^aP5$1;vIBaCAof!3&Bh5avm7NhI)E@ z7_NkS#8Mz^E=HSFMC%>TR60+~<;BFziZ3qJz!!9?60;V$(ECfZFglOAJWkNgXB=d4 zfdzbKk;4`LA>;HX@3$B&CT^q6*o?pq{6g*0-`^Uf$8mqX14#n)0a@1F!BWE`frKLh zdZpRh3_K%AHO#Mg{o%+#@~9q@l#SRo`a;fAUm@hC5#hq7 zDF4S!xO51{-b$UJm67en*2-Cigk5*Lk5Eg72A_B`p1dK;Nk_cC$uwgnub_HNB&l`7 zIF5Ic-lRp;2HA$OLu4TbmB%lo2w0t@7ilf(8GmObGicWG6O zA*3YhSUaz{J!r1Z!Hdmus3KZ@;|iBmac*Q%6i);&hVDE^2^J`McImyb=64dO8$U0N zuXRzp_J6Zxy4gI1Z?9lxbmz$74x#?#BhHa@@P*6oX^o_k+jlWiV}%X?y3Ykqyc)X+ z3C4PLt&A<+3fd@DY~M)%+%&`Cc2W2Kc`IVjU&=ZX(-pdn6}mtHGdm}>~ye$>4dD9TCmPYd*B)EY9zZ;iC@IuaD|W)4_6ZeI%$HfU$^ zr@E&;&P3&K5|2dlK6_kgiOjF4$cd8_UZ{@*Exd8Ysh(WTiZaa16h+hhyZPE5cP9LS zUo!$9%W3|W=@xUhkEoD&C-?IUA-=h|A zD)&KVhCm+x*Eg=5I{Hb_(204y!xDu>#z~;{5E_W@CK|=BjMw)ejoEnJlG<28Lty&2k=}T#7&eywzJg(KCDm!I?dAL0~Scl8h zD14eMxot5R=_wm0(0=j{aujep5pBG5$ra!X!#H1V$p+ymp&lJ(w{L(1lU9Y4>9yD1 zOevdO5u8QWj+yT}i{GRS`VK(uXr2wNF!m~BmT_}p&vSVp?}E#ATyK_BW+xMVD*JI;pz&OP24e7jJ&Z7Z41M} zTrM%rPh^8p>yreVc*@>KgpQen)_a~wbhp}w`tZu_R~s37Yo|5wEptF5-=S6+mzxV7 z2?h$4<2d(h@!5D-ky&o2h>ur8$HoqyR?A0}jBP>A?X>%^cON9m$I$Jek!En5r-ghW zO=M0cjUQ#Z*Vl~{e%3Fk#$a(X_$7YVyEaoeY}ay6TD@ivc8CAu6bF7L*G<#}&En=c z+h}rba_%l70)v$@;Sq%O0t*E0e*hCx7vyYmKdSYRV7Xj-q~b~r{h1*a{5S@Pp^z{? z2EZeB=~RGr^LO?PM@)dyiT^A)Zse{_-PXQ)$oxJelF>}c^vN-JR^7CYAp{(iQ|B3Zg@WEX)^lnd-1p@iuarj(p@qx zAsk^VV-pde-6GfjcmepMsBKNk3A}}|Iq&~aM9*dx(;fB33fr$ts)sBEZPKi*2nB~Y zkQd;iT9bjVkKw_E*;8o#FIKyRl-8e#MTMQY{fNM{@pa*sSS{i{8-NXkd;euYr3pd6 zvlVvJ1^~rEuF9w-k!DW=V=z%r7^adV>_#nT^2NDV8jZ#KPY40*Cx6=yW^=im54?Q- z3y?|AzAZnBywT5y+zh59N}KsS6$#B$$D+^~-CNBi3N!IY0{*r<6vC91r=KfWNW2=%gu{e z<;9B@L9$D2n8F@pU)rrA1ak0g84$07zL<CA-5LB59(p3?h#%$*)uoAlipL!bn!S#Qcr~q!8PnHNG_K209-M)ned_ub~}H? z6)qy*D6?{i5&ui5)YgTqiUBVEUF6=FjkK2XO~G^K0)`@F9@I@opH=_R&tQM4XZMDb zJ=ae(6G-3!iDzs)*2^@Iy7rddCViVB|xgVK5O=3(ZAFEVqJPZj%?OM zMIJ%u`A$FoEM#vFE(`hu58iJp$gdrTU1L7;!)6W*8q?4_jr}Ifh*)26Y;0cvJ^0^z zBLBj%vuEz<4Sz5QNJR=D7Khu2VfNvPwQd3Iy8bD~FO0IoALZa*E1YoQaNJtide$&< zm5lkao*k25>t?!}EsParUp?;-YWle`QO@vbciyK2c=xe z^Ndf3za3C6ZH(~=Cg>*9g|k=Hx?~GM1BR)>C2}QCmRt|Z*G}H(ZH$TK$lXzNgw6f$ z{ba4TLWh5iu>iKHQ!3!9V0a-S;kKeNn?>klDXj<&k{TA`4J|nW*nAyEjUxbttZvV4 z8?16&{)?*%$c8~_S3B>ypcNkUz?xAbdWl497)j~PeHJrw#3$NF!$fjEEk*>1)bZwgBJ@~dD^z+-_;jlc*7HM6c} zhR-{iwv5s>wR>M+H^J829**U35G$YMjV{4f`cV2Ls^&3(VQ5FX2%I0V3$PUuvbvcQ z38sLKzAYctiEpP6!AVfwR7*`oncGP!cMBvNX#B8J(rV*sPEpmSW~`7?ZIF{<-A_sL zJDf$8eX93RAs8xD94n>x^j8N_G@hBQ#?K-UiJ-`Q?rFwa5W~;@2)loOka|d3kPzW< zjL}))HBcmti~T zX$z33VE5-jm2;zN+TxW2dH4NIGbDPfkEEYqfW&kQF94^kxK74y;*jz}jBKYb^L_NP~MD0eWE}0)u|7CJ*cJpk`O!Ep4P)p`r8;X)O*>iPh?YF}*;;>a6|! zjZ;R#^7&$C4UKGWF##2)WhB zSgC(I1*t+Mx;)9YvowxXK@}t0HuqOxO!e6f`6=J^NPn+-I%jLCYu~bAiA6TqIh7Qf zJgU>DmF2nm9(!KXC5#Eare82%zj)HP6JbTzY&Y-^6Cx@lfd} z6muA`ca;l62)!A6S1B@)D8$U2qvo(^hLG@C4qV@x6 z#WXq*aC4k=_UEH8%;Z+y5KF6n{lch&*)sfbSjJ$BQ(7Zm(R7wxI6M5#~HYtmv`o|<T4Q3dl3L)=@Ie$otK$W?Yb}QQG8DD-4xTr4?j3tr6$3ogqyntu zY0S0eLGL9@^N*1j(kW;YRl%t&61YjYMofU(T8qY+2)@vq@OZ(fWms+xi zB88w4O*a}G^I_!G(GxAo+g&u3@Kku ze>M3Z@dS^?*(2;WgxVo#BiKe2O<>^@R}c@(=3olawgmD>vW_D!)n8|b>(%>bb^mQ* z$&6fOOwAVjJLDdhoUPz3KFp-g+y1$6utYPlr*lgy?`;G-l@W<5pJ^9& z14;j2xPko58}fDWIOEJX6kSz1>`{#Er_+Z`eZiyV#WDbeVMMjQ)>?~W+Oe8meeZv& z{eSPAR{@=<@cgj+9EoC+|E(6{{uZ{p6b?bglf$hb9>EQ%+OKbQCJ7rfjsiut#r6rn zoCQwne{>nAmI_%|*IW74kjD=DCna+F#v@{ejwo!&i)tnw$KGI*61J!%`%_w+KRUKI z5ZGaSY=;g@jk7t}o|d`fTENze6iY)4K|=eTT`$RhIXo)t@_oPrX5Wa)xy|na$`=O` zKX|<`bkC$+YM@=krskfTd-&gVD-g6k>GkH_q>^eciB7*9Z~keG_S+;r#~dB9L5O_? z=y0=hZ-ZOM@#?a~wS!ea0&ZM-_&*(SO1!YP3pbw%)14gAK6b#S{6k=K(pe-s2ao+j zIaO|pumCo$MYx_m#SfoJKhtBHy14GAl=P49i>!zhuFh6W1aehpJxD9i>YP^IBdv|} zFZ=Yb60^+I}B;wI9P%mfCh-g=K6l38phG)l`Hy0AB{wDIPlUUhYY!D zM3xIpY})c+Ot)qlaF-IS4t#iUf1Q5SAost#tZ?tG+znqORR}q3PJl|B3%q=5daIQH z|4VJJY8u{TKw9^>Msz<Q-s?Zz z!4mOD%-=l?Js>UMy|jQrk*hfDCH(FqRQKtE_sDiqu5~tF z#d;5H^-8W{9w(2>%^DC4cvL3TuaUwBEojBEh-I##0?)+*6IJ!2DQu>bq4+lCmSyl0 zSa4jKq}()C&C;vVwlwSyIWq8)1^NMt%;IL~U|yijHcHDXx<%PTADOW~SMIXjUB|iK z`J~tG$U_tTv3ZM{#5r3Vt@pKzs1{+@&ZK)k=!^SUU&NAqz;>Veh#8Q%=gkR{3zhP} zWt%EvGkmCeKBVmCkEZm@^>tl+8v?wYxdgn@Hl3YmFR>)7stj+Q^a>nzKa{yY1bnzY zi3VNIJ}ew!_q4D+pM7+vXV{%!7caCxetCF^|%XV5o9?yJYoEYgTaONQ8+3=uwC6sbcX?VUw=MDxngDbbez(Cc1$rh- zIF>*39K=DEc)LrwKj-83^aPxz<*17Kw3spb%Yg3E9~RObw=_iG`vFT;?;F|I=rxtHd!?cb{uiX`Quz6(a7h9p`D;@)g!}yYfRi|Kf&Ghi?SFT()qZ-az!` z&bTcCA@yFtl2=wW6So25jO~T#<`XMH)lvh7!E*5J3~8k7fDC9i2XGW=s2ddF`64Sx zHy`O##1`H0YxP6Xj9?m?W8NzEE|I|$>^S*vsz^yecixYR4sADM?fX@imC4@3YuPpH zhB8$kHROPUV7<4|+pa+42;dWG)BTMtIFHF=IRkSXY}-rWcglFiNkPi*cx)?})|}Q8 zA$HTe_78n>QQp+DqFUi$Y%tCW6>_d}?XB$PrcZ6f6f0RA^P&doRBojlSg0+TRks+d~n8 z_D_1Qi=WS5{z(;GXLka7M;;5;z=z#-mU6?VC{dck*8u_Ogy#ajBXsh)Z$4K)tunv@ zIt%Ps>ru`kCtO_HeEcf_gmJp$YAqsL)ilg>=#PIe``m z4dt8Vf@1fhtzjorpHVnoHuSskleUe7R%*5ctJ`WIZsKoSG?bSV$Q~=0@xmi5*^fq*Y2<;yVA$e97A-S-REps;Z%zTc+Jliey?#}IuEsT{ z5oyVJWrrt(fh#Z7k6Vq3fNFo6WD@3SsX`SWeBfC&O3F(DZFiMp{mD^TYQ<^#Gvi^h z`9&`69opu8e81uTQ1P%d+j02gJe3K2+aao#3w<08cK4-O5cEIL3*Ovl`f*eaYIN~L zJOTu~N|#-{9UCu-M_iQSzpoAH`OggLdFQb?zFD$#ywYyhMs*&6n|5(dwaD}UH&_#3 zk>-MGSgW^LQO^Qy(dS>-%h6xP!KH!}24L^Po~5%gg;Yb8j+ToFi;Kw$)`~^sn$m$~ zN(1D|%nF5?_U1wFyB$&I0`4>theHA#4=?DIdd?`Zajuz>(?ZXJAn#U7&U1>)(UCm4 z!&=R%hQ8-H5Mv|d!r8D zmclsQxUT5!?Hnk3OSy1M>GR!w&0P-)rSz-2cyAOfn-+eaau?*yJ*ug^6>&4&IOy}N z@_+bqblO1qXig3d^j+Q=imq_fwgO&sHCaBSg1ReCt$?@8k3wxz?>F@BTeR^;=8^W0 z?k(TXLFOeNAN$y^Y4VEB{`>D%7@bIe63~76LKU~tsXw=3kC^Ev;%wglYmS@*L*z)` z3~rLZSbJXfwF6l0nUGAU?e~#ib;eS7?KL9ACc)+U(TLp)g;k42;q2H7{YG}GOv>vh z=?+~VUwcD6Q}*P4xmU8F5W+TnisNnb4JrL;vuRKhii76r_)N&}{)s`3M&Sl5dA>T*XzD2KHO zzmVfG_)i=9Y3cmGQ6)hjk$1VKT$4%r>hbNO-C@-NHE;PQ8H?$V*CpZD?|i)%n=+@l zoV9&77&_)H;g@>qphU2us%>0nR(xe?bTu7WIbPjLV`jQkdjN){a^)I0icDwVpK2RJ z=6nXpu@!E^DE+=cH`5&Sl{*IxAQN1zn)_unRSgtP#YVTcHPr<Z&2{-vT*U#>(-w)v98B6<1%0JNrK(@>7`qW=Usv8r<&@B95L?;aNPUCyzR1Y zu6|A0rC~t1(MzUs9}R<~wVt!YLqW#>V{(OD}!dyX|B(}36ST~~JTFW#I&aT-xj#Dfe!q=vA8#>IW%GUguHRLg19?tZxK$P?q3|OE zP%&EyR{wjZ%;IbNvvg4udBLOhgi!{=$G+u7g{d;1zqxOw{*TU%rEPcb)`kMC|Cvg8 zEnz(w;yf@uX%o#jpGj8LWyD>q5bykq-KZoi-x{OMy3R0>H0e*)D*9&#>1y1di-nSm zgtT36$}5mF-6NU9=>+v2{XIYm2qwCMbS2Q#)aLA680A zgofS{BV=de1pWYnxf=x9Xj|aGkT}3B3}I{g6W%u_1rGBB&U)d!`;MdaFsl|vy2gAlA2Zh)GPa@)k@Q3a zm#@$lhAT^%s#n{M`D=#1mw9ou1nzM+z=<@n|6r|Xpm^H%S;Nj%1-fs-pWgKNe-pk= zpROu+Kf`n24L+nA8}+@3t6 ztCX$sdCIp_fs;C`1;I^c^KAcTwuReiZyoraDNT3OX+H~KQ`zf?tT3h3_I;Z z|L6)C$_-G;hU8z?q`i$qN#Y6G9{k%Y67{_w2XBK#jC&x*#^nufr1lAgljC>{3?u(q z9O+r~<(_%9(dTc*RqN^e(Guw=(PQz43SS5Qnt*qD#|k!~vnh9EN1XN7bRY8BD|q9V zET0){3Y{a-gV^?YWziN2_tQT2Puhg{PWDl&`9?3DM;m$7u7m-#0cT}Bnh#!MqXD=VbUvpX*b5`KH??RXG9hU&XgG!<$z7}s$3*Rv;IMD}E&gX{}<>uFp>PuXkM z7mUj$SaJcZr~qewEqS-HR!IMktg$W4oVfBCwumGBr+S26R1=;dj;YF(|F&-wg=^Ca z(S)n#n--okKYe=`FFL`f!e|fO6;xNdhRA!GE@IH7*~hVn@qDB%sZ}~XmRu(3y^+i2 z%8D$k6y|#K2F7YbEV^@qbL1>Wg-5`nghLPpmeU}(42}`uDL`YRNkT4zm5zY|A>;6A zeW9pY({_0&BV^`A7EUmr!sW7Qi>IGlV81oZu$P8?U-P=j@*khJshLgg9KNx&_C8SW z;^17ut3pWGa9Mygrpjn^ijES$htS9sAq^pkiT~E9I||He>-lfOiL+gDrCfOiwR-qh zQ70NiD1zI#n?2gNAC`D>jyKUZvd_k*>de}bvuDn29iBOX`?}A1^)LiZcKl9cTxJM% z??a-CYo!5W@Ji)vG1a9KN*z|&20%SR1CMY1(?Q7WrglXTaR`9&>0H45Ubp*pVmHqN+gybdY;n0=*(n0yi~=%vyoeE z4842l^D^kVBD}ypuX4ZI2&mt@u5<<8X?NDJdaakUH^Mqk$4Rn)r>3>Fo@OIW_X?SX;jM;ziuZ!) zYUH-_=eT6B#pSb?@bPbQ_XC2%LH{{F{}X6UwN>}?x(`|aq*vn5b{^~5=j}$|1pq8K zy{;*HKY9b7Ml%WC8B8UZG8-Ol4hJfP(W9@|B1YC>Yzka(wR0Mo+105Y$R{4@+A*~5 zW&8*h50U15h8zD}`&GxfwPFM)LOqgoVH>PA49A6z6Lik!xQiZGS0KK}r>k9@D(!=i zT#pV*BS^Y_>^7XIpm?gd(B@}PYx8Tz1e7TT_YGlVfOL8z{M_VA2JXRqk_=N2R@T>=a&vDLfpH$$$rBZPpiyrXd0dhtGuW1O=&qiHudjyRl(K>yT7O{ zV+z8AL1)Pu?lVau{so@u7$R5hi<8Bl_Siotd3^TPKCEv8O{Mp9ZrXT{hpf||{&lB>O+)*q zs==bOo6BBYaXA#<{{pY-0=jl|MSpFyREx9)xD1r{o^OmdKMPan$((l5{|7 zYa&1dJ0Ea&REKK+LMIw|m=nNSf@{pJF_CQc~ddV&C!GM2kK z0+;g-*BWlGPq{6x;uC{~!lPt!2Vjt;FPiyZ}tO&BP%GvVMoG zbKzt!Q@b806&@~G}Ux6y!sEzknu^sylG*%f7e-G(n;knGim4&DgAVu%!Qn9H8 zgC98VD`}swOo7r*LyhM{MWAH_=T|246j(!P24gjcO}PzU_FtiPG$5@MxL>=I3|zR5 zR4hHWXWe>W3A!NmBM(;O-DCxVyVsaQEP{aCf(b!@?G;dC%VaobLYm`#t}RIj%8lR^87ts$g7h z*F5syNEr_My z#ylL=r4PzQ5WgD?O14ccB6AMe$rF+55((tKqQ^(3>!w9Wg~W^m237g+9U-RQ3K19 zC9%BvT9*)D=%U;fsP`Ex^j61v1P>^CF#uWLmjZs^xCLHag}7&neD(AI?}P#>_#9#% zTWSrKoAjL5QRYcr`}CKawlU2PDpub@8qRcbdD{HI6Ncp-gc~iS3}j77`v0gAH5MN& z439MTiq9F)x$)8Ql^)+4s2IV<1T%SZpx&mT@Oy`>v>dm>d5jDh{%&TCVI``=|Mtc8 zIUN_zy~`&H_9o)%s)FDzpq-g$VXw#!S{XRcjhSFLHCarhBjWcUVt?=PXWj=~J&0OrXY) ztk06jFV#!e7YK%AI^bU2oJc4!76u93iGAPQKK?GCEEmY?+ltcC`%Jh6l~CvM%<+|~ zuQa=y4BxtjO2Qa|Q~kVS^d;Op6+48qdWHuj@GdhIH$6h1Pal*P7jSH<2NXAyty8Lv zsGVE|c?a;%J5vVDVqRgkTH86RbhM~RT!vRC6Hn!=Mxh|sh!nj8{&&EgaBIa@LxDEG zQiGd4i~05~p)SRa!P8ZrmBFUO)Pt?VuNUESxwWC60{%Edjd16`yIN-1Ta4vBoExhx z$t4I?eDV;_NWJ*niA6ap|sU-!IsO zI`gMFz-h_*1I&=Kp8IVT>L}^hok9ZgU9LKVhu`$;sf(+EwzJ+U1NFVv08lHL+ zJ2(+-!yn$;HpSL-oR)P6Ku6&$cu%!C3p~6iRRD2(tNdQD*5e=dmzJtqhh5IZoz2gD zucVlNGa7CYfvp$NTsu0nx{iSF=alDd#&tJ;X<>NE*APtfo+2m?FVp}DPV0J{KwkZF zn|dhxPpPLhuzDJozW3v86mRS8!;Dt}w(@?rTJu|-`!e?#u|j#9ulcEqGUM*^T|#$@ zt1rKDYldxC_3XW-$WP%W*g-&)CFjjMyk-OcHaDL)@I6R34WN8BjiFerOWZr#d>Gdb z2HK-RM(AZZH!Dxww+!7omxxZUY?r%;Y;k{_wEiQz=mDTsT*%$nrq8zUO(nUe$o||0 z;ER$6%Iwm9YMS?sHMQ4A6QN3~qwRU)j%InDJZ<~Yj%-y+O5DDKJ$xO87{33!6 zlI898Zcxb(R{BGaq(dIWAp#reFDKco=)Q;F=F0he)a1Dzryhpq`dzcN+np^0I*D$z z1E#YjfuEJux$%*UNZ#(_+aD=`dZyPi5LPCEE8<1wJ_rf^t)!P31UauBZ#{uPRka_6 z602y{ePGDampbVF}CK+ z4d{>K$_&cico~seEUkHrT=nT~=G|^>vVpax?kEhp@yy}ex?^N2)vv3*aE~OZ)L(N0 zYE6A*@@3TdQQy1Sr8g@WRu}9a%VO$xw(2$32B}=^+^)M1wCJ=quQc=n@||&G-yYD% zIw)TRnFPZZf5}Hie$r86_3W(u)M?gqtvtq`)+}q_6YM^VfAzAxxA$jcxdrTo;%k(B z*Nihi=YI&OROIgR1LC~bn$N2^OSi~g65tE+))o&|#+xnRH#aIRR<1_D7pJtX&Sv79 zev*m?gDb6jYayrM)iGupv!t3X4S^jTVr3=&dq|nHp>L+w$(ny`X1Z?EKuDLEIDwSI^9bpHatv!1^zQ{i4(@oY^kZ|uQ+#eYFsS3Qv zTxOtwPjKF31P&juTo0ObPg{q(iVQqv=1yUhyw>O6=I$R*AeIYMTY>@0nM_sAf^TWh ziOvuyqHz>aJ*(-K=VPxOsV#%Ei~E~)hufgD=>ZSYO!~N!#2zgrh^YnmZDot6!|xH= zD}!D_%dtjiwubSLbG`}3@SwZ*U$U=VKh)>M@Hx2Ws2-oLSOWFUoV1zYUqqL8&Bict_DrQs4J3Up9rLVlfAy-+61CF1Qs(Rfw_oCInPWbG_`8EZ?YP>EEw+u&wdA_AM5FM+_NK~DP zY!KY&edu;*e!PM7oJp$^0#V*AQp^6EJ~ci<9`!DpB5%N+(pW4hwErR%Y2a2Qhe zGnqxMb>=^cCr7Jc=J_=a==?mljO4~9ka$A!7vlJRTkOaW_sFz6+?SbfpMqXU&V%^s&1bU|iLfsc z_Pz%UEiVKiUq?D{?fi4e0-+h}mr4`dA60*2Jy;3%ypU=oI?0r}>ZeDOAIk{1ost#X zN2&>o8>~@a+T(aV{9vJlatl7{jG3vIa~(Z?K_sxA8urPnoh*rI?2ysX zhCq0=Uf1R8QSbyitG7Al=eIMBZj9F#KJU*Z;)VyfD}H{~bn^XCw*c~Q#E0BNE?JY; z^|hB;vOh<_Qc{`zMhT)T9fY9A%*~@%5M;hl*5DnD)zlF$@y*()5@S6F2>!F~6KS?|3D>ci3Ns%a9=X z!#{Q`OhNDVGxUIpeVJ_)CY5Wl?uT-oPDSD>!bF%P3uoQ7d`8G`ctYm%K6oL70ll8< z<@1#hy(|A64KG}1O9VmRwMj>kLNi4^6E@J!8%zn|Mr1>4cM<;GcZyG+tVkpuq#y*t zn%z$+W;uNazu&;99i6ejKJm9V%7Lskr?7;p)h#n z#RfDB2%qz}Kp>8y;4WYxV|2PxA{PVQ1c&#KY#_%e5e z5eT^k$SB;CJ|vt< z)S%%0$=TwGd5=RrO!@OmvAI{|$$S%IY_UE)GYSv1Eqe4UW6VJnik?MiGV7?SvS*IX zZcG3rUW`xzwSSrO7jCJ1*;9Kf#P=3WeAyDEWSj7x!`~a>-{|sQ7GDsRqaVX%s0z>1 zF;hYuNn|{ci=*`_I?XczJCoaIt4|#bWaVU^^6Qkwysx%J2oL=>3WJyjmezVmvZ-h8 zVI|DTHiBAvp#oqOC!rEsCzhz&Vodi(Dqj_O-X}hn|MLI}u@eXTs!e`EB9^c#{B);y zNrl2pPl66Z9lin1Y)ID$jMu(W5jT_q0^bL4@Ch} z&oun&v(p%L7jBi-cbo!+k9i2;;92-ksbW$}I5G|8tqxf(6pB3xaSVA(YRAs{0e$+0 zv34g}xkyhF>hvP<$%_XhGZpHmt6M63^-G-l+eZ*J-^-JddpfnXR2;GP*FU|F45&mt zNU%~02zZ9&U$uS&{ImE?Z^RLv60u%tp~8$rUVBmB=Y>wj%E(3hYuoSb{(hwD8LTV7+iSED7s&nHEKpakXA zh@sNzvOWNulo9%r6#Gn|g+P0zy82KW24VN=qVM+40lZT0>l`+2VpFb`W@0s*Hp~=d zvjnH%v~WIy8P=0UcGcBP`I#a@Y{x)tMY-29KK{ue^`FdkO~1i6pA*JtsN6o#=~Set zb)|>OvYe5}^7o&8{XZ7?&x)g(d)pvKI399NWO7z|MXY2`=eS(sRDq6BMZaRQ%(o>2 z$zye(<|e|23BcN&yJ3c$a%*hyTQqX?cO!*XiJnzZDp@!bsr?$M8hs68Wtt1+#l%?q z1jVXT(G_Frf8`3boS<4OLe+B8VKN3L^6#lN43pR^h!zmgG?0SODf;N#9Y5zqp|_s? zAwi?9CH9W8o`E6#*Z%!yL$6>h19D&YsYgaf`_|i>ZP#}%{UP3&-2A=zBlCRx;gwAC zhhqr5XKBaP@h_iK1v+nG366W`#&}$2XR@i43SD(0N5q{wsSV=a9?QcSe(M)8)TI&n zLJOHKj5>3)^PkshW))yH`nL#`4%XAnj{nfM2&4t1kQ?j79{3pV2Of3aJ+BrA(Jj&) zbN*Hqwp0}#5EULf)M5W!R1}r>nJzLbS~m5k=5MYsDg_1XOX~W%1oUF`iY3D@8xb%h z|9K14_rv^tEQDAPP0h_$5xxbA_Z3_;)Yqeii+|0-lztA?OheNR&y4Drw`OQfs8p68 ztTDZnL1&>}9+9j|O^Zl}_IH{e-5cKC$?Ql0CCD}7rP0=YP>@Lbndo?|-l##3w9YSl z(4;*%K6uJ=Ej1-IBqL?QZ6F7Lw>xZrt`y`t&?S~un<&&7O}IHlPK^na*l_MGCcvTH zQI|20I#~FN4`VBBcg;{%&h`C86O;bQZYVwjE%;xd;(t3fdyHSr>sQv!7deNCc;wcp ze$8u3O?sl856=5fHdObMkCFMGNcEAp(;g?vDP@&lrj8++D;1WiPAR|yX+x@HrC?xF ziPsPi@Gw*TqO7rt9rN{rq#`u;Rdkc|Fr1|;RKpxq=NXZT1fA%n8eNDN4X4`6KHnR4 z)_7X_m?rHrJkBr6ap*;dsWu<%BXcVf%~3x76Qck9&Hc06uYJ1P%#a&qq~!t?yHin= zqE4ST(n~XIYwPvTP67#Kj!Z-nxmdvlN=--JOUo& z^(@p&T%dG$!Ty2DZ7?u-{Za>Ua`WLo2yaL8PsZLWV5~+I4H0V6rAGCP4NGP7B$&pZ z?au&eD@S?r1ZCY;RUL^4UR+v8p-3H_dLyP0`GTzy`awn_q2Z{U+L$2iS}+>5Yjgh? z;rj*`GERqHOzJxJK3o$Ky*nVVrA-6Wd<{3xoWfmDcK;Pr;?|+w9E6uL$9;C0`Ql4E zt#Nj=&uCW0kE5)R5z}K;m}-bAqPJ1`JU48_w{=64Qt0i3!Lq4iiQ3_azzo}N}C^fkgqQ}Oj;p>74$ZOB~LD@=Bt zEnE4Hm01*fy8f_*zBYz(1a4hHnM6IMY=yqd@BZB~lS|#zmM8U196TYNw!|g@oi+-T zJ6nld`BXKu9960wX9Aov5DmaDw*3Klu#)@Fy$A!7+tx75FgRwA9R}yW0^K;&l7Tnl zyTOzB$4-C9XT#TB93`4c*Dj(=9wujHT1m7I5p~e<#4UdsUsJy$sy0$h z%}oUje`^V`JoL&tcrZadV);ZG2it&t{zYieQBymepQni5c7dj}V2_-OVs^Hx6XR85 zoOX(8Q@#K*NFc`+&6$o|TAiiLw6imXX^6IZ(z{iR;{CHurX#Ahs`;`o7uzIT-x?hE zb@t0&pYr}1sY_uqYg`R$-aqB!!Me2>$#gG#aVa5{Db-?P3bbZjLNxAg8WJ?Ba?i0_ ztdB&No3P^Ii|bEibovoWfDAUl=uofg=ykIB=7++(UjQcc*nYXoK7-o3+t`hO1j^*? z%3!T$4kwTca_JTF#S5kEnC#UfaSO@=-)uWb+1zxktMzd=Qi{kqE;~Lnzg?3CUltq9GN8nZ9JNFU6CdmTYYwM|G z)usRny%y6T8aQU-X5{6L(Nne>836{HyP9D;%f*;Hwi-J6=9CC${YJ2#-_@OdSDqaZYC-pkvgm*U&_za+Q2O3Q~KN(@^H-3=WZ6E ztiTBqCzLMLN+Y`PL5Wu)yr;ccThkJX8jjynL0I0TeUaAKIr^7^qvmnBh|c?5J~CY` z1NJ_QP*NM;Kgj+N;gJMbl!t9Cz{ga5&VdUQ8{0j@5zi7a0|VC6)%G?Fl<5ssku;iK zeS!xvue`*ZGAK+;W+`S!>*ks#&Rj%YYe7GKW@to8wWgvdic3p=U`};=F0h_g@eOV8 zaA=UK!T8B(khD?l7p=K4D-*e47A~(Qgtkw0(dK?i?HWEzIdJTd9&YSJIKLzO>dm+EyVeIq z$^lo&c5AQF;>eU!?2zGga4dPbz6Y^Vvsxr%<`KJdn;4o&cRfZM!U=m~LP6E$iL;h^ z%0uA~n3ZWN^WAQy+ze`TE$(%tBq5yx;x)ft zfBKrde0?e+Iw8Axmv`PgjEc7!e&8d8fM6%;Y=M(O9{~iF=k1Dk*J5-tQ+xXTH0^ha zQYyBnaME{uWuiH}q`P0AUbEd404qj;KuYzoDAaQY*?!1kd?ZqyxFgl8VKq(Gjt(YB z8b1dsq}*?^LNfh%!4N30eh|}7oXk}G-=y_+_$^Z$0vY)Of&)4 zt|8>!@VX%v{Dp{!h#C|9FP@f8ga~HsU$v~LU4`~*BQ`4gVg%H?zeKMr=c=^#vQlkW zg=98$PP17zbea1Kn1Xadds+O4x1GPK#+|~?`BysK%_w{8@=f?fbLehESQg;TRj}gR zXKBkZaf+xGs7Tm?5-mLCMI^al^h{_Bo-3e^>JReNCf^hZf7cZbcqSwn80v(N-Q0V` z_s%SBL}kHHY7=F|R3vkZNWdjiYUM?3q~bxxIdDhWGT(nvqay7;b2!%d;xSd`z#Y7B zfA3*t{!!N1TiH-E#MZP?+8)uVhDOBK8ZLgrmZp{%?=?ePXt2Zt{j;_q+kji^tt9eeI-rAI{<;tCAXa;q7+CU%I5em&HNjhM^j=PU)K@4hrq#ld+nud&X5 ztq;AYHpT6S(SvD())PY-N==KBJo75C+bEOa3Nbmj4(s$#)`%sAfN>?yOpM|JHG^K= zRSGaHnH!2%w_>e!q;%GmD$Q3aD8Io;;zwV;D7&S(?oW(!5ClIr!~@*a7hR267M#QQ zBYWs8B|MmR?1wq^gi>$3k=Nw?7m`?MF;Lwk>#w!reCa#K+;GrpZ`lN0i}&%$bc;uV;KU z?a@~T{CdYFx$7kSQy?*Qd}QBUB&?^>$j{xuDmI^yG`I|8=O-6+1hQ9;+)FKY@#M-G zxcYO3)K+)QHfOXdk-Ew=d+CQV*OoOpJ%#O8n`=F-^WBqC5)v_pg~IgDf1kS?X1*9~ z;s0Rq`l<0l?#ud7Rb|JYKUaU9ZaySr&g%n_w7yX_L|Hw5`?Fg*71NQHjV$aJw)Ul4 znt@^<%4EGTZjXW}`RS`w!XxQXjVee)Y+A4<`cR)Ng0!arAJw_ywVw}b>$SpsqOfu#&2=^V?kvBxj!FilC!5DEwH=0 zIe*(KII&_G$@T144<4lVUQiRYaoqYIew>xeIpA^;%B)nhTY;@A?LDVm4{^M)~1Oo;4 zmb$K786@9L?nC+fgSzh;uAg-6h9(B|EvszBi=FF@)2fy++PB1)Y}R8ng7Z4oa{oRT zKc#t2&BxQ}rfv^?-#lFWA++W<31j1fkj@No%S_gXEaO*cHzNq-0P%lz2Oo7l-&o9k zdRce&Kte7*&FsI!XuldgBZ$k*jV)HlEkN0&AU$JKPh*v#R6C_A!9m zAJUHwuLJ{G59D_pNoI_WKJqesZ-F%3zi1098NJO?t?OLFV*-FuIs#RU-ItCxc-_W2 ztrt0{-KfbAf}5VA-W869OOx6vECjxLaZk^^UlGlXdI_9HA?|ym-lq(v4d4|||7fa! zEh(H{6Yh!bdN_Bc7n-RtaBzcCr+Pi&iR|NoA?%kGwFY_4l3PfgI)K!d*v?ivBE2IZ zU}NnB3Aw>VfQ|WNxH?ttP`em+3!VfEBNqa9hbrG>RVX(rNw%B0fzrciG^S ziHH`I6Ez|V>Nq^$T8Kg>gvjD`)PhCmV#?=e&An;qw>ZG)#sDmm_(&vwzfQ?^#^4>` zcz908^gDBkQwmrouG-0*Lnpodu+Ii|3SW&&PfxsAp_*ggcq=O0UoSq^2AjDFeKAqI z&}cPn_U034?0auPUf})v&fxj7x;A#oWGfXDVDFNTF-B?MQ5*W+JlMC_U$--+_9nWu zI4WmOGq0i$6F=>?K|%5WKG87 zhfcpyzlo44K$URUCD&}aB}m^`1}!b-*A){(<81+jDDlbuFlQ zbv46LDqZenhMe4lu6h=l(wh% zqE3o8hu!X%N0>#o$bSCX?u}5x0V(c3Zv_qH9yi@TUHG|Xqt|r=U~`Kj1zH>?x!c=7 zUJts_F@_J{DG5mYp*l8;IQmicaB8zs?|8&lu%3Z8USG06Rz5GAnlAGrVpYOW(Zs^K zOj$d6f|~ zxmU3zul8s~9v+(896!7o$!w)=75NFd0~MchbR=X}b56nPZ4+_`Gk-60|5T%Cg$yon zZ+XaO>V$-EiZGLgGtrvm>^hIM-#N!QbLi=Ww8QE;tQ9z^?bg0s%GrML`Unxy)>{g! znZlmeJbht+Ra1KP2B`6K!@N_%xCtns*BzAn!t(NP0WYM-RR1TOb??hAODn6sPbmB> z&1rfOyYwO?qcSar^Lt$ZR7>YMz0w=-=Oby@Nr2}dU#i5V1l9~aa$)wPuW}00+_JYG zs)4;qPw>_lbChybNA8j1h!tYWm9pO~hUm#vtA1{vzbpnpuq(f}P?lH|xmSUc-NBXG z2PnA_jMgiJ9l+2`69_mo(*r!d47B#cdx2b{45z&PFgdrs@FjDzRLo_jhP$&9^*_Wk4n%>N{3LICa#WISgFZKu9Il1H?xXr*jA9#7|zkI zKbi*Azh63BILLO6)Ex3FmEC(F%rs~(8Lkk>N6MLe_FjyNKguyIUwRO~L^oCtAh*5b z=>}eEzAQ&ztfKlO({A=9@`IMIa}~>S> z#M@y`)_T0)zGu#spThrwY5e4hut>%q@XXqi%RyR86vSt&@!)*{u{#{X=e!7SVQ&2| zhSmmi91@!ko+F@{AZ4C{J6t*l!z^xv3u z(ezrmxg1KV-9**(yZo(vkK%{_(!n7VyYbXJq&?Yg__rHes8F{Wnj7U?3Ov!?fUNiL zZ5eNETxDweT6)_o#O8)`2)?Z?Lyp&tr1k)Af8U-AA>@m9e=m(8_1Erom+rs_4u~-q z0D`9r9v7(d<^o1@*l*encV!xcRX3ZE0A7qi0!>10mx;`OFMLm73`oMj+cKN>hu}jT z;CGPK_>%1bs-$W~XN<9~A6C1cNA_q_6xSi;wPcejUsps1kUWLsl8kxQ_`J2aQtZG+ zfczl7^6r?0dW9t@I=_g}lR{>O705GsHURsuJ*>cy!7QkU;Pr8klM+d7yN1xX@HV3>a( zF;#Bi;V)aFKd?oprnp@U=A z9Zu}E29U)Ww5OwFV}y$Fj}jA({Wz-R7nqerIFZe_)aly;0bhz}H~O|6fam-+Rx@=i zh*n{w`1U%lhX@|m{NLqpeFwCSV8T@Hc#LPN=@yz;(p$?~TTk*ic8^JT`c=x%w)ul| zSF_viXJHLh%XVbm=v4JlX^QLAJg2jZI%~8)t7V1~R`zp}K=IUL?t4_O13{b8Fl&C- zWw*cSNP+hLch*kXzv7{ExW^DS7`}3SRuwM)`e+hZ!#`4_svej)2cP27fSfsyY~?b* zw#9eEq1p zF_RHS{(%G4v^}6iA=zzX`IJpaGS1YIo}Qj1H96LEoU3A7@9xOG7Lnyd$CQE@I$vH% zi)vx~)FvU?{JV7tK)mEJ*5KE6|(&VnKFP> zm``+{kN$TxV7sg77gaX`mC#*23f`mDZgiULrgUH74`mC;YN*T}(@mP5EB1({-92G_ z+ORJE3$9tGrn;IRn8voCGd_xaGjVu^JC+riE0Nj{Bd9K_ue1in_mF}@4_^RXlrqHG zEVx`UsRZu8(+N7~G=A}6g-lGX|L0+~wC0rR#yF{0(x#+7frBh+;<6!d?AV~Mz7onD zi;-?D`+OBk6*}3ta()Bchn(KJogk-JNmXPq)l0eAe^Huj9KiNJlFWAOJXGMoN`r;2 zRttX^)ObL^M~CwwPfXvqJ3!6Ldj-h~6PC{b#qiqWx|cOB))0HJT@s!Nhe#7s|NW)qaLUjrPWc2tlh z0+L&0_o*}+4*a2G$b_7g5GIce8~Kg7>vCEZ1*Ndec>`2$O)0)cdrtC(<|6}0GCl7HEjDJZQSI9cOp%T{# zf&SR*Px|YpHxJZ1kB(4EIN}OS+B&yTF@NziR;q^`Cn|iLw!>5qu2?HSxR}#O5O2^EGH`jnT3vOZrfwvks4zS za<;1j8&c#?^Ol~&xFP9`$!{HN;_(JD%}M=w8L<;)ToT>rxpd-g1s5eyT3j<pb)p zYFM7$R#|^lw?FVO0;ybjP*~>Hd=jng9pz?WX0{9nlpwS57fr3Z4Tug~4ZeGt52zqs z!>O)?+g2Pe14kHM_n0Ii-bM`DN!M#IV3_evf6Rjp$fu#*=N%;Yva8X$WCcHEZ20EJbd^qUm+P>}by%Qg@B3~I6j}i~ z(8h5#?7;I8x)N8Yhg>~ZeM83zgH~+giA+zXne%uoDWRT85yQ7?W+IW{-0Kcm-6=Gw zf`jPYncH8jH7T2NPZR;@*BD<6IHOeEd=XBlx__;VSl0{$Z(?&^Dvt|F8`mFBNoky@ z`go=Eh)v5hlAI25wJv$uS_>f{Jf4f*({$bupWjCaK$eMUbao%S{EN48Ro&3jFO6^B z=TNRqn=K~$hh)t9DD}hoX5Fl@h%0~lmmqwZ2qPI(K($3E=z}X2Ls)m1ujKZJF*^4i_!{-*7`DT#tAb!MD4gwo@^aa#Bd zhdG^m2_s4#Z^tLBLe)-D(-=yxlX)7;wMHRGF}jq3>6utYqlnlKQ7}t22c}t5e_M6z zYpPk|zf}W!-Ng?yJLT&%5(K4(qNO0G=UtD0)zoVG?P z$t`iOlALgEAgYHF4bn6>@8{Pz+m9XPxsq|UFvPA&@Rl5C{3vaS+}~7e-l5;x!`=$L zo_`SN_Ht7+XE&YdH>}bNXME}S^K$`TeZ+4$5QqP0-z;(s?QF}f-*3w#IJTQ-a@F0^ zx}nQr5Qgje>5AuCV8Oe)^S&+#Afk}r_>`)AUtJq*r|&@Y9})L5AI8T%S2!Uuaj&HZ z%-9;n!)d?yrCMZVWyNZV&;8^tXkGGwBW=YTN(7g5(w-`Y z6Gc3ndB^FWJVS;Y0P{!#N-FPo;ZIlPW>m$4gSwbEOCx~!8aT{y)2dQi* zdHCxf_`O*{{Hk&`-lA^$Li%UMrofWHo4KW=F>wK&vEqxXzec!xyjj#TC-=$8WekR~ zIPiqN8zk`5p8x~mY@Ml9nuvG?^<`h5$;!p`l)JYB7gn}+_DIMcm-I77_uZpM!9 zw5|(7RWmdQtDU!{;^~PfUG0d|pZ*oC%V}(;g1bOTKRwAQ4zlZAxhq--?bnut``%kk z*d)<(oAH#>z%{d02TeML0y5pPVYnJvc{wjgf|5R;unc*STjo}GEg2K5vmDY&Mhvov~; zpvn(xcmLpS)&vKjd<8E5K08tk${Dz(EMucGBO`dJ%g~(-0Y%w>(pJscuNKV4S#mp; zkbpmj>DCNy@&C50Xp(p3Q~R(Fp()lr6k9k<(ssj=+C8qn{Z%{O_r^yi=}U^5NCNbk zSbg!*y@@W|5lQ(^65xIM16agnNWxi0c<(|=M#0<6hdr%sNR-@Ov6!m2b!dxRo!t&(nP#PxY7k)3LO z)Jyuho+QdL3H`j|Cdgv_rtPNJR~8Oy#H+!=w9F`!4c?(a5~XoT#-vp6a7f>faMLx~ zH2zi!%kqYBpM?pXWHjd}0@JP3(Xeo_>aXsbSZ%BmBFn=%TH)-LcAe~|>R(2Y$Cu_Y z`ZvZke*hD8C-&x_g>jEknVdIExkBRwt3Pb%6khxsM#Ir_=4+K8kK6hcnxVC$+j2~` zhP(IV&{3anwN`0qzZJtse+zswCAqcNk#sT9ZX~v?ypX&Vv#xq`pf*hTzd-qi&;WX< zJZhI7{r?XrSC4FoU8HQSOVMiMSOzp3I)5a{6+Im9*)Shm6l(rVpHeLvYtivLgL#Li zw)-``H{d8|z}i7tV+*q+cREDLVC&cGs}FNpLyA)QSZZvUdPDCgJbZ>A2Wa+qJd^OCwf!KV~}ck;kI5ZH~`X`OYyx=-PjS zu29f3qI*e($nNg!ha=8<(r$R536i2f0kBX1PAe{SgxCL8W7b&!h%K=}ZAUqgc*DBfg~1e%%b1#*RE6NQo}W@S&Xd<|Wt%E}iMn6y>0 z87<{$+UqwkYmM5zQ@azH5iqgv=H`bJDBHt;gF+*XS4SJ8Zys9gsQz{2@xAYYnPH3U z-Ip#SW7v0kEp<`Fjo#`}2Btn6)m*(Yr99dV)Uk`dWCu*{2ykf<>f4F;zc)l%ZjX~9 zldO5SPJ-%w<@CQ6E3`K3V$kHQ#nyXi>-uc%?X$lk)H`elkou6)0ew4fP4CtBx@$7) z3Vv`t50T#9e`GAo3Hb<66C^a$$VTOI#cuTY>9%QDD6*_N~sgXLQ1h?KhjS|AI zB!bgz$Q}P*(0ZRU#c$@W9614FBM6}%TWnDJt)f=Sc6aO%I)3_j<4frjzkXXMjvxyEZ~ud)AF%vQz# zs8V|(9KFtQY}Xo0i6(IaVCLR>oIs5um=>o#7b@r!(-dkPVx?$n93d)a>|)BxY(6Hp zYJ1VGnAN=c2__1ARt%G*miu<3(=shF^Et&7bX22Nt}bj4_Jf;TEp1NN95U~(=^56d zw_1s8^|Q<9d!l*50{dYOn8KNNAw;|fvPPP_K6oZfB<5`n6tpZVAAU>|}HG3Wd5d>rMsRg^0j^~4^44_Uq zre3^(+8-|uESKeS)?7@T?bEY#I7#`QVmO?Q>mmFXd5OLNO~RY|3H$!Hpv(y|tu7YW zagz0jAX-yD55bojyfLTRA%(1uJ-w@TDrrXxEiYHrkJ5HbOuT%7)R&c#!_MRzBiRei zS1bLZ``9&jQd}SdfM4az^|(Q$>E52wkEP5;$w#Bt-Q?Q@!BCoxwjO47aV6BBk`ZRA?T=T)IY=!(1OcQ&DpDO7fe7Ka_WIsj&5&2P#;(g#}8zt=(f$ zvCQ(FV3WEdNW<$2=*LOpeeS{})puzV9VL- z&>?HT5ijjT!>XO%OAH~s#Te4e(`pagTjn|-`=)MI*tv)`)$UW&9a6o(#MXkDZ++xB z8^EK*w9CPv&qC1M^DW2Yb#NN(&g(fj=~Z+0c0Q zJH6a=#qe01ZX^1VpAEiYqc2>tb5&JjN!bF8g+Q@&O+N>%4YubKX^;o~qFSu%y|eR& zKyO~3)C*>@tZK0)ZP7%o`@6k*tB2ALPBHc`R*T(zss3T#8{CZ{CvkK+1?$z`8t2VY6=I9q21%oTJ|IS%sU>ZNP^?;iB!pe$$(?jXR;va z+=P&-i91%x!lEY;w-X`~Au6>%f@G5;&JwfeOQBVvJNS|%mps=|pDaF;RY(25 zD&o(dmn>{qefgNUxpq-hAxTxbtavxhKN8@a*$}vka~TiW(p_s~6aE-_oC{d3yi- z9SR`YtTv8rtK>+D6!cKH)cD3>>hS`;UWa|5ikq+7eL*pL$ntC^j|RGt<7yZHeb`wy zJmk%qs0yhBt&i}}B&Y@yro}C9GR9V7xS{)SLy6n@3xtmbP0^fk3Ppot4ltO#^0;Tb zveHB2yJPCZS^;eJF)p>OyGZ!1DP+x$#k?yz_ zHHU|-WTu=y1~6}n8~Q6)N3j?f{ysUR!55x@Nc{7^&E~xwb{{`3TgDb$rmJTmZ;4r< zj&$Cb;?G~NiZRcrymFvtEloGKnil~y$xk|s-)3^8c@QbZuFAF}*)ebJahcf?F;wUu z9z_m{5>0qulOr&K>1vo;ZZPrC6?>zz z?k%_yFC1>oi;fYxGAV7QbAGO5TZHBP2Pksy{hYRk9_F9I`()?W@9jF(KdJlP2|3q) zC1oX@7wn_=&3oPQ)qoan{vWpdUw-)jn71;9uRk!u;IYAb9&wn;bb#wVP#dLlS9zGB zeAkelV33L%CjI1Z;tb2{Y%z(M9HDHfsuI(H@<2f(QO}(vd-%;^@n$jlr?B0>nWrm? z?nNiU*EIBa0&3EmQVM)Ho^;Z%p2U>F&T-57wmiqN{E{g2dkYG72yD5ugHD>#AjOED z<|6!R9buC7xNtf@_AT}gP`B1`P8kPsUvu&%T!`Fp>_A8KMJlYd08Pjc#sEex{ zmWUbp7QvM+88T7nEo2u+wH_u}47ZY==~elDhv`OImThjq*9(_y{6AiRYF^x0Mz0AP zn-IEp7SO!UBimA}`yd69EVn@zO*W8OVw~co2szlWvE$%?`ybBvFYCDtA~$SitJPnz z6K@8o;ZX!Z?bVeDGAIo8ry2ojscDNo#m-wheZkb9SyUzDskA!pAa{omPYK6BhoGNk z>h{AVtcGKDR&=`sY$H+3$I-l9?aHoRXjV3Pfqn@_pizIp!qiXo*G7GH1zEkkf2osX?vSF^x-8K+_oR-iaaaY!|kckK4x!?`He;G2&f=qwzKiS7MMT;Qw$tGj&6r zT0?TE@IsM+ArCeJZ_7>YMbV`NKO1A+b}}+M$O>^FyjU_K>HjHlxzl0(vhw?!a5gadHW^y=v8nbo`M2BVUy1BCUJ@quyOJ;U4gru^iKHpox(^w zQ8SL&#yZ7k$g!n~Cuzg8SsywY1Z^eSx#c*OF~t#Zv1%o0;}9A9K}`64Y_Y}ckvA2r ziYbvTvgKuA-@*5a>@s|w%lF4bW&6mq|6@pYy%Dvim%^_3-i(aIQJi!m7)N0(0I48r zN&W#@p4{BhY!Lhr((oS$xG1C5>B30x|Ac_6;8zp(Jk#*CvkjpCTOZpe57QMJY&$_)QTfzdETD2X}XOTe!QsySr=PV^{6HPt`ef z?!DjB|G-+~8FS7)-qBj?GRng-YHt*ge(DDol-y$l=dOk$?9JT2lXtKmDOKWO;~%0G z5?MonyUX}h#Ezz^)G@o~AxT{!93)%2x%CxUoHPW^ zwP;d7qJTA7^ypX@OpD7Zfk1~Q<|g8JpPsdbnf0k@$#2Dv2?~!4!m~jDSLFC7RMsY( zNfnQq9w}Ja=vb4{MiOi3js%k<(%0#zkQ`YQ2;7HPbunj4t$J&hMP)UFt8aklsDEpR z%cS+~$p91k#;A%1pBt{a(*0JH7;_J;N*(U@(5kfXSH>NLQ}yjO#%1OYEE8CVjlFY; zT7Yr`+MXJ~{;4;y#v`4NL@^4H>UvTHr*x0#V-C~w!7##pPz z;fJQa2Yv2(uc*q5^w=fXOh+cttz%#__8X_=O4D?mR-@qM0M{Cobps|*I zOF7$CalesK+RZCe^~f%Y;YCTg!vV9TZ0<;dZ+l1%l#rkHCU5-1KGFv&ETc0{ z^o?bVj2an*-8QL`VWkjFfkrXBRt(w!D53Mduj~AGSo3{D!i2`gJb{ z5vdAZHKOlHaaef}RUZ51Ctn1C+sUp%2B?8}O%1s>EI>5)hJ>=by#_gc@8pWeH@C6{ zivvcYiEG?ZX=&b`P$@ynw*Gg!8uAkKDDeRj*s6BKV+#wUp%(NoNQS$;z(k=f*AeFIIn( zxpO6a6?)pjKM(2=ZdV-Qo>UIgItRnwAE*EC!ronP?gaxcQHT_1J=T^s2b)%wEnr|# zS-Hqkx9C81x&b&i1=lF2{r+AGoWSp;K@%>j6V7HWh0ue}Gt~ErGW4zHfDXJB+uvFx zlMagsOfigDu}$fy_W&mg>TVB_1`jE#TCB(88l%;@z#?ENUTISgoP8y~XQ~xS8Nv^n z{zn1MGb=00zL@SnwP~;hgjFpwjC)cKh=nt8v&44Pi-0(ZS8oLbs&tR1y}`Ckt(o7W zSPr&RfvbGHuh>Bf+$*=R70P`N7E@uTg0z(gPs4-2p6{{cc%XwmQz#m z%bDPP<9*tZj@Tsrt5-qR=md~n#|OJ4NLE*vu){0MCD!k-5VP+vgBW3VNsbl}_!bd3 z+KdbOrh;{(S!=ZoS2D!#3Ss9_rRoMP<@xgG@)Mv~`9;i3LQ5p97VZnob&8>1Wi3cZ zKy@IjUx(`PBv(YYMst^jkY_lRGQ^LjkQi>ASO6C1NiYczj$=}Y}wRU_eNPg$m zKFBAD`+$88McQ>^ZA~8hfRhd>D`=42P14k2mTE;@leJtTrXfy-yC!FC}bh{mN zyq;E5uR0wjwpbAUr{zdPfXlPyh#fVSdZVmP9RaDKC9g^Ws=kt(TMoyD<)88Y zs3X*-fZX5T-1?YtaGfX>plLVq8(~((o4|XeHnE)y3=n13`dAz zHiyEK-|fCG%wGUNcmkg!Q*BN9$mO^j35-@Hw9H@H2Zl@@Lyfk89s-X7O3N><2g);w z5idaN{!qWAL!98z1&F$EcXaOaPfz6dHHG6f(z~@cZ`jhGJEG`*UVLYI875k7KFwvP zen1)Ly;2yW`~S%wMR_#oIjhG^S^Rb}4p5PkB&MnXr1phErsCFsqcF+L^ri&{!h89$#3vUx-Cy(6*M0MqepUA11_@mq%0HJ6u9|xrLnI99+-H%QuuY4 zdvuhf8eDi$n2Zg#g)8-@O;R%=ZcOZN99K%sK7qQoaWk2u@9m~@a6u%mUNFTXC`QfL zG?H`vL-qHEPqFjG7=!`6I3%?jF)h52Oyi-VQvx>M|Az_9ft(!kK${aRvwE-JCxvzH zRLdUwqt?jKrCvurg6|2=O(;bB-zwUBI$q9eM7e8D(e(rxdG_so7&6m`ui zLT#?%d>`?=vRn`#ndrMx7Nx7tVe{I5W_ofgr2S+y%8xqw&R$?N-Zl61YEyfiIr*wb zY)!2SJ;FB~kq9L4hwFmn`Eh?yLjeWX;<7HE5g0ibU;Y(CJo2N}oo23u(ic9YfCXtCRx3rtXH&Vn7o z0aj)2(e2tnq@Q{;Z2zh>@kY5PST)249tU!$y=|)CN)g`l+`vc~tDny`g)nVmsIAaK z-?TnE6L`ZB42|^(n}}ZSdd(fWTIb<3*Sf>=YO4KQ?Pl3I++ROgvSH!$Ub2X{^n8Un zb&s8g(t;{>duBbAF?Zpal!X{n5kJ z(o)3aBHI-Qc?mX{bq5?&9DSGvf@ds$aO=18i>BYjbP)QmWf0F?BtdpEQocF>Nx2dZjqPVd@@VRCnJ zg|^Jqv9uj29Qn3m5bBK&a9LF9f#|UmpfthiZc3eikrsiY`k)sL2W8gh=KwQ#8Wo1; zoG~7(>V+{DF%q4a;5)I-0I@$F}dgN z8Y3KZ3do0zB^5$$OHp>e-rdIOin+fN>*=L1o{WpKf$KnL><@>BUj+)=+hr$6VW_@u5%Va*FT7gVA>R|lDAF; zu!W|&St~3FsgnO#`X}6!&_Fh&By} zkXPSNh{=eKxLjY}qo?U6G8KQ_riFD`Pm9v={nzw zU7hrTB!Shf&V4i-rwjxZMl3lxPuUgCmP;30{ZOdsHG?M0G~vJsR-gPH&DiTDsve&R zXRTEL;57S+V61W1vvNpoNZ4O=qYfYjEMr95jfWj3B_Xx)i8K~5HdRznw6w+hB$>#c z1ON>bgna$a8+;YY?o|KNq^+96msH=j^8l9jZ(gW^# zmkA$!gg^H`k;x;Vs`De#I6^#@O#ga!=(WQpG~KDZ36@i>j{f0r%{DS7Drc7*ud!S! zV#$He+?L(ft@%q-gt`(|UOq7^16(ZGC1?5-ko+;<;d8Nj>YmZ8LL^F(=(-redtE33 zQ%lIKgb7wx;nx{GrM7__Ap(_nkOeJay#(x~8% zG_{gUE&fhQJ=B&Od}3uMD{A%hMKeDc@O%#U`8JZHUBi}C#n;`(db1g1e_75g+HqMM z{?_3sKGybcvk@myG5akgbV4#LRUs&Wyi5h#j)u}WD0XS5QU&DJJ$6{4YhJ^f)diXNr!wz}swAVD8 z&xU4mn~W#RFp=Nk(E1_@;?k1LaU`)n=6e=AFOn+oN;?>)KzcP{35^;YDcpkMF!BYl za5;xN`a6v7q%myX>on59u-WkZFU&Y*S$^qW|jz@(+h#2pDYa zBjI&Sp&Z{e19lWrJ1l&kg(1t$x0seMI^!sAaeHxz!hJ4fk^uapqCCK)U5c1=+f2Xb z7q(FQAI2u~IH1IP@Uy|weJ*5DpPhz0{H39gIjOXRp~V?|>?7Ssm45{Lfx9oKL}Ra- zF_>fThIcn<$t{9v1u6V%oMX7&PbxZ2e@{LSN#9V|%U0n)+HK z(%7c80xGQcW8wP)OO*HxTq!D3i+b3V={})qm(Xi_oemp)^f%IbN3kPG)v;C5uhQWYN95_Thly=QI z3{oU`5)fAVN?R%Y3sdS)SO!P`8&c-=j771~p1g6?!r>N&Ug?=dbuwTxuC;1W{iBR!}LHVC)7M%o9V51gd>&WpT)34T6prQzmmYSCN z?f|U0Qko_utR0RkYz}FB$s2KZPqK@=)?^JE#`RhnlHDzbED1=eKsQd!8ddr*b>kD) z1sY3`Y;=r99aAH85XM1Jv&2#2y#W7)CQ=Zk(hTaeB*-Q4y4U??L+{4eNYRRzF>WF= zi1-S4)iCAwN8%3>3WbrxJ6+tgRBo2Ue#3=?!|&i*sJSeC=uMzk(D+2Qu)dA1sd|%c zItL*wlIKBN@p$@q^}%wZNtv&=Z9LK8;)we^Fhf<#cRZcg=!1L;X*y_ozTaFyN8DHA zF66DEVX4R3w;ILnd9WFhl~Vsi_=~b5G=QzzhkE0LuMaBZku$^xVLw}v=>S-FsQQmi zzt6f6j(H4;feL_falFwqq9M0XO0!^ACd1m4k?eq#Box#D7ou9~&TVceQw$UYuv{N) zi?FzeR6+7o8j>l}NQ9`gm1E#&i^3gaY(Wa);}th0_|2oc^66KMOnhSlw4>I2CC+~- z)E9uJatbuvm8g?bUOC6s+RoKDnHEvzXFd3msGmHkqBWO)d0F$DwrLb5=)G2VILIw= zq~zr}$)nZ*?!(<}<7hEeH`Eh=fq6P-yQAv3Biy@3_`Fqf$<_3p-JRd_5fm!l|3)U& zioL$#xxkg$Ui-x{zW-(EpJJAe=qXTRkn8y?epoVgR7EIEG~89U1)a2Qa;aGvcA&yf z!PGUZN(Kq)o5AcN4>*t6wsJhmd4PJ4*mTZBXu$VvLKZn9grUrDzR?Zdd8)|>F&a06 zs?+91AYHjueS+*CsT6stGe2S%}pa5{UwLw_GY_Kuk6!YvH!-LiRrwH z{<=I6TKm#;998vjVPQ8?D*uc~I4S`H{zuNKIe7Me;GF42GTll4gLA?&{>eFK_v@IC z=UBJZ&vMDja~9pSVM1*}J;n;O?++;E-FG-*t4$o3LW>A2nxC(s z)##$E4w`eus+w#}$WI(tC2@?HI?STAwTpJ>cxm6xDZB-89R6ocg8zKT>S_(QHO5g= zf~s%&@_v-Q4f)Dt{DocEMUSib3x3!QU(El)Iyv0_W}S`nY*WoP3o3QaO>27IYgdSz zY<2oQ``Y&zls@(zWHuW^hGf&kKVkmbh_%kJ0I+@wBN2ExY zOftp-?c~oDua#t6S{t@Pb+r8?z*1kfhZHXAj^%a023UAE2BV9Z@$Ky*;jy_W=f+-4 z-{cdA%S0-VYmCM1v**d|otm1EC>6#D5UDN%DZ zcC3Y)YQwQ*Ty-JT2jKLZD*>?{kDd_jicYa`R#nvMPCd|0oS zB8P!8bcu1>9JXA2|CTp^eTy4;p_=h$+(3CbD&J>N zbAJTol`Nw^rC_jr>`7io76rf}P#p9Cq9;0dU98DEtKAo5AfD?qe74A3HNY>Nhv;VS za7@18ZQ}^PH3NGb$97WXifsBHPJSE!H-WV(E3=gc+S%DkwUJT#TIMO>jdtXxHEh^? zuGQR{skdpw9Krc3-1GEA|A9?2788O?c69?%{Ab)C{Tc(=L+_S~r6z(>NPd?_zK}b~ z8-|;-VssmVa!FeNZKb`%{~aqb`GQXH1TSp(7~Y;6a5dU{e@zAcWWf6Q>g zq8d%gSG&hE@ec}OF*VIviKI{C%>CTBg+yo~re#e^;0HEyB7%*zyaIq|PMtK0n57I20W0ukZRM!u;nP znlYBCD30@971;5@Lw@|7UOmn@T|}%0t!oWIbY+P;a+}<717?ob>14eJbkYyKNILG2 zBI2>S`aWA^uGF2@$v3x{9u97BPpYgv+WcGj`#>06X~g^_x;R6ojwp>_7Kc&==$KVw8Fe-pqT1GY(CMo8yc_ws4+FHj}~9dgsPj-;>Vo*;tvvox_0M*$E2Hw*n~Gm2== zuSMfJZ2s#o2k0@Tm1_T&4B3#$m|SOP@&60})lHVde3?uRzQ(@j8`o6pI7sIP+>efm zFSQ#ovXF87%YFK$E6mUQ-9aW#BHcdUiLWEP+rpj|$h+zy8GA+~&eg4(VYmhC`6oK# z{LNf$^rnt`c%J=%*WE!pj8Ts=M#@@7PL-RWC8T0m*+kUnN*b~aBN!OW?zs5@kEWEs zyGA+Yq1SJ=yM^d0ao3DNl*0%m+a{8>f9MKvhGRVc(gOS^jp6(MqA~n`Hlb6r?$OoN zv+P7NCf?_Yiu5aKJAxxXA$4 zIq!6r!qAr6^JAs3IqD0JXb^weCkgBaA7CVn z)~uG=p_p{iS2bv&?z`QceO$}%#-xUYjYn<{y_8H{t%|YPo7~}gHdG-t2U_$`jkPV* zZkRj1)+|shJwBo=-jS6fd1H3C?zQGk5Dr^!+9WOjHd|$PRvsF1az*eBzzj5QKP_WF zm34UVIWv*nUOr4NE{2b<3tq93)L_9|^}~V3(8PJbyQfG-_q?3lH-6d3hog;Kg7qlD z;}c*qF@3m?(Az>`!2syXBcXe`oh>#Xa1r_Zo9Wy|D>2|+YL1tMDMX6&U$YBwZ9wc| z@1V&$$@~QhS_lIT1Q1ZM$mtD$)2?DcP2-s13(&-J6cx966Je@YAjO7i^#<(5S9=zG z=dOC64@M^&#W0F$VkMKO+|aEUBTVOwPX{H3U8Ug;)H$LUGRHrQNLOA_f0L$^u{-8J z80NPN{LMWpp2Ea`Oa|?LXhwU`B$lY6t34E!-_%g|2?I?@e+0-+qeoyXo>ajblv8ro z$vnLU36p^|9$J<$(m2=oxR~58~};Ox-=10fcQcJBc6zDb@uB8NETJWpGs?pd$UC;)&y6Sok%s0LHlIA+aANssoeL??Zgr#&xf6PA8dA z*%Qw)jQIyv+XRwKWbcn8Ce_UkBi9{QGw!07_tq1niX^QD%W(`015HCUBJAfCf%{Zi zvE9bei3E&48x45wit2t3Nw(?7{CB}1qs_)`8j01!cGG-Nj(uF^yqXfFM(ACjL$uS< z-qnGN9}iOo8w+wn6y=biM)rRZv5%z9nodYkC{T_BJ5(BMNjEpRVv+cne1s$+7HX64 z;>NXhiBeHlm(i0|QV-w}<4k5|B?0d=mAt2Zw+G5auh3>JQM@Qz!*?FDE_9!6O^nj6GCr<11s$;u@!luf9-VauFa8_Xbk+Pv?gc+5DJvVSGI_->KZ+V*uZ9-2 za{dHjPxo5nq%52C4G9b7@R+z#I;)Y6R@;aSc!<6vL;W$sj|*p&?t_$Dp!@ zNpG_Gg(xGzyUyAs9ME#KYeUb}` z({t2`R8;D;zmsh03uLZlo0oPKRXodGoF9I5`wA61I=u>ve4F|dr_xNc-&Kb@A7{^+ zKMoDcs)o__%Im-v`3wr-3M+%yv~Sl>VG9F8H_t`zP}dPL(RyEiSIcB~eV4@IG-~Qu z9e4ZN73RMxeCo5ohv6c}U-V!#cW2TMz;qP{U`ga4(t2e@){?*_Gp=ngFSO(#>eY8+ zo)a*nAXDl}?kbV{VXH8Tfpj_qHjKgaCG1p)p}|Fx1+SU&fecKs`x{RZbP9GOwQ~~X zrIOqLs}bErE~8K`YJn%H)SkLnxt^N)QHKMA*La$w#F*ImToMZIskb=ge*6{s<=@3_ z0uzu&631wheU6${@T4YWV-R8^> z7>Sf_qQH2VY)Cvm{xPhIo1)9J=1_gMo>M|Hh0@wuyqlasUE54T^R#5Vw-7oef55Na z$%3KwI#taod38mZFal&=k}^k`cA(SU~ow7DfE zN=@c&6=XL^7yM_538T8Ti7>d?Uf9Lym~$vJmF&>gU{ho;v$sRj0IWZEsq6+(TUgoG zKoTLvHS3W^s*pD=6ZEZMHwN`gv0`K)3J~?)VVZpk>>qi@#V&4iEVgtgPGLMtQnW04 zXa0@f&0%h{05)8aOH;hX7LQssAb*zch6~KkZp;3}3&QTyEXLKZXB1MCN|H@?c>j1=x7&vj(kaC|{qTsGA3Q#TYV7Tl}r zXMmJx`lP6>fTR-lb9~*_Ak{2_oe- zce)w}uOSY4P=_cm$tD9p`ABc4*f!?!VtYF!zpR+360zkG5EW;Ctko zj4rqLw2UuYS>F+@2kIzk_|Bh4H-KI^=2lxPZol1BLq|MR;Z5XmFS6N_?WwKkjJhX= zWnj#ZwbTFOkNa+5zzjZ-6>0@olD;7)?;-BrI!AJ#$4m&QpwK6?MP){Am*u!&N6e_L zErXBqN=po+L-uQZgUI?Z)fz36QJuLN!#^Or9L}_38EQwQ1@S{;W{};1_?Ak-j)(A7 zoMT0&;`!cG&N07+xO_g&#X-#u+=?a9^+u+oCjSY3TSd~2DuojFb?dj<2#x~PiRPuJ zB$LWIBdOvYj9fyL<~+A=3Y<~g9mp>dkG8i2WS4k78{5D!FwD6<<*OyN-&aN2V{+y0x+6F_@8$Uq zjkrx952y9pq32}CZzv9D#&OAUgS^yzhoycQN@VRfQGRoD82(-SwmYwcc0-GdiT7mA zV2tT9%$`kREE9!Low3|xfu=r=1S3Mrel|CFO_Pcjn1wwn;SQJObwKu-uB{Gw+p!js z*$O=h$C`+1VbfbPO~fQ7FHgzJ;(Vwi>1bK`RZ8e;GK08PDnCA>h$1KdWU>zjI;xJh zw?=`cvLK)Q3Q&50s^Tt3pyq;Zoqc9b26 z&DQ)Dnq#TjTYaNFWvaG1vo`#eHdYg3i5^*Xp@+kIg`Rn41;F&t4L-tGCb*BwneA5y zkS-Tx^hV$$x4*b|g8IlKEz5y+glH7QH`~K(zon>rU3?aKyaX?})T| z*1@H+E#!97&ADN{W3iJT&{=dBuX~xSKJZ99nuTQepP8B-Rjp<=oC=&|h@38YOnOgz zn~ZRp<-*nu1PF1 zOp?fEjeAMKOJHlo?4mA2^KMhq((ei{vHmWvo0t0-<)GzRm_(S&YNNTMSDd#O26YGc zU2PRmlr$EpWRg~m7s#MnJ7!Mz1XN0e;XG2`jl2Km!PF;~?H-F?{Nseu6Na_tt9!ls zsG+m~DHl^s9`^JqcT5=`1$qOFa8cN-J|cw1hq-uf&SJsftYqyG%{R9&7k&nvX6uYS zR=(K|)MnPYf0^_~uDvbHdSmBjpTXjT2>ma%jK4LUftd1~@)MYuu@-7E@zTgN_hBDpjubk{bkI_6$p&50@`GQ1!s1B$r`v2M)$HZw9@dizG~g zkUncm<(-&;P_=X;z-rDtZsV#)~^O|3RpfBsO5LC0SV=ZtWVW&(^3e! z@LhoO&TWORP7de*eX%`|8b0H;9>6LNPP=NR=#;m#GW~vfIp`FXY~{?-USkZmFH2g2 zZ`p0UZtLI8^~-UrVgC!wB^y9$>&}E)zFoxy!W%7yIH?y{ssyy(7qW}r9!y(*#Hv#*4^O*>(q5#Dc?y&D>!?vbmF*%lcWtEMT z0O)G|;a^de=j?%An!5&6TeNhzuf=#xAqO4=?%91BmPeNTJr?-Akr+CxQHnh~3|hJk z{cf2u@I;K1p*9N^z-7DMTds4#wqCRaJ=Ha1?ka(h&w%g+1L=|=+Lv`<#ToC`*6PJ+ zlb56p1GuS(t@Sn2)z@#w&U2dY)$^Iz`FjR-!-KtDX=AD$%iD$NDEY`H|Dct22gm)-M$i)x9dR*&_?7Z!+b$34 zsL>1xWl9aDdAqR9vM0bsA*-MSEUk<0pqkkl#kE8$zR%c`Bt#i33cSpag;sx7sPV__ zYuM@RHLkcu+h7%KII8xr5v6IaJ&B$&ECTKVFRpJcr+pa1QsPvORzCgT7s7Vq_76EB=FG>>=^qx6Ko;+tmSy`oTfk)3}<^+hr7Ond|<^tOCgI&kNncSgVuml2-8G<@3+ zh?Jig2+Sp7i_!*_HVYZ^~Sqz%=f zq1!37Y^MvJyZv@5YNiJ!ubw!AjKjRHMkBQr7U9Xk)f_NiYOVdd_aD!j=sUK z$^D_Eot@WACp54HFjDA$gJLhv>%Br6T6Szi$*NZOy(b9_OE(p-1W-Wi_v_J$4d&i8mH`$yw=-yqv$g(z}ZEu$#wR~5C zSU+^dZunCBWXJ4O2bkFLJq>c!;&3B7aWdEn4W3+73w{~VUP8#gR#!AjFL?a+x&-_Z z*1TS|J+fCIDSlLr`8J26;&GpL^fSJ3H%wz{7gq#OvKP8-o^`Oa%(GCcOxak<4UdNX?8 z-A;gY8-i&ws~Z~15hx2@*c;E@pc<@yL{dtH5eNckJ5DaFdkFWL`JO1uElwtv?_>;M zi!Fm(?zcz4V=R>(Ar8x^dzcFzR^BBqUCbt&hs*rEN@@AN=Vme$#eIdZxkgnu5+!zVldSc8_syGkxx;SvWR%Ig!vY5Hrgs^EgAc5#NLm zb}wzy?kG(Le-C*4ywKM%s9?Q#Jx0uU^J(W#E)3?iq^JGfto98_Wrq)(4WL3YKE`oU z-MRlmTVH(7+Vg#@uPH^`symq+L(iVV<1=4#Jy@m&UD~H%D(a~Lr%1APLm2necE3eI zN%5TjQ-{@S2G<#!hB34!$HDuIS81C|}-y zeYvfc*`Xn97o@r=Y!jPhmOUa;mRe!u=|-zy2%0t;OZJ#y8)Y(2Jh$FH@Dhux35X(Z z)Js1LUCO&?K0q*)VO?1}3RgSl!zjZS)|O@$3~1;PQMT>R0bXrd*wLBCE+LZGoF3Jd z6*wLk13F3Or&q*9+T5T}bcJKiY_r=FSEw(Vyd@nmwCScAtP9tSOV8moda&|z0QOv( z$4Fz|md}A^J}yr$$s!YdxVi-$@$#UTP=B3aANoGix7{N5M-fCpp^$y0r=M64rKS(A zEsYlYvb3auFJ^+9Nyw5QYNjI-w=+*WR)jxMYFqt!Sl@A&)mvF0*fdAUAuqWH{BN>)8uGXhX5jA%uz*pt+h%0Wy0cZ4ijg%uggt-+lv z=CNe?;F*wHv|X|XlYz_9*cM*y2j?VWDJFKm1*9|)xF;e6d^k&XAg{?IhS{av=|&Wz zw(3H~ha4GSE@yLas=2q0ztnN$nJPUbzqI`NN*So=gD{)v?7Ww|fTgjo&c_(FRW>%G zG{}6vx|&y?Xttc0oGw0bL(5g0xSLkzZv3bzZhf)ycMqKawkthbS5Jt|Fe)vI@&p+H zteynB!k#HJjM9rj(DqTG0iO|EWGbp-m)!1mBw+z5E-0JpXJN>Ry|v{}a`tIyV=C)( zbE?itlB?O3WB!1E4G{vxvH>i!h2P~efn*G6pH)?wOcgJSj?6&?p$xTM@JSt3n`Tq^ zpsjyZe5J&ruu`ULj9QJ#ed`3l_ec3s;g-e#z1r;;b~ir2b7r%{RM}>R<4Gpdj$d04 zi4{Sg5YtZZH#7pV@nz8HM8}AUU#=< zF1?f-Jr-fwx~&@MhUf*61-IdYZ{T{kne#XDXs}vIO!s6Ta-#?-Qq|= zD_A&Lz-*x>?`PqUi7U2AJyLivbWL?Pz3SHM@+^~r2!+d(6%m}i#uD{Q*4rqX5X&N>vM8ojgmmsR=$viVMaUPiR)B)cTySmG23 z3t2jphRssQzt#b!n%8xaWzkp+_*8g(rflmPd_d1)VwsHTstRX@-R%s-R@3wD>nme}FGH2OL-ulFEVpF+8MjQjX+U zYD+1KcCcW=1Y`0GZ~Rh0mU{k4WqQEiO(9vbUg+6O1DjqXSn0`yen z-z^@*R(xH6Q)-QrcJEJWV~7{}Tp8kSRrFeZ-17+-jq82$wWZ^0!WAELD;(a4pElq7 z>CPK>JZ}EMiP?Q%25-ckb0B^dZv9HF%aD(9jaV}IGKUdm=O6Yp(n&2Oil02TUs0l3 zigE8~mXQDak$_ym04(aW@QR#O8zina;z1wj%x*QGbnr<74iua}-bqh@6Rx4gfiH5l zUR!>yIZ#yVbhoJdq&LDJ*W4(k=WIu!Nre(tKd@_{i(oyfzLhY)rX6gekr}W@s~8iu z5$wuqt$y=mLXsx;6yXQS(#c(8TvO>64}WUQ^+q0fezW(O_k>ma!dRIm3?3( zC1rK9l!6$8VXZq} zN?)wPvedBT~kFK&Mm5XE|dl3)?UWf@Sk!1RkRr zK^62nbKI-4`^i`p`Qz4#sp)*E%B8&Kca(hYPk1wAo0sdHaS8_%15LG>wyEOAxY`k^ zT0X+^4bf)?05Q{EVaH+0g6ZZ`a;T@Gdj!`8D9uo5A0Lt!y zd19;%h;)=@mlt4wb{;@Q9t`NONfK3O53B1Qr|L;C5cYx)e7wY!KK6?X%%|CoSci8x zXe{+ap73SM=+$I1Ev=;ts?hx*-EQN7VylqU9ljwqJHN-?XSoKZ+ylGXE>Pf~U{kKk{z30lx zqeoW<%x~>-i_->o)r~sHc!X7+k_ggqgG7gS9IjfJ%zwUHFZF9a9d&UB0ChDp1RhM? zhzP2QHa_$FbAYHJ2Vemh@EVX7DA}7(GdVWnP^Mac-LgYt)HjQqkduyFg*kjFK`1TM zQ7>~(nS;kC=ME4%5fkv+ENB2rIF7?(XCs!(CnIP>h1uW_0;&DjcQ9;o$(#$3Ulrpn zF1d+CWMTZ-C%3u9sfu)3&@EKY`YR!EdGMT)8uXwVSY<`_`r%sTTd*t=M+Kkm>F|BL zaI4i|U)B%7uKRPNuGDU**^#t@@WNOC7EUbdK-SQjZW*OSRRaR7@=6P?f-%i*kDSyg zm!c_aI}Yh=)FH_Joi8sIA}lsDN7T=s&q;fgDm8x6TP=^wm0xW46|kZTSzNA;Oy~o| zVNVjdf5Jx+dK)5CJcv8i(}*(d4RGGrp&>ktlia9j zqCvZ1xQ4TJST>dZJmciJ$88DtSfyFeVc8#6fsr&k*f;9Wjt?l zd?(~(*rER*=aXo4eKtsS5Agzzal_Kw<8TT~5?_3gdlgiwQ?&dRo*2+L6(gtxcMD3_>X6Gc&C-~vM zF9#yb_=)FB2lS>PU0==N6;|1VX-Sc`_61tWT$yucTzDM@qL;Si*J2@5x*>AuxGr(a zz(oz%dM#lecyV(49L%F=*Pfu11^-QM`;7OHt+O3!QB`Wqgs+w@?J1h@UGn(QeP{rU z)NZUy{8LigXJ;p^JIDq;wvFNCXQ!oB^!SgP3u{#U9TXq`rLLtj#a!yC+g$7i9&q3h z3wml_RNeN7&n|q7&-TzeT+Nbd{h}i?fS-MBPY1Ckpa$Qv>0J&YlxyKVg=u-Wk@8$CZ26<*tk; zqe_)z`*St3fPG}yIsl;AlV@`|ReBMQOrP|PSfH+VJL9?)mbjy*(~vmicSMF%lCA*T zU(!FX2ZvB5jPC^}w=OUuu`iUPr-OFXm!Y&-uMyj*d;mHy>q%eBcsLn3JHi54yI@aU zyD{bhw`v3X79=R*>c^}brHEY;gZH_&s6PhrTpiOs8uyXQbJk)28MVxV+XtWsptuGH zwnbB}4(!Ybwza>OIPlhsfg*=hr#6n#pw+9bemX)bTSRjCq z!=DwhvG>o<4W`e8#RG-7DN&`wo~`{~XIXRSAY(E!t3QVC&_b~0e5;H~kODT7F;4N@ zLnmKl_`7;v&|gN&zEbX!H#6N-*6G4(F*sq{yAc@EL6mMCN0D)7-JN^zUhG~cB>xDO zo>6MZxGZ_eOi%*2emtLM1GgJUaGw1a6`YcVg_JqhNT{b{8%!kr=KTHZMi`N5A#m_C1 zEOJUh1eRH{Egv=!=$9I2S!U#^49v0th!}md8s{$&X)-L-SSS(_P+gD{081uI zvm2`=Urm=vUAEjA$BFTK)z>J`_xD#yG*%%JBUk~W1ECbe#fzw4T%GNlHnuEX4p=_3 z0=HItbIqu4I&%f^`z zMB8QQjVq0Jws_a(@wIkNn-|IDTC+WCKC_Sh8-1v??h-JwF7CWLsNM&Zh%72;lu+=T zf349>(PLP`Qt@hAccP%^Lhb+R(Qh%8&4h!8;RXHjFq~RU3ybr(Tm@Zab>Z(Ye_`IA zeF2{r%65#xJsa&R@HwJo`*>|CBgE$IVy@Q4lxuSm|EA!kfV83WhYIxp+#@Kf%Xr^; z?pML#pIAO>YN6_?cRNADiv_}YP`YbmqdPV&G`qz+KZ!(Q``{& zWad%J36H$C+9~&*p^oV;svHWO%~X z);Jm+o8plKN#A}<>Ugd2#YaK?1juqWP93`a^5O&ePJ6K;ljdA&+jIb4x(9bro$c$t zvcCfB6SyWRQM(<%B@**FbO@LtFl^`lH-~6dRgS> z%|BoC@Sj&!2`?NDOACxQi^_GBpip23Ad6)Z z&8Mtg`nokY+Uz8+K~_D%A#sq!Tt}?Zy<+1=e#xC6RUzf-wpNXc27lv%JyY3SsL6vk zU#)4R-4tL>boqk>TguUg(wA)QuEWVmg|}gh(Bwu;r1UI4Of(*5!f^3Tx>Bo zTUc}Fo;8CDJXv=p5N!)80-FOb45GC3YONL#8EeVF(iFqBw|_B1r5Sr@m#sqa3w+mBP5@GK6bU2^_5a1% zBq~LQ#kse(bMGTmT~XSZSDViKmzbxHnTM#K&^g)7cKZ*kwF2LGJT@I&Oll?Z?lY&h z)bO|dI@V}ap*wG9CDD1maFa@<`gV45ds0<*Voz3!5F#3x&MA|T?=cNjl<(O)Am@O0 zzo5U{Ey7Noq~O%KvYw3_rt~-RF=m`C+~JC1IKW8^v2U(b5qUbD$0U4XHfPkF6y!N{ zC+>heRg#TONg8}I3iz0~BlcW7Nse!vZ7Ap}6sNo&(pyMc_f1*+n8lWO4oHfNOTqtf zxsxML&a3Q;EG&oFJcqEqG}n@t1o@-~&^Q4g@lEhNt<)J#;nw>C-BtgpZD4!m8W)dm zcJ~*d>+85FijjSU?`ML%C0jC6vegNc)h?OqLxr#VkmE`$+M8V3QIA$TNK%{A{=o+~ z_TLr~kmho#MXCcQtrg z9M!U24gLsSTsA#?-vtH^-H*0D?*(A&R`^w*+tS2kLm7Ml=Qzl5gfmoiO&gAt&w~j{ z8{(m#KDy;5TQi!y@fv4@;Y$`TF%ABTDn7w{Kk+%+^Q7%PD$5hTJLu1AN0G7L16aZu zP$~+3G#LaE0udbMaMWBztyrrQJw+@EHxfHYqEVAQpl|v^JblXGvyc#4sm*pTabS7( z^prae1`E0p&PqHeOuV(H;fQ+sO^_*!ghAO5ITNGryxY`D0m8XKeoWLV1i5@hM~XQ-(|x28uKp6TEB9!H-~NW?|_r{Dda=O(MYZaD?Vxz9uo88VyP)1lIavNJLo?-`|Em>QT) z+`ElU@8dHQRHk5js4Q~K&quH6gkCe>hiHXDbb|lft^+GT_Zw{GE1Gzu=CGOpS&w~T zDHDnXahrZ41Z=tb=5(w*M0W8^!T3R`-V$*IM{qm-�Q>_@k5{UF{++!L0%OXc}!j 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

4S08X2sv8MP-EOJ8^4|R+ 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%0D6>dXfRjzLF;|`;@_g2Z)S~hoWLkhz2 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�$n3=eMa?i_n!Em$`fk(B^sWg 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%0DZXsd+GlQmOd7UQ%Z>aKmjA!o6$b zuWz1A-Uc8BCf=KB0dXxvA{5VjZ;KH13W$194#iADcH4Q}-~G1V1*)dz^>^lT0TXM* zLPQ^a)}R@4KJ|#7u3n+FjBhPkM5!AqX;#pO!d)U12qO=_D9-9kkz>Oo!953tJ%*P% za(KQ99@1Jv{_;*xDgyYgG^DCx34LFaYCJIK8XTtrhK0QM|9GGHTaEIC`wd_S`2&x$ z8vkaii=_I;?Y$NsAHlk^348coev6slIgAx1yK1==Gd|`^6eA6k#(@q+GNoZJfH)$DTa?CU zMn-}X^{dF16oCrQU~Gqs-W_|nR(i>(&e)hmyN9(i<7DN0^2&(ht~ku-&^SMEe96cETD&k9S{U#(|!oio?aW?t2X-H!3lGs`09nW`$XM*SNb6K?`N8?)jkv66fEJ< zLcL@o6Y{b>E`$E=muK-l{vEPqc47t7PRt7RUW$*9Nl0Q2OAb2$=60fCAqfVHWuq*Q z(svf+tvh5nEYk#TV{lNDs&levc1peSE~!PGXYh@RX(9u5{SY}s)&%9RqA3B3KH;>} zE0r4$koyOFZ%F}TOs6wh|AlHVH@DPD2*dUn9rh9WF|&bX%H&8qBlMe?ne27BaBrxO$LPqZ?c-+XB1qV6N^^yJEATCC1;cmL;Ol1q9BG>d9j76SKGRs3TN5mMRjy zxgtm3jPolLd`l=`#Zo52l$qec}|A<40PY* zZ~{5KZ6kV%&Bhvonr*`Eqt)Sl{&!bIzIqLUH`NSGk z!(#b9Ecd)aC_p<^Xe*(x6u`LSd-KjNK3d4vHn8r6SSN+w-f-y_*5XnTYXq4h_-j~q zoY65&=!1!a5!qICn|53+{mXn_S|#*BYFZa;ZyEl}Y>qPTH367VK!u9m%Chnc)_riR zAO7@vTt4B+L{z8M8@3Skk}z~=`(sw%Ck^?)LkDgEt$v?PG;Lq{5D_>k09s(pX_D>r zFjvp6@iGA#`19#@w=G)ehoK`mE$#-dd?e)AD3ln(3{G2Vd`hCuo`U?4y>uNsNBJrNA8aZ@antb?5Xd`jKuVb&2^Sd(( z1Zw>Zp?_;r5g*?Lm`w0=3fYJMp)KBnN@Q*_%sBx*%C{y{j?9cVC_r2{UgVHna%^il&qmY{W{esyT z2_}xbk57u8^SUCU8dH7B@@XV`Te)^XXR0HH&3BH&H zT$_BGc&1nqj6eJ9Kd!zxhlDEQCH*=ck2C(O%|7XUEWtwAbTV8Wo95o_7BmwxxAJ$c-X1!^OdOHpAg%?2lCzR7#ucWk$Q$xd#gk@~!f~|KyLiG%;~Jn z`90ILyv}`s@QUp3#VZiL-o@>>e6AOv+Y3_XZR-c=)USr;=<--a$Y|?3u?O~dslR^N zf@j@PGVy<+w46LOw!={Q`e3E-jX`_=>Q_mYMHpiVzt+V!ioc?!VOAmT-X6w)Lgii8 zW7Nh;tU?1lku3w>m8+trw#wcS-C@=PC90_{qXhfuN?SAJq+o@D7mCgiSJK~ zBtA+sryw%`C2l+wxQ(C%ldo~eV&RztHADo})_qZoNA~u2?bq4HNdgvrTy&|OaOQ3=21Wa4=I>fA+^{XJDRO-ov<2*F z%zTk!-~q!`(@FOdaz(?&N6HlIhPLF+&|tLc##;G%%cgJWf|s<*oI}Y^_@Jt?g;U6}EOlgm@WZ4se&V-S7D+Tc@cJ8>$C@9z%&8_i z(4RQAY=t3Eer|bA%}k?tak=9>9WLZU*kYL6YGpXPU6L#87pJ00X=jmyJAtTR@1h$}83%I0dTE{|X0akdq zLnR+}WoyvtigA#k(3Myp>--!EAb@}+u=lU=K-WVE*@F-c=r|sLb{{BButDIZTi5!!>i&Txe`4wgCk$l9dnT9@6r(A&S>u z=IkNqVMPk;M@PpLph^hPF%4~;xzL}a1Kpw`c?){LCpH*cSEov~#Ich%53+h&oD1wte#} z7qM!|@15)zB0hd$1JRxEK6aN#$&U^(A;7HNW0F{G+RJr#qt_Jg+*^kOvH%lpA>MLw zr1b>1U(SzXAc)H%H9Zwe=SdJuzXWHVzK53RS~^)GOMf@B+i4Yh+$#4h+Q8lDTzmC= zmIOzOG)O4s%+o^x`OyIRXaATGIAsm%E8BV-*l94MQud855ihb=#?L0z4ZaRpf1zwRYb$D|#fo`+9_&Bh%4zFU!TSlFia>lG4Qz`4Rbo zqg6z!d9<;+PhY?;e^gv5P_FGZMLW2%qV?^)2HfFa`MHU9zA=OX56CQ=rD6OLajC?U z{2sbx!zCe5blCZs^WSI-Z1-Bwa~9)ky^%RJ_moAQRB;nrkOwWme$HUOcz?xi ze{c;pPX{Ett?LeW)CGRmkTyiton#ETcZ+-r88^B%8s6NR=`hic`@W7u_MwrW*!6F> zjC!@%dpJxtO;Y$YjZq}i2N|`X4LA8h*HuyAK2r8osSCmnqM1MCi_+(l<#Sfm{A!i> z_dRZMKfM$(0pcZ`_e1%K2EN8=b*pAR9f4p2!?-X2lRsD+JJJ>XKuXshrT8M6Jbh>{ z>vC1W{fi8HKB<`5$l`Xnt7I0vwi>v?jPT-$y~RuIud}ZO&OrOEsW+!;#YM)4rwLE` z;6^2;d>&H;wrtOOgR{6Au5kMgtec1I0BC&3pF%AXn>#BrfYm3xbtZZ1VqWg3THDj3 z#MTAEM^&H1x^kuucG8ikS>>~EljS%Gz6`!Jfd2lbZsRvfs#4R!QxKtVeZK?WJ@(a%~7voBRmX+V6>j-U8(wB z%ymmpOLF^3iTRLRb}>Gb%=$rrdQA9QqkW@iuoJ)Qrm1&fMubU_rbP`DZz*?oC8V^e znK;I>RsddUWFl#jtcs7(R$nizfCsb-s}5u_TyK>aBB_P83k=M-@p8Uf2#?)}vhbo<@2#4ui+NUzlYSxJC(0D_zs>BBfO$qn zgKx8cUn=CG9)5g16m`=uN9s*N1(do(*XVy@ukw~I5Ahfa4ta6lI>T-D6>49}w^z2OxakklJ z%ufi2A7R}lV31Jq_fwJYs$8qd&vWdQ4!SmM(%Db9y!m@%^_MZyg{aYA$Mj!@!W<`T z^Pi$v4CVD>A8g2nzr#)j^eq;ad3;r+F+W%eZZ_0Md>R!?Tn=jf4C~q|Tn__#%+vJ^ zt4-#*?0+DeJaQ*3b@3IWdX@D5{{?$@nCKczojqDjcYvv3VrCW`h?*nxa8tYD zKba@`G{jAYh212B6?+&TO%l=58P43DkV7l>L@QSUa155kxQxU)J|Fd{4hB>(aWqP; zXD)2ntldNZI1GrMw6PvM1Q&X@u;a%uk>>@09nUNG&5Gtv%-d0iUV-ccTT#$9?Fpv- z>gM0K+>L_|lSy&As-~n9O@bt3CU{hP{Y{*Oni>LUiHU2(^KV)rw?fX40bE9tHly7hN4wgiEriBSwQlU~g~eyPaK84BOM(`NGZ&m{td!79oA9%=Bs51+-%;~J=h02+d zy?{H#^|m8U%Vex{ex3B)g*=KK?v^L-RaCnop(Fyz$IIwB7JsmJyJFD$-VsH%=64RK zipOl@we(R%X=u-l%inyjPhFvmp08!obYp1AoPYNz7bc@q+bQP|*NMB(l#crwRh2gd z_NgfIm|194fvWT;#kR%;fGm{h6}DtmYAebmeSwJ^&ip08c1al#xD+g8K03at-^09; zttt438Ah_}&s!HSJUw<5pU@D~cZ0H>R=ZT(#;(O@H9kO&9sKeiUXs%V^64V$?Fjr? zT&otC;m#hsN=4lXA-1=3HoePwp+nobUO3v!QFa*W7O6Uj2qJK3tFV&@T@^iO_3tsT}xf!)2 z-Eb&pZM>XbI-+3aiFS=|OiiP#Ls%T+R^w2;K}KJSR8 zBqLx1%+%f}f6H$+PE%-H>#x$u!|ucpE#eo*1tTdc;hdm9I%A&_kj~Pf;VM{L!n(?N zR7v$4yTb=InVa1I%k03mFUh1@Lsa%MKR-AgExj*uankFZDyu=L9sG7C;N5JnX_h?I z;3Coz`*eO`4#~RhDN@a%PJoC!rNeYkUBi^zZ(ls(Z)ze3c(7#$DO>h*P{t`KGD|4> zG{+yEgSAWws@%S6Nm@bOJ$Z=3TG*Za-B=yw*K4gj&7cKSKZ= zNmBY|Jq^VDc9=bbK#R*BYR=ow(aPRy`JQtFo%xR)JT3LyT!e8llM;q3i+mxwl2W+& zIjWkn%;J^3?Vy*Vc0BlX5wwu)k1Q@lbT0zYj$x2+pSx$HEEOz+oAtvlK-R{|vppFituV5- zOh569dxyx1w*I+`8gWSmJ}G`99Alt6egAy(jO85&NB=u#=@AA1%xqX1ZyV>BOLfsK zsYup_-xhRqy$Jy$$n4b@0KbzNSWDrAcfm7PW$l z$W`OwP5++3@A@>^{n--39tkAte~3w8eM4ii>B${wEuJj)d&G!fCskwoa|c#5owIY# zcY>pNzTATC!rsQ_h;$QDp_ zxD_j2RQ9K2`AlW3y~2C;*)P7rK8&3+atwAT$0a)gZ@d~G@cC%w3dUJ=ozN~ZG*y?i z+q!pdE%xo=VC!+dLHDLWSFQBtmrc0sSKfGpP44^!==+`51?CK4Y>NSs3T|^v1zE>K z-PSEyvk2Q#1DMyXU-M;t$+7ZmBSvqHgjZS1-fg-0ty*APi_ugZI#Hf}lr8egO1onY zjAxFEJp0D?xSyQ;&jOB+&G5m<13!ki$@r9mPpw4YwXdKYld;Y!=v^F1uRh_{dN|7S z6Ew8tpE*dMZGIN9b;;P9KYtjL4U!yb+bdNCDDEFZpl!(7hpBnMNG@`>jmDFtx%3gPT0Rbkki_HhO+WGTrd?lR&ghP%pf^81#l!H_hOUX6~!O zSirmA;NAxLpIjaJ(Zt?8QK5Iu_=O$U@d`Tp?MgA-AN}%@7(*< zcMcscWIVLal?==cCw?0MWb8D#^jke5OQY!ik3Z^w!k@nj=`L4h{^TZ6PLYf={vk|xq!afn? zQO-W^W=Q>=5PLE7Q7#xIet4!wB|b&496xp}mU7=k7QNFDW4^358_Kj`-~vX}a$-!i zulmtuTaoV;sIgqGZ@#%vG_5SgDK!ZBlMXh@CTw;AtN1w>7dIQw-&@0dwi)8jPNu*7 z<3Gkh%qCY1T>!DbaeN!9z3Hq|hfnW+PzAc&kgq&6S7yP+i^h`(i(dO1%L`6-Eugk> zg8LWW4xzZb<_`dObK&HSFHh({-6e1|Vw+iNqlP_dYR%Hfij3>GY0V^C_K)y5-jpFL z001BWNkl(JeQ;G!KQ`Y&@yQSUhHvq6;Lzhq_>$T1LuVsojDkYN+YKhM7eN&V`4DWaCA;Q}&XMeUH`%H#E@H|5%k{bTZo+>3X^ zkH9;6?W^wfKX8uVgnON;em5Z0^YErdPCa+*cs8KZt9ZG?(Ga_?WG5p1UuWm5YXxoN z{%}Q<*7>saK%G(TvFnO{8(3o5e7>H;O4oK$SG;FuXbar zbrb2BHN$vr!L-+fOS4UAN&*LLU`Mq_x3WFoVSK1fV){)Hlh-6K9yftP@dc_rcA+xW z>C>Jtpv_LnHz|}eH@@*`M~N<^RzT3E`7t~5Q@qn-g6>STeCV5G*#rbU4uh=vV6n^g zN(##-+uCMii?2*RvBB|a^V#C@%O;!L2jp#j=j3buCZ8x8>mNr?47i#dM5~ya2K{C# zYV3DAVhj@UV2PkbGw1m;D`SR*^en zSsK~lL)2J4w}1yi#I{h&S*&Ud@Yvnp9GgZ{hbSQP(? zZj|Vs7j31nSgzmLj4Cd^J{zESGDoKKnY|0j(hs1wc6i5ZpLK^x1wd*4-dRLWyVxkr zOtew?sDVAdRL>}RQ8JM6wHBRTaU6}^@X*&~*27hS^`JObYCOs~WonMVu&kGJ`o3hu zpDpLL}-zrSn&fI3}TR?YFZIM>d&XN~K!WA|c_Z?f`G=jhfWBCh1}kxG0Xzlt(S zZ`Nv3zqhT}4f=kp9NFURI#s7&jdSlviosLU&3iY3;of@k`N4BFf!iHSR3yW3@8W7V zGr7U`#(~Cc{dWJRUdK8+s5`dqm9)mlEz-UtOpW|11}zGHu@3uKVBZ~4Wq$O5O)3>A zp6uMvnLzG>`(Ld7JOxM@HAF`_a5s)sTnr32iUsy7)Pos?kci>GNq4Q=#L%Ehv4X(y~o&gvMAiX!Ulu^^Ell8BQ(w>A?oeUpYiY623XD zniI5Ltm4CguAY<;+!t-fUQr~y=O(;)p@jg7L9wk8`HYR5!0e8?lMYol23y**&xKBy$)1rSokE2|myD@WMz;Q0U$=IDv zWuR-~XKR)lczD57r&H^Bpq+YQy!SH|v&y+G?JXiyW$W2ngU?>ZM4j6_AJGFwm_ym^ zNjiT9&`BGLr3Fv^z^AXA^QC-Ik}{)L=haI=V=bcoE^ljPv$?LwuFAhgc$-S-Z7@$* zF_8K!C`P-kbQeLJ>3d_>*y|LfbhUamYGv~aTeA23ag+GQQc?SY5O|DnzQtKc{qgAq zvnM^-{OH5plATU}(pTT)7f3A%^}uoC8oV#oYkbddrR?(6F`R65C_2+Oa#P?uIy~C2 z?%n3J+mOh?d8R0KYniR-wfl=EXMv@27e8VSSUEqF{_fxytNf&2TYYEbZG6&|CTpUC zn+aM=g{H>tB6%uHiZl7K_fuW!^!?UkP%56r^K4Gh7TTU=vqxL-V5D|g^#+7PYm7W0 z3SqAF#TpxS!sr_3#lrjOPQQUm&zBbMR=xZy#t_n{SU(H0)!51LBs{+QySPa`boD9k zJeQ~62Q5~#^#__Seft}`J!iZx721V!ydcRzoy7*8#-5{d=3QvZuCHdY$nrLp(vVlb z>rlOi=ZdnmY5i|~)81D;;e@b!dohc^OJHM%Io}~=aq~9M$7iBdI0}5Fe5?+Qu42sK zX}@^phE9~e@||cINZlqJ{kuv%m*3BVO6!aSgR$Di+8bfUP7}OH0ylu2$Lwnyp_v;N z`^yu!+e;*X_Kx;DCajhO-T1ej-2BwIP9^aCE}!1%j^|3{<>SyMR9)InTe))Iy#&qj zT-&%Pb%WZv`^~^U5&|A7lGk?;%pvz^24lIx#WqaMdFK&asrx_vum8chN#k?|Bg)!M zw6i8gVRhV}`ALb=l-?qcfKflMuV!3-@wTIen4s`_*Ab&ykaZI~rTbqVw5*}aW@?<+ zULv%RK{t){O|SY4mO(VCJaJ(F@?bgqoI;}RZI;fQ1up4 z#|$>*9J@ZSFAki2h?`?m!n}c4PkONO>2q8a0LPP#{$~^IEa-)PSybjxarjy|ASs>= z0OeW7EdZey6a=){(xk4&3nYD2r;Cm(U17#5J(hGx@~OUx&S7wiOHQm$9{uPFl!jfN zh_zX|#fug73=AT5<)YcI5Cr+9H?CC%VAZl{d;FqkmcXtRyFj{el3zWY zjZ*UQXzc8>`^wf5duOv_`_{J?TR#iFoPScY%9cEnJ{s8hd%iXxy}a4=FlLcmn&S-x zH?i7ic`oU%w2K%$%AEs+?^}GHko42Pi*DM@pKlBW?OQBi+4+GZ@OI1H(--7D`OeqH zwwxoLZFGvmtTR?^D1|b1w_d;Tcv>Yp9GbfsBro2gOIsel>$4uu7HccYzNm`%a>F?w zSDS;&F*(=Y>;?XNuHYWMFJ_{DA8*a+Ldc_?%2MfuOGZ5FhoiO6cG*1_^d-ZDrC==p z`C|e|u|a9*#I`1_fl%@0!&}>)eQOIm|3q89EXwxi+Z#7A-Ox8a*gnar(unXlzMO06 z0^_S7p|KpEE%wPj9p*7Cz8E-)`9W(%lKmX|co#=yFAD3Fq5Sl)IMG2_-s!0qS=}W@ zhgj1IL4zNbtiPZe|*6#~( z{h(}cP|j}noh+ah0OPxj&AuJ|8<+HIw9$ia{5aFiMwp~J7`gOQKqw) zy1ke3uhA8r}4Rtp8r2(5{iBCJ1+W$ zN$GzJk9g8L3Cd30a8;?#(W2#s9y|Qtdy_2RmT1KcAs_pUTmbMZf=&UoMe;LgN@6ib z{r7_w{b%HJd&%fBru;1h20~1E5^xf(p`28sDcdbo@hy?<3k^Q947ooM4u?1|Ci<^F z{~Y%~FLvYgu3(E-{a`W=KAR^6^c6kMhA*cz((;|QrWTb8c8$~+Id|rp3jn!6Undjz zmtV2ZMr55bU;0)z4q2^Fk*4HDL8|?IIpTQYU`1N|C*~+jlD7W6yi}g1^Xm26w#efA zwyC`4*_Y^XFDHjRTNH9>Q3@Fk6n$4tPWdEnv8Q5v>@sBcxN4T)b~h|C)0kB@R_d>6 zP2ZP2lB8%(3v6w3X9>F)Z8KipXlZfu=%3c^=p47pg;*@>iwX?oAvE+1hZwMaZB1}| zv;tY}s8&l$yJ+(N-Hv0OTWzLyANctQLDYz^c5g>P6W5#z|JFM%?9wSdmGNSm@(f7% ziHzecER`GI*{N)PgIdJk*&W#%0T^bA7>k+f_Yz~mAD3KfdH|KJ z0CWxtbN=a*AJ=ofZ1ZncsdMNm$_B9Q$pD<&e-EQ=BEJH?vmSn{w>T&d5BHBJ%K((~ zchqF(@xK59;OB3ICTq6aZP^koQ|L zuN-gw!sugX>XXK!3nM!jch8NQR4$tk&=1-{sBi1mbfroGu{bL4J9;vTU7o-ci|tir z%gnhx`A-fZ_fNW~FU6a#`sI=4k)o>B&&L#XFAXgi+MrsWzUZp*9@UX(hJLj>L*cmb z?4Je7m^ksWqQ4$`AV=HW@!X-!ec43?{_%7SEaf1&ip_24++aEu>e?F1{33{Ac5{S) zx0k?#)-4Sn&Z1iNG?g9{8*#CEOT6`Z4l2LBiuP!YM$PH3y%(c?wq>a-1jh@>RHh$Y zlz+*2W5d(O58sY95C-rqD0Hi?VE=`vi{=UrV{8)*<|U*s@)86b*qht%K5*-Zndv*X=&l=M;&zufkke)i$I)vlFw@UI6g%}=T1Yr1msi>QFXJJv`Z(%K z8feQSS$(4Ustt!6g_me9F7JyQ_IIby&f^!=7%#&nvxvqNX4oj4PB7@QUY=-|ARIfX zpu`;N1TpZPOoJ;@*=3jDUif@Y;9_VOgS&60Yw;rdbSh+iw?GY|Txc%PF09}v8IR_r zOnV_(IQBNx**6lz_-3OtuYP4-?5!FpHwVs+D}p<|XZs?Vb{IVRT+FJSPUoc8yI!3? zp7cA`J&f^e(F|>WcI@*%_$ZyHV{DXk*j(Rdt(8IE1#QARrfoXT;l@DmGxhWk=LIvi z?joG2#=!lF@#VnUWkY*j77feLIG)=5=9P7S7N)r?Lq%Z9r|J0Z=_k;>evRT-j$Pji zfTGmh5NVUSX=yHLQ!#*tleH(~e`@!K$N3@i@l(}53Ei0{xUmvL)%IEYCmdg_)gE85 zfd6;CBijUSmno--nH-$%PDa{d#i^hX8HBaFB)tF{miuy zonh+U`G^Wd9(PAVnyZeTHY^g_*fukYHd)kp;T!D8^`*x3fkzu5wb_pe$s0h+9bo&a z*Flq5G{e2<14~x(*z$Z5-}or3r*GT_yH|O>%A-9Bt2RQ#@16Y3Q_5bAxvi&{YV+}t zz{4>gPj^v<-F2hQFm-(&0&+8f_ST6)L8nKb-}#!-h(xtRV^H)R&-Kd~IA%iYi>31s zjOLhmU#{8aX})W(&zCp$=!V2ajkP0)tm;%V;D zE=C$JK5N5rUb~dVPJGU0^4}2gP|Du2;VG7L}z!!PTJ$k*cF8`q~ zKLxTN{{s2(c52jVm!*$_sK>VIymMj-Zbo1z)dku<7B@CFO-iM+j~4(m$ZvX9Xd4Wj zb`iJkc*|}8eT!O!fAhoUy@gG)x4P|zOFcPA#XF`MtPCK%4N1QM|5St6>QcyJJ;%`| z%>$w?NBht!1QVa)ozfQNH9HBJgxRJIU|IUo4#$#8HIRjN68knm0uc1ox+hS-ykis9 zka#8NotlGZtegvQLV5H@PI@%P+L+PpxAy8HrkX zDw5eEJ{ciD`3|$OQb{pX4u3V}$+zHRury2DC~(m(&(J&1ReW^_e)dKYeKV%Y4R^?H zz#TKX>nkQg#EWkV<4Im=leRV%(veetJRAG);d*zt^-Kd4nGWq4P8)Re_jw;WeJKAK z2kKq$qz@2uEhl8s-eFr4TRb~Hx&*`7L#074G-K>0tSfhzV>{krm_C_r-lXop0I-9(5uQ(twQ*zY$*V7&h(MF>=S}~_2jl5x*0s?3PksVr zStla|=PZcQXfcL@iVfAdOmh}_oR!O0Y;XLK%UAjsH^ho(>l}`XX}_NK7a^#7aog@= za>Yp91{0!rV7#)0&QDyS2`HQwg|ms{@4xF(uKlOBWShL!jp)4ZqdCcoAma}5Rkzt% zMhAY*M&sfLiH{6_E6ano=1LPT>o(Z4?{p|;^?NbR{B7AxX>*6Px-eRuEuD2YSiCIG!%p{&xdZajdH689x%k!ZPb~goXsWr;I(a^4qq-cEdZ$Oa|AN;TIq!SN z6F%P^TWmE=Yd=Av)+!4LEO}9={6=y5#j@m+$G1jO-)GplR^WA>x{Kl%=!F`re2p-V&We#0lo^#sV+wqEmzrLBI{M5w zqaRl&ek(~<2y+B`9rpE0_oSYG)V;&Bjgz~_OMEAz zhEd+;HGS&TC>91M;r_%zC;D3`l5#B6M=TCUX4_I1Ic&M{(3Tc7Vc>Wh)nWrp8^qIY zi%GOXTiwy2T{Y5ige?aFaAN648(=YTr97anhUb%atE9w-(6l~yK)~82Zkuy`Hq?FFnZ_8( zlU-1^=*Xry@;Gla*?PH8pGeOQvEsqdG^oNk#o>F&_T~(6s8jA-J{z=Txwr0F))yIw zU3Wwa&TQ`DSS`keT?$>r_Qu8wx~}p~em|Gg7#Tn3usK73k$D%QdmAnb^W85` z`3{G6tQY}oJpICMgoOE%n2@>jjonkN4D(PsqZb^*GRVgG{{TH{0iF3Q!zIyg%j zD%U5)_0yw2`Bc<1gm`#1p>F<`3x-N%_nJ9bq18$XG%OCgARJM>YPm%nQfH z^xy?>(r_F$crPb3!ah2ub3V!|)!eqbbyork!zOn&KcUH^j#Ij~fVcj-n~nSTIV2olA;q`#lT@VuAU+6^u@n+Pqr!1Wuz)T1b>}E z$65>eOq&}s&NV1^{rC2?c6^_=Zm?`S7u8=*hzXw~=EuFH(fqRS&zpx)`JM#&)05C* zt9y=HwuwP~x<^x;KRsw571I-s?JWpP9^tQXHKt*?J}X0MJzyZ58|s=wVLSgFpaAK^ z1L`&nQ7gDd!fU=u^E(|LRrjZBNcLg{M18PyfoN4e#;3Ujs5d#>!0|5w`!6$|Q-9&V z0&YD)&hw4>5OwSAJ4ezT*Jx%y9tPO&sCORe|Ns48yh@lbw1Ki2|RmP zr}pBnY|{LOacuTUIo)wP(LxM9d4K=X=!k`8**iyfkq%Hf1dC%uvEEXz=Gxl9VN#GFh2VHaZlN`+l1_n1bW01`wgfA ztve1>Z~0Jf;0U%0T`@Pfa=6MkVlKL1_G9STD?-{zm@NueuO0`=J``)edbYuu$Xh?O zB0k9djKjsD$)|r7cPRJ(>(p_a_3DOt%@u?5ul8WFc2#*TsI$pks*x0q(5 z__E}@H8D_`LyoVeM8HS=a|j$m7FrXGt}RdpWPeq)PfpZM%(fBG3{S%4J$ic3Sj=Lf zj;1e2C9b*HWfInGyqzGkNs$+4ZA}P+K2Q@JzWo+Er892&y9)!Fa|D0Ph_a#>I!+GC zg@_{~e^2SboyOT<4(X3RuAfG6GdC^!+^*Ec&$sApq*+l^w&;`-c7WAHn!Ka^yp*Ee zj_A{kH;2Au#c^@C+T^ns%5dxKh4_AQB=x*ZryHM3tvGLluz`Mf9OrUJXr89F6@4=G zMUhvP9qHZ2PRuQ`3wm=)Sw$^k`65O&B#EVSUVh^zeRdgl+CF)hua>=5Ek14Km zyjZQ}2&+e4G3j^SSLVSrvS+av_Fh3n$knC*PyV;_R+e~8IjPAnhK#|$HF{qm-0ZzfjOvi|i=QgV6=EsP|;OrHM zqTh@2*G+?Gb0dr2y8d!PZCQKZ&Al&8VY(I=+s^gjLq<@V@@`ndx!h^^#KrI8Trd1E zy^T?F(2ZBB?Q5;TekMjYsctQe9GN5@gp@amJCoH?wscUsWup<@!u`$;1X4&i3zU ziRnMxdyrq|uK!BY`K%?<^sIdneec}WfY8wfmq~f{?42Z^KXe79C>-Z39+lJm>CH5sIiyy*rNs7(?(M##RH>& zs81ovKYP7JNqgR`4tH{B|KNW9+k$eCRb;_abtJc6rS&v$a$;lb#{ZDB0z%s5AtuTUQ$k^zOG6wX`7ApE9xA&@GOCFc-2N{iVnGY%t$> zy^|YA+n|bKta&vLcud}Y$CZZ|s?}`u$$RdGtF89wk96uD<#9vhM?SjgWbBOr581-1 zru_0_w!u-HumOft;I*# zxTaGHi)IQHk9X4|E+?d~IpwEv;}HJlX|@sU{>KM+t#TI&b8Azb4q%!~3t~QeVXA4l z#JthY$VoHngj6L z?W8WABV1%RuO(Hmn=_)H`DQ*12gIo-fAQ2H|AWI&BGoFQCb=VqWOJ;Z?4MnlRKMz3`i{b7mBSp>N0Qaf}TE^ZoYe-`SCyh zXTPxyvHLD@rW*%MlwTR-UCC;}-R1=GSKjaM8`ivM^G}AWe=~%$eMtorgInR)UCBUA zEEn@R>c(|<$}}9B+Ale5dxFyN5|8Q|9N+#G-9V*A9@^Sh+qA>R(ZqC`w#SPb7FURVECb3-S&gI^8dnZTx}HeC1Z<&N({D`JlhnU z?b0q}a+T2fWWDhf?n-f|0}=9SC3ROfhT3oJK6h0yuu13m!t9;q{rE3!5+w=n$^~Db zXpb@R!~c5gt28&T`u=!kH#CD^;Vlv%KDnUI>(PeZ6#yffd6}1MOG^h-Ps<`|YbRqm zG|dg7b21yAfcV(@WYwpiEws*W+Or=bfqSt~x7b>E7D4gB#+X6dW?f-(hS9$~HC{7@ zKDbvFW9vI1qtz~L4BbkS5-3_V7%e>LWaKQ@dH+hE-NX!MIi)qbnui^KcIT60c|^7= zbY{8md-0NP;+|HTt}dNGIc`pw-HV$S72bv5Q+KHq&Wb2i3ADq#STRO+Sh9sa*NaCB zf40$$PV~4q**M@1AioqUAAwU>79@vAeu0Jfa`5bct~&YOYk4NsMJS_vQRkZSYm2#H zX`WG)?%bS>)T3?O)JfOks}SYIdf%uAn;84UaywxU`&3K-(k^=)!}HX@%c^-CH%h!} zOc0#J=*$&peq;kR#2NYO&PhRFcW#wTi}|4MqUwVTPr3jRh=%8+ZA@r(@_+$+O*m+4 z@E?up<_tE7VIelnag?^vg5=29>sn&o>Yw>;k1J1|wH$x7L8qI4TGDv8!I#XlLp*Q+ z)VopWmag3EZ%oxwe4l;eQoIZ76oaA7NbYPK&|V0BI>dm-^`tvx$m@?Utq<5bJZNF9 zb8El28%=Fhs2t*`J}H*$CH|fd9plfYQzhWPOBBOw0=^qG`ZN4~5|X;-k@CfXF<0Ur)_6UK)8^5-GV8EF?Ah73ud_ft zY&*=i7s@sTs;EXi%tPlWH1QGKIGC$#G^K;qRT~U3VF3Y}FKXi9-O$z?Z+K|y6nQXG zhRe8HcYNNsP#v-Pp&sV$fBxrx#AssxRFtg11W~|EfDOoupiWXtFW~T*6pF@gO{aN= zp@L|aK+wAjW$@_voc#TfVpvU3?a_GeRJD_aU?AIQxxq83g{8uMA|VxMBn1z_(F`sO@fErJAW|tiR7fjheWA=CAw=vih1dW)!6>7W;fa>X?)8 z(b5fRc6Jw+0XCZYr>ybOJWs}^rT8n0-`||!iarqXS+wczKK%=<8&mNq(_byhA5>cq zSJYT5dTJvXldOZ=u>WgGTA3vXv2G70S1bt&w%XcY>*9dREhicQSao zT;$z2o!RB4IoDdEKiK9b+E}=WP9KLNK%f53d@+1rc9e~y?PEZOmi&$hM`L0smpFRj z?IZ{Iv&lR3op>-nTa>kU!V=fko73J;{?x&i*Ku!cp)Ri{1IKWiN4?0C==2n~1C@y? zo8p<7ZwRc2f#~kIi%fC-=t1r>K8t!TfAU5dDcZ?P+E?HIzI8ZNcZglgp38NV$M`UG zeGE;|%3?1xD1yLXob_34_p7>>SBk7ZVHIbauNJ%~c#gfX6zh&*2o*pN3OuVR-YQ5-oD&lfuE8zpil zDe>AIVuSMPq}rxgUbo$BJofTadV*06sJOK*RP*Bp^AH!524HDX>`#sAI%GwG-Ix4ws5p)%~vi- zSXM7{hw}a(x~=|IpXW`@S+Wn4Hrw0~?G#sjbry?S_@!ykUy;v;W%2zM3z-|&@edPj z^rk+!=`qj5F=oB_O1>3G{U+Adv&KLm;(0@q&O+#LgWqNen(>I=aRJYNL{KU4(-+9; z94|cLI*!Mp^a3^U4~AqK`B1EEz30NxgQ*j;(5^`u&PLTZ#S6Aa{W6_OPJY<*ffdh&o7l|s}50qw7^Q7e&cGiP@7%j0!bN2 zeu`~*uD;!+ij{QjDiuE8W~c!2?ks!%zpF|0(VYxv{m!AbAZIaxPIPWF9l%0XT zKA+X8CS&QbjXoQ6^Q1O|8a}u$U$k+M%RiAVj{jRzMqw<-UvuSqX?P(#xngM2D-XTA zAU@h=dGDkw=FndU{f_5<(?;!OhvajqhXz`0VMT?BZ1%3}OqqeE4Q=V28n3 zx;XMzVlhIp4%cM!bkLnQNY3{*jp)xNY*D^%&r;Vs-#zi@)A$LMNz|hSeI4;_JgC;M z0kSl&(LW3*7;GL1wz-b2fR``+@D5_X-%HyIv3-_Z zuaFYQ<9p~xfklN?9_H|0txXy*)fdHD+K?^SwO{ryW&tBj7e4w}QPM{}i(Pg28xwqj zad)~Pxt11OWG;tf!<^CUqYBaL##&xIhmaXh_l;@V)kFK_W!$Dj;DoGvig}!QV)`dE zgmD^s+s^kDf01MPyYp4?kXartrU7vJc+S_lKE4Bh=WA`!j~9Qf&hqo$maDhx>AUf; zwN*KdBWFsa*<#fTG4n$cG@==oLynAwIA5f1zI6VFtY67tkh1cKl8uh{>9g~#d-i$0 zc5|GcwEZZEz8SM(i9KClW7uLguhiitTmPmJM#`c@Ui8gswPH1x@{8AIZ>)$>(P3>u zUpG)(yCHpUp6LaBexA}&jGc?h)SeCrt&dBrPrekT)*d3(KvN!miAF}yeM>2j{bl0w3s75ez>?((myMpIEU!3L1y1+1 z>D-CFZrIR|P_`Di@i9gv!vY8oZQUz`Dgm|&O%mYQ=UaZyM)~~=i)EkvgYfJy7xG8a z7E=?iyEw4zYR^I01bA`|l8I%>E$r35H~21MOHysUgrQ&F$t?aVSI%$OrJw#b94!8h zhF;8%3HloK7ry~#1bCLjP-%A+QS}V`I$to07wXVy*F`8lFR~JuvdLUy&QjT2L8a ze*nEaPu}=K*QeuYIe}JgfVW9G8$ym(EcF`y$Ft9t;(SRhFR-EPk>n(){=#nEIuzgXqt?7lG}ci8$VQe}(N0GswHwfu;rx@~}b zBCC?3#(_RLBnB_fP8+^Df4#Qcd-Kyf(VyzU$8wFQkUXx`6g#=H_An2=&eyJ={)bNz zJNb>OM{l#rpNH1?CQsv&%XGeU!bRtC87w$n^Ua4I#@)wxJ{WEZWvZkvJ8eZ48_bUR z#z?K7B4)y@zf3GX^r;zehS?O19@sUE;J*5OHA<7w%{Dk9>!O@46uk@xkzNb}a>5#3g~#p-mW&D-ezrF@u=f=whr zqn3YX*ZKpu+=WB)v&$xU8|VChN}?DGiu|}eQuXP|uHr`5z6Zzi<}LWXPLAuBn;g8n z0#lDUi7>aG*(cTT&4bGBgVay{KU}#eV&p*b$EPmR!S4-j?YI-x<8iuvQqq$>(;<91 z!{1W@I@a8Upv8kF0k#?SooMyTLil(bpWofc$>6h*F;gJ|T5-oP+u%JMj!V@zjE*xx z;ec57u1s)~Z*4ekDqZNI3@vz9BZuFNJxMXyGX@zM&Yyq#YYuc@)bV7DBBVAEHXcii z1bis73e5W8UG%o88Gpp-$~nV_!)SQZ_q1QILpZqwD~9*W>JZcIw><+Y9`wW$tQY;x z#!{?IX7}5)O&2_8X*7xb`Ko{l_0dbm^c!Cv65XIUdqrcsf-f=*DXDg;9!xvg3wrD{ z$$U2R*<~ zdhIGXPNCB46#lGu0f;F(;In~zxq@i$+N*4%<%B2ecJ<*zV z`DgyP#hwli*6~|Ro=z60ZJNOGjfHc9#T(hiRXSua-(iJzi-P{~KDna8p1c3YIMdxs z70xhXj{n=F!3W?s{f?Vu;Pm1HlO_@7HBiP#jrU_UcxT@v^r$H}r(UC@mA^Eo{@iph^SjYG{-nfa0dd!Uzc|G03Urg$?(V`=tiqw^xSB-V* zn&K4VH}S7^kZtaY!^-*028(X;UZXZ!PxbR% zr*k7Q9wNbL?Tr(>2QpTV-`QS0IFG-J0gRV_yV91w-k7}wcq|!l{`{IGo+~JIG4{wB z{(rj6Hgr+;+xXqcX6axZX=+SEVRd59 z&-e`b;c|W)eU4cK_(Aay_bJjmLq5SzHtfv_pFZ0iucwN0SWibe_JQDzc+4gElNys@?Cw_ zs?Sx!4K$kugZLG?IhkiVo_z82AQv6}`$1K8dp!8E!dQ6rGkPao@mo|n$}9B0GjJ0^ zAP=zJq!ere6=82Xdhz%qC#B3WuwwyvIFSz#dmOq*Wmza*d$i`#(E(cB>No0~mOMm() zJk3YsnZ&Q;xBNYUu1r9PzVtXUpP{pFv}>GRvlnsY-CwqD3@Yy)LUsAH zLjOU%cv}Q)OQ*N*;tj?^t#7G$VrC`OB6<11UlQDpb;uGo{!#BOY9$B3O^%Id)`?z;coRo?g(lSD<=sa=)h z8-OCwOQoBbaH&A9OjiA~VB(&<8vQLNK1^c~zj^bE!%jl-@zZd6hj!YO6HJ~y=R+(u z*@Jz2$M&ZcFu=T$EPUWCk3pY*MjIaH3LC?x36rIcp2_=;*MeN1j{hh&O81M^u{wX$ zFMVP?8~FaTnRd&Gei*sD-^ok(WM3(v$@u8O#DKnXX;~bEBz9z&R-Hb*T0yYnQ_`&e@weA+p>U1Ik zo#xXY#CkH6l$(nZYFyg8fYV95HctmI9&Rip2hNvBIIAvS9uB(riQndr2{$K<^Pm3K zmP|jf2S#Og25-V~JK& z?|GVlqYC&9v@zxDI?DNp9*q4!GT2@toax86AXs%&A*0G`(R-7Pvau>C8Av zHt(Yiwivgitch9vtUa9S%Pmbp2EXd(zE3_)4#%2fqc6Ytl0WQ+uyz&P{!1QvX!=e* zJ5D1EtDARzWY$)8E75AzpKkM2UUnosxW3=Tr9b|*&i>|ppz+0jQ5hcA{jJZ%!zo6v z+|)&%b2Y|ipdb+DDqUOF8MUNC_bI!+Jm_V^u?=VFCc$kq;{N_?K_UoQqWyIm3&Y?y$cTX9#~*Yg;|vq^_;(GF@LUA+z;NcX_^@NTlHQmxeyTPR z@ag3mthme$_+;ChPe<^*MmF21BDdzL2+z+UDzX3BP}khTlped+W)6;9w4~23HD=oK z-(o_3ymxuvyg-j>eqBE}@X*etcrQfwa%a0_=<;lv!y)$(aBR+Pn=@eET*IzIxL;pi zR_%my9!ehGq4???t|o&Vm){WXFP_28L&1)1gk8BIAJ6HflT|LV%d^TF^=z53%OIq5DK(_@Nm4l?2v z2lxl>aJJ0GBfXP22D}p7yt1!rgVA*3aIuJTyl(E_Ib<00bFZy@`PY{i=#=G8JU8y@ zzmwc-iqUZD+h(#p{vEGJJPsNmg-wr9g=a6Opy5|IrYica!0BycA`kxPh<4wa1-M9)cOIy z13&!J@Bb+C?<;BuXTT;9$m3h03nHe1P6-W5VR1W&P-;7j@#EyUZ1o*pI! z{5Gg4Hm6E~=tFsDAhVlxlCAfWy|zu*9KAB%WZm)9> zL&aesT)k#TpWe7@9)CV_4=SDg>Xd#z$@6Y&n&-8E~*&BHJ*)nbW;OD1rqbKGQiHMiRw|Fb}oXEp_ zvX&rnqW))pic)58Co&Iyw@C0G+|vuvJzujU-yq|U-aFGx4=OEVhW59tw?et{__czVffmUK#U zIK=Hu7W?4&{OD+>*r?kBt=_`8z zsRr-;-<#A<#~**z@y5-wZF+^jF979``6d2*IX~l<{^mc( ztbAl%UEa;gX!#Tm!sv*OfPH9x)dQFghqKrOyX!w>eYD2e!~J;RE`Mh)88LC%XojAbA{CHKjYWjB|cf7n9a z_CSzAFhaK;)UjNNw{=8u8-Oyu9p&0Ir7zvYVg7YYKm4ixJW1f)uD3DQCj0@6#QgY@1zB=msv-U%c? z=q-d!s3G_LzTf@L+?o5w&hG5YmYF^0?DIV5glVYBQ#_!5aO>7B3PpuaUvAyH6LIU- z?SXrw|IXNx-j@BhyZ=K$-{saV@+bdkw{QJPdw%QIi(87Hq_sV6@3!7=*+cs5Bk*%q zX>pp59&43+QjRUidm8hbX1XXFq{1unO-E73HXvBad41!Hz;)xc&iETn;$rBf^0bB% z1w}}NQ1I(;gI`Hajnbc-dnD)2u8xhcb5{-=ffn*7UNfSj$5(`zOUlgss$h&4WVF^A z=7?#VDzMz~FD5HsPWCeWe+EV#rl9ZNztc_&gQmGnE(Y)=x~(O?s|*;M+S*#-ikjk+ z|9=e@=ik`f-Hjdg@qNy6a}WZjf*b_mD%@M`Q&UoGrR)euvg3H`|L2hZY;mr5+75NC zK96>9c||UlPKZrvv1rn`Rvd0BUeKaak$ijFK1K4M!NGnHiPS{*3N7Ni8ysOUpOfyn zk%HUGg=X;4AVpLwTCQtgZf*4o_$D2+J^TX=v(qu|Xpqmb=wt8jxPY$G24ZpXV$%-| z!w{13vz=58#^LZAbOBzMh-1Jl_e#N-ZsDhqNaZbhfng%L5S2Q5quq=a=&kR(E6hs- zNgTvIYrepzoV7FpQ4kyPJ>>p$LCWPmyK3RbLgw@8v-Tl(#MJ`BIyqmZ=dgW^cM;hh zw2Q@fVsiL(WNGY9XyBM5r;&|4eM-94ub?FxdWN`4hHTo5mNQv*}6czk9^Wr%7B1FvibnYFR zDcZ|Le4k+x?!7mM+QG6Y&3>RZnOnR($wj{#G5)Twzg>|%b$H<9PT<aFB_ThF+*$Z6OGS;PF~egKDX{I|ZlX-vZ`MqptlNIG~m3F!D1f zqj7M4p6(<8S0>Lqc5@)k?`R@QbVsA2Z;9LgF;@MLJ*g*>f#TDOWF^&5TmH13M6sy_OxebCRgC* zeLk9d$PrK9H!!&0RbMkYs7$BLa~l6)-pI(ooA^HYce4{=f_`9HjU8Xus3DP;ektsS zV)jg80#1rAjsf0J9#$II3CB5fo+6t?HcM`3qJI73ZC_gX3?XD$4MYqCt*oL4i6=>@ zVVF~slK~orO9T=r^1bUb{^odEnnVvt714GFxnErNUiU&&Uf4tYwnyT!=>@a=b$J!g&yf}?taK~W( zF(X`_{%h$zrrxN@3|Ikqnz+=m3(il%mApBm8GeenQ4rtln9tCH?2nGvbIjXgV3xg< z6*F*x@Sd0dqZNjAU?%>TJEV~yc6rt`2e7v}{=#6;rJSNS^P{BU$R0kOkZw|-CS^So^u-%kQ$72^4;PmI&uGC`#_>m|cBwo+YOLNI)!38=! z{dGZ_$@AE4fWR^q?R&lIepv*HF(G|kpdbika+$4m5@Ix^Mw_8mLA~{Z{6rk=E#lP) zwv7;kC$`;O;AWuSUz*X`_E*c8+wy>x1w?CwBym?AzDh}0x6CxUX~ch{G3aeQ>;Ucu zNuqO2d_BE+1_LiV9UfhC&Q?9EIP7XKx*2Ca1G)Rnc77ATXu;?2UmG{G*)NuxT=`sG z{cNc6-HS+qnKYgPf1D#xQfbqu)f+5-og__VM*;TM&5oHnlb++9*o?T7rM1}y|IBCR z@!%l>wkS)(-se?E1y+B*%fykpN;-Re`@g}TrH2HYc0F@+noZ?fw6OCPZpR=DYEi3P z(0ku<-p#+UxOh=`I5Pr=B)(}{)TP33hk_3GNNBGt+Y~z7vDc!J( zSXErz6Xt0=+7Z#B=F#1Vn+P+hu-O;I{R8+cm7VV0aTiz4!E|@{J}!Wp28~F&dawfp zR*IYj;QL+FQx7s-X9{4R$85NvxZj>rnz*9eXyB&tc{&Jlh6*9>Y6D{ZG_OJ`eJXc^ zh|1pG)x9utbq~3#wIEYZ?PZaj*+~=!{{rJ5T$1tFh!3lXSPua&UXt!gDgx*4byVRQ z60zSI?4Fz3U>^;lz1C{B==CwsOLi28IXa?6E9Z+9?e_7*;6;6odIHEB4L11EwlYud z{D?Pbu5yOmh#(qV)puqh0rHd%H%J$>IrEP%s7zuz2}%>{^}YG`rKLD%_TsN`2&|Af z_>L>ZE?azJ!(=SzDTjX`n>WCmu6`G{leswZTF5K!iLB)Lf*99f4 zd*W{&kv_}sPn=bhLRGdNltJfzwY@=nRm00IDrM?+u6?$2I<#9sd4dybcWYJmMRPa* z0@1l@Vx>kkf32l_Bpem^R6GlBGB(-~y^MOt82#(Nv~6QxjPKbsk6I>q1%AnqhuL*Z z8YcvAq#*yzaCpW%*H7QfYXAqWl6tTHGkHzCG!?(hP1fQ8SHKd<%_)Rf8kA$;g9`*9O>g;8nz;EVapy|hk9&cDod#|ji%*l2Jaf~$Ig|GNWLD*OO zqzvQWt77jZO>Li@Qrz|#s@ZqBMHq&Uk3%0O-8gv@$N=byCH)>EVf#4CL#eqJXkBfK zd9{U)Q62&9d*ATSzivx8S=cv3wOtM?dp^%~o&c^n8hhOZ%%$F#su^h+?%!GH z1G2An0fRP@m1R4cnHNk%_sfZt9p=-Pq5Q!IhDP-6wJ&ilX%dD&*P9RTgb2$Mn2>g13|V(Elk7nEu73wJixs~`j(|_J{sTfDc4-geS z+2)5Fi}mac;P>9?bHY~91qdH$@1G_u;)jSj1mI=~d`MRb%xtdRdtkUdcBb6^x>0&B*|Ez=PWs?*+2YYK z)8!aEFf(AA(}b9Z@ALCjKwk}1)>7GAAYq3-Uto}wc7J`v@c9Q636|_9|oe^;M`U=$aRpP{^CTtV28V__j&~LB!0f7 z71w5M61vyQ>H}Qe!(mx`%)RF@8Iu5fwCdb_ovP9E1R|If{=TyR>TjYX`qj-U8tuzM zXv3FSOE$J{SE@T84z>qT92j>WG-6fi63&c)fL0TGHh>0*QQq9MtJ#{WDg$)P^y|@F zCRc3w0plTosRQxIq+(Xz(5>~!Bt_E}L~O+qCtK-lhHl>H+4FP(JBE%X?%z1(u`KL8 z7y_;pSVGRiyF-z8@J+7q5PyjVy9o7wdYI+$*+tGnjG#VN(A(7ex|R~;=#4W#1^Hcv z!Z0Bi*=Xi*V=zDd?xcH@>bZ&vy0U@m-Xeae6OGPjyff5+o^|@;2$$OB zt!Gl1LCfZ-%$sc-`aVb6WSeWZWBuu7YeEpw7H16S@#KCz|8r6j#ed|Yj|sy;|gi_`seJ?*8CnSS0aRsZO0aSgNe%P(x+PxW~6*e4wLu3K|L?~9BgFwTJ`2n1Ym;i)E2WFlvf!m|Y%X82uNtVm!^GJ>R(V0<_n`G3vt6d*C(4 zrpMSxt>0lYPFTGX;xz2k%AUzI4!QZNJglxm?;S?QWk5xgug_y; zjCf(Eal;+C=$JD={QWIv9J4$i!2k3sZUcK+`;2MsQk30{hAh`&xV?MPhxna=TzT+( zH7G({=v~ZcJ@CnbEKRRQQ*gQ1S7d8&Cmu*4RC4*?^h)kH>|N#<^Hq)NBgQGnnOtGL zyx~a(_!50_@aPfW2;rWcLQ0=eViUVH8V<6BH=s%WC1zKfXnu(^C!glieBfkK#^2G6 z_>FzXxxc+xZf&h-Z4*5@7HB=b1=(876g+C5vp9}#=)Bz_wadFZh=*c72)vL9&cp-Z z%feD;TLDQPWgq+WvowSeQCQ|7w+Oe@ch^nyr9$6=%NzA3;oab^S5R}+(-u6{rf<%| zjgj91D%S)_IoE0ZS?9e&iDB}5*L8Vf9- z^yZT@0NAYjg%t1qm3`{HOowSBs4{6+~dPmn9L{2Q}Huv7%EMN#m z83rwbMtiZa4}QnFO?3a9nT(jXP(M4*LJeYC6wGuxgMk$eb6Fh;MTdUkUa8)wVdB z?;`KxgaH$2nHSWga8#%`TMv|eB?#MSFSY^M4VAjMKgBpT(zLIBw)t7N3e!F*x$i-R zckmQh_GPihDfZT*$7yNst;*i?Zhl|^bqbb~d3>#em!4={t!f;@)xPQuY)K+&ESn=fORpD zyv=g%2xwrcX~6IhhpY6pMMDnUQQMW4fAf8Ir4TIyBI5iHK2-L6N5#acqKED;!%@#Z zb=%gV-}K&L2x1&^n3ZXVYtFRyyCLOBGe_|+04ryoO+S%3znjqZbNA*ZC4?CXIn6@= z5yU}t8{M|;Al!lY-Xb62$Qng7^gF|Eb^6)WyzQ57c9@u3)41If&NsG)@bzti1-`45qGX| zCM8uP>9}!&e^N*;jI@HZNv_z=c;#(TspN;F&#@gsarduriL;?^IWj zbB)DSMch@2mQVQM5!H`WGkj zYJ=+K<4?B?Q{1K8fYqr8Pw|~Ov5k0q8gj9wb!*lezJD~l)!VA_(Qks%JiDo?YL)$9 zn9l(_!XO-l4{XFfLZS?v4n4?@*UX7O{@nY%pEa@k(Djx37cCI!mA8 zgAh#)y%fCX==kkfyf%(66jxPo6sc{C%#Z^k$VYcu{XlofTW`F{;+z@)a{f7`w+`V$ zR7UyN@^EdC6VpM<0rfm|y@WGVa?{1B6!KTn#l#v$c%XGgOT(aban1l{0b z!U(ZEChE^Y2lo-So7SVWhUc22-20&BEM41Sk@PrpB) z2+ZSqFsho_;nrhGzpy`N_2!Vx+dvlPp?o{wAhq~}ue4W}Bpy3@JyI_l?eX&C;>1m7 zjQg7B(m>|4P?%SKRJNJB2=sATX2e4C0uE&cAN$v+wLAGujFqeBaD=yh-X8c9#a9) z;{W?8xgE4WlyZCbc@z5)vY^0+xIV%M3%9UF$PsDkEZ1L-^CQk{McWKHm56H`SN^k6 z+E!L*P_v^0pVlTEXp26kNU$_|cfEDgLxDvna0*>Yf-0Wxs|4(CNPdHDSX2SeH2F@5qGKJ!hM5pm;G6Ll$~U^C&d znkm@vd?uHb@NTq2Jh+XH#(@C|wAb}<%*ga?q#I=s=x}TJ9N9Kj<(-ePo;f5zG6=g$9m|gU zgjvg?gZF7rL%W}^yJ$0-2&I*k!gv-_Lp*V(;Oo(-usRhnr~sKR%P~gvM`>=H=gb7prG)WM z3tW!>1!tBsC{IlbmA3^~qkmT2U$l>4Qt5JkMc&DK7@;n;=y0armfW?QW&2R=L8Xj<}ee8;nijRFS`K0UFN-Th|3ik zbJ2~@*>5ZIy~ZKNL8Q?2UM?y3yWZb~|0BsuftmG=`5SDqsft*f`&jiKX87G)T=ZQi zKrDtab+tYl?=o#~T5a5)Ls$6I&U5GY-l`4$Yx*U)pE^lM>2?a%i*dVVRt03h8OI>1 z{$LggjkMj3>}~=tb*kAQS_#%iyypzomFK-28(OdbgXey%dYWzokn=T+;~C~PPew01 zIj|Kuh+-Q$saf0wjUULeNn`5Sq~QPL3(B&|tWaJ(m{Wk>_hB7LryYJ>y{;#G|z}#>sQ3X8Kk$5KYzYa@r+&;ua*~B57cFHqI@x%=` zFBO?M?cO2vaQ~yAg(1^^eLa@>?}EFx$=NVy#?#!DwXO8fr{# zgfH|n4cEWPn&~xeTU~Q~)n>t}TpZcCe5ca)KpR~k)2h;uy%FqV~Tuh6-$^&L|sS2rNZqFTV`^$IA&e~kZuu{WKJ?nHD`!aNLmnNT+o zd6{q`o@v6=ZKo*ig7a~669nhbf8xFi6}zuHv)?t#|8-)o#OS6px3K_b)c5Vp2FkSD zB5A*AtTJBeT=%#pKKxl2{&rVt$$SBxPme z_+o215{rx2kdwPkn?h)gyD#M$zK$7JA0<#k2X#XSTfffio~l07uq8XMf_&#%^4cJw=^rM@uFy^r$&d~>2s`OD6(L! z+?V8$@;2?nj|!6b+-ycGQ9X{Y<|~-!L_|pEuZZ1`{$!4%P;l@=o?GJoTMNL~6!AG` zV43V!R*9cP2BYNiX59)o>$6u$*X8YVeSwuM@~QEcpDt}ShI9EbYts_P)72f-S#W1g zKxKk}vuJy9+~^@!cixk-;ML!ZFZ=n!ibIKSyBNE}6*KmJ92x}o7;Z5zn#J*tCbhpe z-6kgvCuW&#%dCVPM6}VaXSKuD*ySsyx0}WCgh&(qOjBROW^bj-I@sdf$zQ8|NSJz= z7nct>0wyV>zB%HWypia5;`clkWWpIC*K$*PzPkPUwXss_W0mVZIj-Pn8x@~Z;E5YG zV4RR32x9BcV8R5sFATkwwCMAN!LrJp^fU(0XmQxPtjXivW+QKc9OfHn#LI4 zHysnu)`7t8$b;5PyKcBmXd)z z6O$bzSF~t;v5!&gX(6%Y4;onMi(4KjuN z(_d-H*tTo%jXR1c9n9iVJw1m z98VMvWa9j^Y2MWJ7@QnQ16w=tAJUm|(G_RBcz44p5BKWniugHJWdDfATo^FT7ZV{I zaTvNy4QBa8`B-g+=6mCF{%Gy)uZITK#@IX6R)?$x)ifDfwOmR^dSPsv0g_7r{ zxc;$|cRfO%a}IvVt|B zN%Sg8Z)jMnlBq|2UZy|l-u0z4yKin*Q+7}V86Ws>ueu?=4cl0EuygWsJhE(ChoJF= z+-&6Tmfw$i7&C`>$1zFW!knnqAKkoT?-R>r z=WDb^^x$Prn9qbw`bDR}liSv2W9h}%UzhIZ>Jy+ZkEN3vizX=%%EGsc2B;gf>Mu-0 z@7PKueMF@@bQUS{y!?L7Ma#XYl<|F%YTzvg?m4#47Wj7bG^|Y}MkDZz((vPe`KenL zOkp$tvMSQmBei#L;CX~Z43HBA(OIFM9lQ6nn7yXPaXlIVoiLRB0(P~Aw%nT z#Rb%n)KK0+$y51L`BAeM@B28n#W;}ie8>~ZKs~_8;on9GReEQ+ zyu*~xmv@!8{{}pL`zaYon3(i8KHQn;q|D0DO}Bp5F3qqzfzM%rMJ0XH< z*q&r7ZI0Ehd}AFm0t8+sz9ZyNe4!Dde4~?#JJP6M!_ z66ViMrkq0~Ktg4~Bp=w&KvbHb#TiPY2iJoJuL#lGPyz1|}NyjfQIcus! zTV{S#P<+)`!KCRJ8a=cMsL?k$Bx_IDUl$8~;bX64jobV)kF9ehzu~9<(?yYPt zf_H#1unt(w@z?cbw+r?{n8ouzPfn|+vo3 z_6HB@_^RH~a|I&qS(;RP0=g0(cac+vQb9w397mN&vo0EH)tghXfH)~!zhT|_>@0J)&8y1wXU}ZFb$3CzRedZi)|2*G$DQ1^M5jA( z!Sl&^e0N^GJ5fqbn<_?jWc^&PvKor5tD(^DJa~2;#`gZ(ej!H~Nx}r^W5y~}sBscR ztgMqUyViDHUPe)H_1^E*Je=~-bsNx1;-AKL8QgV zGKk$hJaXpws>R}Y&UDQhM$!G=hbMirEm^R6#F(tO2}#1W+R*isc4)%dMZLevEi#Gz z357QHRd4Od?Z`g{QqA55S!XoWR!Jnt$z>G@-OQOclEwMLEnlgZaPuxx!w0f3l`<~U+u(LD;M42h=Ya5x zrL>{9qBZv-PxD3G-qVJ`1jl))yG+a5BfEp}Vln8iBtN#tT$%bXU++seZ7r(|F&!z< zId2cMazGy5Soz0@v#yuz^aWwg)JhyI#zy5j5iAzM7B z8I%3>=MRw1t=2KQJ|iKjH_-)~+?BZZom92Hw`k%by4N0%0n3b-+)lk#?5q_Mofy;k z{Q5~aXw?^ZLUV*hijw@~-nKG(nv&_dgFH@HFJ8g?k&0RdAtR@kcRMbV5u|NtwFh%C zIVvoi%HNDH;`}m72WMy18-$2a@{$#ro;gRmG&bCS9cW80*G=>Vi(ToO)G*!5I=CD? z$pZ0mu1_Do*S%9OKd?#l9c4*D@?axVf+o|$k#sNG-(b}4vf?$#((WEVl^4H=5Emyg z-SnG7)19V9DB32JI?Ha#7#QjEd>L&jM@>HCz-wNp(Mk{K9e(HE)SPz65GnUhra?^PU} zpCb0NsjY>2zYydY;ENVf^o}cie;z6e)@4)EY_8N4(fygV{0_~iMK+y$Qm6N5`*t4> z-Lp6Wh`bq%nTLGUQ=G~Vmvx3`^qm5pqx#N5s)Z3AeY;L%9A?H@gD`FtiLX2@NtM6|`Olgb>qITjxOA$Ic`KTfsPwhwWs zmH*|DQJpxF$0x-;e9i=ET-%iSRyAF})5k$t_-XDE6b+8NC>MA?nPrCNWXap1O#GVGr==rHf#9xoJ!|U<`Y|q>V|AiVznQeu@A@9gmvFdu za=wd4XTkh=dXtJ7cK9J#c&r0y$-7Z)r9d$PR}|4^CqK9|Mt57dE)p8lm7NxAT+pmA z!PX|IAZyMuNfrYvZkdR9_6B1PO?+a0yP-A-lFfL}%19FILrpa|LC@i?RQEz$MZ-$k zPNImHC2_t#Au*?u=EIry{Y3 z&fT*)R9&l|GMUZ}bRbX7@`7V%+>d=AI?hK;1S0dpFGx^Kvk;78)-Q4|js_EkF% zVtOBKzdP!D%WMB6+bJlIwsY;ELcm;_;jy-5XXmAz+TULp;T}^$T;ay8>xz}@hrFwm zDv9rZ-WsD5?tB?TND$j%ua1@5Wi%3*p0(e7`itUvP>)agX>r|PfM({bnZVI@(we%j zS5~JD=hAq3LLDSqE#etAMbvJ@Hl!JAeqA%opaHEG2i&D~Sdy0T?-?Z- zw6#;;3LOlH2wboh#FuggH4Tq>Ei{9?t$%$&G z=~Gkv@KIX5UhFBL7#nX4r4}^|wFPZu&neEzp_6}-wwA?uK7|92XNlt*Ooo7?N543; zs`Dh151uS_Z!Q`7#3XvCvwH|guja79UYPIJg$g_E0l`oAN3IomLUK<1RLizx{10_H zIk5`B@wsK}5O;)T7rW+*_Ob%s3_z)IuU6Qj1C1G>$$Q+8Kz zJeh)fRbbF=7E=Gao6l$GCx=b@22=hF_LVr{`3oGH#z3p6S9*>S0jzHOl#Lk{=6S9W4wM^{B=1BR2z{a zEz@Ve6(_p-_1o{9haa7`E_9w~NXC)!p6>L`=C)c9b>S*LV(N0spji6@s8O7dugm89A<_A3S*(ox!C+~avF z&damAQ|wg3&HOK4K;*BT9y8{3e+@dI9!z_&#qwq*&|nVoAdf0_$DFd=b=4%;3V|EXyl#yZ!s9#XN{?XfRia{EpzdPr*T7^ zp2qs@E|wLBJPE$r!aI9{U6o~r_f(f-i(OGxej0`JEsaOXo(z%EfvBQP710HYo@90E zo#><6e`2&&>DvN3qBw}dijM%e+(!sAmbNkQh-Ny>BM~$xQD9>XBLu49=gRfN`n>cf(F=W-phou!v90QKwIk>;|QkGSg6p4pRXx{NBk!`cmjDy2jb+Y5?l-~9*=`9J9Jr=U@(I`GalzD zEv7cE4tW_~Sd@pVEIU<$OZJFJ7+&y?Jsl6D)wtBC8|mGWm4`VTL6nc?^9D5>HI56D z6VS?ykDcg0rAQZWDLuIEJ4lm?GW2*IfH*HoO2pn)erTlqLjhF1;96xNeJ{JM_WJ_$ zLiykr`LAats6?V0wJ}#qfKYly6WxAJz+Bf8ox`VW?qf07Tkp*H@H;a-z`d6WiRp1KN79*p2aF4C`zVSjiG3Vp6aRVRbl}tuJJu6y zcNvmT+IT^%L+AMYiYXgsy_>sR4}9y=)<*-Ov3dLSSB1KmF(2hiBY(+9*(4_qqW^UD z%aQRC>8za?cm;X^4ijHXYB}pkX3UCrR&>q@eZwP}cW=Y!NXQu1*Dqe8cm@vHJA?IC zHeS?rsnGj5@1@r~CxiI zrFxAvzn@EP>qspa{7Tlua@l;0(_COs|J?VCCHAR~-k9j6ilpFJu}zX7bF3b7WcXTi zryR9ktVBMTKb|iWF8R4%Q2{rj*%T+pp=sV~>GODe{(iZI&Ud1P$u|eD`tXD>2+^IjAQqkTtnkBKJ#+f^=fzb^{{%dJGxH zEwf8Mf?aI5o8Co_s?_DyV$EWY#(g~LOzw+Vy)q*kxy05470t0s zJs^kuA7AWnD0tYcy+QK}EW*u2JM$}sr}0I&>YQ!v!l`PH;y+yP0C#GA^lhB#Myjo{ z&$h-LZB3)JR>J|03WQJT(7&Zk6=_`t6N3$qCZwt`C0E>AfKr>22yy zRu4or%~HH?ydDVvBubfc0Q-Rs38Sj_*W~~g#lVW8`T5fMFU0{DSI1iw08OE74%+0y zciKJ=hP7UT^Ay%pjsN);`yi{fKQ_xIjnoBargG4^Enq0K<7$}}T2>uV@Euz{_0gjK zK~$g_>0=Trb$|B}MZV9hgZ=&wolG<2s&Bhh4RYysH*g;3i@B&i!(sJuune z2Ltf-$Pb0;BD!@?{Nq%M_3ap#cD)V6nuT6}M&*3?;HjNO{$%Z-FMmpg!&rxE!P2qb zb1%B+rj)*~C=QT*TWD}ZVkRDPYLhxbn|t_>nQNGK+gYAGifLZFP_s^pzF%d2cWTTQ zyz~!CSf5e#AvADleQQOoj80pO_*>WA?t!eZ&-iGgz$4YTIj@)8{J34lRyAj zj!=IkukrIR9__L6yOgpYc7Kw}UMsx1JNL?@eEc)QbBofA^eK~a=R=YhX7}4ml;4{I z73KOzdo=dfIHx(yf1io5tPYSZR8=}70r|f4MUU4Mc9RCh?CU>s|1%x<~+I{2>mi5I-gUo`FgK=TwAFT|HMVA$Vl zb*SpQvXt`%FDTov(~9Y4jY3(VDOYS;ID+hPDe({AZtePh@?LZM zR^z7-!KE?*a0uZemywj;6H~JkV2??B$qzZ3xT8*oax*jd%Mc-(wH}t+RHLO<-}dW$ z8NRy<3Ku14KF(G18T&=WUgf&qjv**7ijzf}E$YVuubI$m9lL!>uRaWDw=IuXtcANh z=T1~?dnS7{>?xz)wuY6IB8a-g>e)I$#@|}fzd6+wKz3 zR`7RvrOVbeOKsdabiuY_d&w`pZWlvABV-&lE|z?E#gQZW{o{*Iv!_xyxCd>((j18= z_a7IB%bZ&Fswvueebns1NN-U;;sc+RimbIK?M~TA(Q4pqKJi|tvk7tb z5yh04wL~hrip-2{G%iedHLjT3dL4!9wUyd1SZQ zl_9Uz!&Uy>?R%smr_PSYEo~!E!c@K@T|cGTm}S*;KBam>kV2Y+JZet(i&zK!PtvQ)usUCfUDYc+-)ia)F_j#_&%e*eeMvNY-i!Mm`t3@)gg$*6g_ zNyCKJOs_Em?~x$qw9xt&hn_rklluR5c7PBwpiJLt;%yM=?$RS1lg9# z$e0C^JZQ)69m7`9!1hJI61>Tc_n=GjqW?adc*TL$#EQ^Znvn3C6YH|_3p0Amc& zHGk**yyw|D+HY@RYyW^0_$h;pMRV>mrKRRAOd7dZhsIy?>u?FT58$tq1~JvW;9UWB z1#&g5Zd-Pit7YxuZ07YXPj067r-QUcv|htM&->LVGg6pYF3ev&&K5JT<_m@RYXoPO zQH4%%k&-I)h(%vwg6vP<$>2AeORxy4S6!xqR83JULl4zJ1>V;CsM{ztYbQ z+grP=J|*k=2c?@!+om_21b98QHWaigNpu0$sY8!tOn^;8K2(5vcb;X86~_H8<@(tk zSTrU2=D=gpCPWGI@#p)3lHH@Vt^1XA7c&tNg->@>6nklUCkJ#Z1U}EFa7R!JNN!A& zl3hp3;v={=0Q@@%o94{i^ZHq&yoyOoDwP*f4$6RS_3ZN%Rg4~&U%caEvS%#n8gQs;1E+zWo@u%1#sSoReKwO| z@iH^IeS@RaMw+VpPVSVq+HvA(M0*#Ro|F=NG(!Gix28x9Ag5=RKCn+%H$;0&uDEUM z@ly_~h*i`lH)g3;nL1=n!F(~+CK@{wnNjs?R1tQFU z-SlnT#Z8=d8?9!+pMJeG`t)UE%ckB?yO`1in&F%vu{z4mB(AV{$t=oK`O~f8HR>0mZodaJIHTj{=DmK%Ke0MnR{t+#{ zWmBi0?4{Nn4<3E9r*cm)og}jN#sAgAue`pIOF;J%0Rv8O@nW8_b@{*(e z@R0^5WfwHoiHierIt@p8796el@52$6`D8w}QmH>(^eG^`IYlpRzN}QI*p#b{Uc0NN zH1F1ukDR;P>7B4?q~A&=^raTl%U<=EVDc%ncK-q~cx}&ftF(z<7a03|On|H`QotsE zMDXt5QgFczj@n@ZTk1=Wc`-hIiT`}0Km`ROw%Lib+YB_X3Zgce1EWU*KiJd>1p||D zfLqxa?B7OM&jO6Bv4w8jHl7<77t*hl+;=-g3sN(+FI_RW~q_ zWMQjL@mPVze8U<)%(0CuMtAx$R)X7IxjFu+L7OrvfJ@|=OpxI2vu?_Sr%;)Jk+_pv zE_}7oF+=(OwxSbK{AfFMDf*0)$_HhjhdU-kN~?UoJf%ym^iV~-TxojRN{)Rp{RgS; zN}7|~QwNxy53_!B0Uq(|SMzu5NEPqU!O_G;mS0{-&c_QX)bEJlG&sy%=>mq>+rM-M z#RYGwi^g0|A*@dLl z$xEB8)!q~_hu{dh9neHUAI?&ihOe zeaaM%=^HGxXQ$39+Oo?P9^L{B4TcT9vwb^IUUe8F7h^+?ReAOhXJ^-!mUTEJ=A%?^ z7>#`VD01uKKV-*Ojw^!0Xyk?u9m-ESbU#%epdO#c#{@|~R9==oX^ib``gVy_Ub!4v zz93hDsmxJM@{wfIJ|no!-)9a3oC%N;)PsHGbmEhy$|V;twTLTU+J6(5_;r19$(?%p zpqmplH}>%ax!SEvPtzYcbxugksB}!XZ~2QB)ItQQ^!#5BCJt3EKK)2rboJ{c0%@Nx z2KrQ+?OW9kv5RX%ZflN;h#08_MVvV1nLy)0RmHISdvXFsW0_BOp!c!6T8!Zvz)0_n zkI%D5Qyr48jyVfp&7efR-gCUPPZ@tA0{=Yz#{z9afL0q17sO(VE(SH@#uafU4|<;2 z7wXeuBh#^)+_pP5a%&eS_;H)Yy7CZFpaOJ2ss-o(vB2ORCn8V+KQ7c6 z{cGYGLTrx{U_ysm^Ld_)EA`PF9_#&>Nq$QfFW?foY8_MM2R(SGLYU+&|{+QFa!u!cal~h6t#Pr zyO}OG^$1mN+>GGZUo5fPMAg6k^hXk1IpD1w3mA-(KC?4NuYOU0uQ8}_CMgR_2dA{e zCgpGqU)|+^87?V`8`KFf%&2i#&BluQy%ABU480grvBhU%QLcQDS!79E$Y%YMk27t6 zAo;*)ko(q47VkMv3mqYQCHS8>^L2aYI-fJN&&ch*k+CT&$rXLWzqDd|z=6?)juWq) z-)u7O{usR-X-p(`+;n8x9qh!y|B?ll-;P$VaZYyWcX@&ZGB0^KD2qrt{Q_44nmD*H zB>qlVVuIMgZWK@TotvvV&K4(g7c4`?xl39o9SI+Z%WMX7rlAUNJ`1leTl8ZoJ8hia31U7)hqQ8@uTrM&= z<|a;h>Rsq_nmWe{eXn@?R39=KlhBJPc?JkSWT3|}K5uRAL`QOvGjb&PrwNf|Jj&1I zX)3vv%bf>lVyLw`gi^laTx`@OHrS@W9V2UD4jm*erZZuJU4BK_w=#Bkjv}BSqx^z< zaxF^zo13F` z8Q-b5kBie>)c5M*f66#YqwkcFXSz`4_4sXG?K6ha3@3wCS(h9BV25 z4TrM%$0hpb;`7W$f|8e)`kZ{TVA4m7$)1nr?)^l>dAl4gpM4&nVDfoz7i)E`pgCfp zpUu1FQt-_IeN0Bum|aj;p(4al{!2-WREizQk!MjJDI|%H#)+|oh6X>u*p3{>EL-Ju zY>0e~^ep|DHWxJEV??FN)_jQ*a-WfRb-c`9eA0_M{nPQhL@EV8dQ+<|3PGiJ36o;0 zwt`>T<%qN$G(v*U_< zi)_vyRCaowR4=_+mw2qBT^#vWxSa1e3%_#ovOc%Js6a~;gYrc(`W&+gbzT|phRAy0a3&}uPv5L~NMOiF^@ zqzV?3iaK@M?qnYuq(q9H#;8-IFH3S+vI(+5x2V>^MRVmSdUbWkB_`LIukEPNrOWs+ zDedW3n5c}zt3>CS!fcN#yVvRev0(8|$pK!)w!7fSKr9YkmbfLh7ooUDoI(_X;myON z#iMQdE&TD5aVIC~$g>VifT%n$lI!?^R^vFEZ;FkH5~sw!W73sUK0h87gLqBHjdSZW zFlj^3FcD_@>;(vAyg!oO1=3>dwea*(yzT5?V)yw z;juBsQD;X{29@DSws})wo$@%+5`NA?n`2?qZ`PSV+MkbVl~jQ(s|RHJx_GB{P0MYWTkG;eR|zLCF49-1vp)2r4Z+SR8P4QE=^O-3)OHhbYMyfP$wUN~jKqz4 z1$v{cekVQ`Y!-FaumLJD;yCA(HpB8qvCAefzQnq{&@qG`B5Il@vm-v;8mPTIC`^SdyEKet~x!2;RrF8 zv%nBzsqgt@+?5SQ?8t`CdXvlXf+aHz>6cXZb$k#XQ)9fwxq+tmu0J-A`;PDCx$|ds=rxaa&D1=RxnUso?2=KpKK&6Y zO*1*-6Jm}o?J9qQmL#*OcEIaPu~bIBx$A7Ad^aevFpqK6z+<0BFccNLk(*)_l{!?d z)m4|?fd9;CImNAX^_Y(kbIgAay-gaa^kIJbOo6?)s~864>8}*()E7Ig8uiE+>m+Ry z{iLm95>+80PRi_Yu`asYqD?^HO#+LSmlKBnMmqS%F_@hyLmi395;+}ff)xu=MJUcCv~(i&@kh$Ay|W~=b9o<1u&Pvq0`JiDU|Qs zh7)w+>`<1@E~;vm#^m**`JAIGVj-!{TS=>p4g@;T1+^CD06z7}YXbL>}! zr{wW$M)bq$>dKTEAN!bI7JW84r&ZQ2W(z9Kkw5HnH!g<{LM*rG)%nE0Ntdu zBdD0>i5>?~T2kk(OP&egTKgn73>IF_?CfBT6xUD;6YIGf%<%rf(KR|fU;Xd zS2QA`Dq9f9wqOX^yj$b!d3lxDkFbdz0w7;7=R^ou-f_V>v$XRtVmR2P$ws7X8g z4+SrM1r2gkJbs@oX@nf*=1+p5%LUA=qaz&o=mso$Mt_)Rkug^@qxo z(BF|?lkuo4UKfGM=U|Yww{uXkvCATXvytuu1KqXPkLhnFY7DOU_#1xJbftAyb zbnV!J8w)(!=XszsMyVFzLxzKqy1Z7mt8#H7fJ$2YkKhcb&@4eS?i6c>0X*pyw87%f z79gVJqbR$C%2H(BDoN^Gx#*`E7I4)^0hK4>#FZGyFO}y!7@-t@?K)N%9SDS=L-R2& zHaUo99Ai+g!M^LD-pNB5`X{?V(0dm#8FauBU1nRMj!(Rj$Kv}q%g0nnPJGdAl|ONz z-Z?yX-knPX7X3Xm_z_qJR^FV%0UeCWSm2{R>9oL*2u2%{Y&zW>fW87(d7&-2c5x?W z2d_vN?KHz9lGs7J`H}HCBHdFaZyYRe?hIeA^CrN7pcXB1>9cU>!FXk5@Jve$c)5#2 z4qTZ?98}G?@-O@b4;hL{j?u3ASw3f^+mMg`0D(1-<%S`qB(~Al^@~HCTB(kieCbO~ z=CipS3Bjw&pomO>M7n;>vy$;YeFs?PvWs|2;VqsFvg$L&CRqkRZD(*H*tN@Vv}V{% zaZ!)$@u>^nqRS7mf>R$lxWUW6JV!0ZB9$VQBY-$$Y=Izo z1&5g8TRR#>TT8lFh!OQ;OiR0{%p2tvGh>mL3gSeS{EN(PjCo{Sq6(S*a)d>e*oLig z39#OCiU=n!Xf=qVsZpFl} z)G3DiG#fTJhUg3D{~<})E6a#=Y?`#`C@>hi5q~l9a<0oW$3C(ZeKns9%+9HPk}>3Y z%=!R%*+E0OkGF{2(b>hGuJ+bEj}%ys9!OGh14MhC!$o&>JNg9l#yvaK$nbwKh?Dc` za4d4AN;*L&3XIiIfMo|w ze%Sr?Vw9Qqb_{zQMQ7E~dIHX3(vpD=1_SH!L!lm?kQW>^;iJDs((q+6L7{g=I!{~` zWZuD|!!*a*VXyBCF&vS-}bxSaV`Py(3hHyP&beMmvbQWl#@vCBuMQ zY{ysd;F6r1l)1ChS3#>hg8@pEI#4n@R_zmV-=+kwDIp{lHDu-PF%3ZoLut{XjVLPbT zu2l3vKyyrNZFFa3#KuLxeCCB%97M8M;UYIB%vfS`K^Ymb7<=8iqi{w$@8pm^v$LmG zft1{tCpT$}pU7wQBcLDQ1*|@BapdA2OO|*hj#W zNK_w}W~uUwQ6P$i|4OypK$$h^5AfLU#1$z@_8kZ;_?>%gO#VNf@6rIpYe@zi`uAEVad7-zl6O%17c zAgd-#>cgYmT80X*`InEC(W`EUa>qXP>Z`zEFm{6T(+i(Rb1-(SQh{HNP*yEsN~!%q z=;mG?Uhg?sU!>CaA}j#$Pk;1>Y6{r3D-xn236qI5`qW{TK^zET)(>E%NgO?^08lyZ zF0=)9LFwk4*KWIGN?qsMlkkLE`(=InLJ>d z*ij$X;?!V-)^`12AG14V?Cf+aZv>JZI3^nmz{>8Hexy!4rklM)5Wp??=7921nm!hE zv_)UVdgZyGm+^pDw{=?m<+F^|O4nRN@F`ohUC%{w`~Bky`(RVa7UCS{&otCJy2SF4fuI1W&j_ICd%5Wjh2=EC2! ziW89Q*g{S8=7w5z*df1bu!@SJGs0)^(^Tfq_qVATIdSpTy_^8kM%!vwTuWkA7dA zi!m5Z7>PF>oP+6SSj99X0U)}Bsiy$@6nQe8-*o@_h5*< z5nNcrI`ScRjcO>M5tv1v^kV5Ycq8tBk-JUWyi?Mj_ATkRbFNG$2S*Nf_FQ0Qw-6)j z+}x{!-P}=G6c!E@Q8Gc3b7_S)IbBW6o5kY*>%f#hjfG6*=tJ`*B-Chc3J1^3VFv+o zUOX;sP)r;HmD5D0vxrVv+7mLa)JHA3oEV|Vfhgy+s)OcWh-_+%Z}{*-*_E|@qInamrTVH9b?G!N zn&)Xe(~YG;exAer$t&rAsAy8Hp+@#+vA%o2}7Sh zC&}6PVdmkT%4h164`R@Hh26aHwERqKx7II3d)0S=tiI_**V% z83*)hu!DL>W~+UyC>tl78wIo3GUuO!$=0tmD6K`Hry9*F69G1jj<2Cnp;NjOh9Pk>vx<&L zz>y%|Vh|Q6lR3fh1X(%fJ&UmkENKKfTFU|A5WJn(PH3$!i-V!Lm<|puePUb83Vq3l zky01H*f2?Q;FQ%SjH&2HdT=1jJCJ%I&d9lX?2ra+;Pu+UYLp5h zelat#$K>!%LS8W`KOI0nbFQw@X6MVU1~ui=ZmT3JB?dmzn86C#EDE|q5r-}K2xW%A z>v5Dlf4PTBcb)Q)u)fZhmVQ7b4epp6r~s;=9Y0J3$;%fT#r)c^mTK)?-Oq8Ss;OlH1Br9+IT12hmfJ zIwO%>;mVj~H`f*f)x?BukZW(QV~4sy<2k|$S5OBx>^V$N5AP`XyeoF zMfK|uOVy*FbD>)lt*+!h=Y`0zvrk+FEOr!!wA^CSzOdj=hg7X&N9HZ%sX=}PQyim1 z#~4{v=7S_g$``co=f+hAgV#&tP05KR9gn)qfW#P~(&dXQ@8@KIMZvz*>s1s6tKd@V zSTi5^hRNwW#ixISF4D}|^r>8d(>0>?OYGXH-%DSTm${BDSm-PD`T%*@CUt%tApReA z6$FP3KN&My&8^J++6AuQK|^i|k(mt$Wy4c`njoMSx$syLVTh6ksi}O+!qj%{324z5 zk7N!vncHlmK-T?Hez8p%(Hc9c=QyNjyXUS2yzz9*G2g`Mgs|M7`WyhAva-w9dBu~3 zBDzK9$I0?LzDcc#j#1fonqb#gV3!VoM6-Y^Qy~=BOroo6aTQ61XWsJH%(4{=lfo9# zc{pe0_90KjDkL`xk#C*Saf%a3FFL`_;=)QE0~#e=uJmzKoGiaI)MA-~LbiaZt&gQ@ zSIj@zweGGJB zFsSoL%Eu61O%#F)LzmnNLauijNmd7s;HU}Hg{Eehw5lJm@qFTIWX{u(YtWXc=u8X4 zBHfNuD+@aGCjQ{FE&sY7vf~xkLBlZ%G$v#h21p8y00MD+T5001BWNkl(Ille4(bHE%FGHu%a%VI1|VvGyZFQ&;>77EAJgqryB-Zo%Vm zW%0Tu5QxB~|4fsLr^-|7TQ5Dv#Lw3Hao+07lDLQyqd63FI;P(JtdDf(z~E!!%Y$-npoJ8+hHWtq5& zI>(iKu_~fykDRVG*p*v3QT-tj(r=?beA)<&>7?EAb$QkE6q@SfOEv$vcXq0uZW|7D$VR_QTKs+r??p7ne6owssA6Zm~@Jc(^nH0S%Q6K!?Qh3rS zeE!dY*gMP=jV1yb>fL;>&@d)vgY8GJ!OOvk$Bt?TvqSp{&X`nHbB;Yc1c{to&fWhh#Lpmg&(+{{V`^+R`Q2{e?djT9_FZyEbNh$d=P`Y{YW!c{DZvic|b%@ON8JFtuvv*vOm-cp%zDQ zl@9X}4eorRSW8eXAGnb1H&oSO;$(8BB{zo9+n(Q31Cz3~x1KX2zoE+)i4bYdr~?Gn z#{X&xb`rf@i@Zrf$QyX+@8SJF?7auvr&n3$eNC^KO!}lkLJ|^+v;|Z^st72kG-HDW z6tG}J#D$L%-UTHTS6Dy+1B!&=q9D7<20BW-#afspRz6E*B1h!uh|xpJ4Wqq zRU;2^`GtRUYFU&6z)k_S&!w#owVXEQ1c3Q$_@j9Cfjl%tq@4Ua%34Qd=u2Sm!GC0z z-?g?q_V-8ud~F;Yve~4d&@wlyzNr4YgcWzf*VXh8{X26x<-V;#AJV_@5&aiTN@WzIu} z_$4~T9Dw*6z0T{dzg}XV121!;d_?4EKiFBm$2C4hgRYuO(tu?C6BZ2T2W^&E{ zc8Tvfp9jjZE542GyAqC#fpsp`RkTt#nW=5k<7>7TQkVIJqeAi@{milHViBQWq`h?@TP0{##I9>?TmX7(fPh)+$n6VenJK=#r$a1QjOiLQAEss_keJv}0X#_2PuK*uSA z31ja&F$hv9Cz-K*tt+$*w1Uu+yhzm(q$t!oFzh7;Pljwdzxhbh2Rf8Iut0bWHE~Jf z3^Y4IIKk`}6oktMZ0#SrwEe{C$v-_5tH`~1ge)cPj%`8Fc{BNIr)H~}Ne*fp3Oh$jc|Y-{3+%q?E6HYoZIjHfh^Jc%ls|b=sogGO5`HeCt9X zFm0<8U?QEKj;KL)`8AI4`}Oei$bxV`8v>`cfsl@a;_6IWcnKAf+9-sB@8! zk7G{3^7B-Ip)O76OMRvh%9>HWGv6SukN2*Zc7%q;Lhjf+bm6KQ+!m&6)--%1c80D= z?E?&*pi-X&n%WEnWl(0ze3U2r!xJeXmia?_@X2oUL}S#)2BC1kSA?((FtW?b8OXkh zU(G-Tl(TKxZ?awVYt7m=t%-Rh(U!6CSalH81+FQDTEAG_d6GT$KOWCXc$8w2grCZpS~cH0RV=I zt*pvsz|@zS&&bOmGJ%_8l$44X@&SKJj;9zoWoS=23hl1pL?LvfmDjkCQ3}g;?&ue( zYFdNj;GwJ1C~qu5Tbs|)gilTjc)L$(p*M98W$lS1*eQ8r^^~^f76_QsQDUUA`n&03 zirUD~^SG*Yy+221nxMc32EJnr=msqy5VDVcZvH7*@xY(gwX{X##zV4D!*|4~aHiVFpjE82wWNob?=HqwW)EQ>pD+ z8)FIybbly){9FBk47PSqFSAm!Z?Kzdh;wEG;v*m!;JVxBn(d#hBZcl%(6)`)KCaxR z=rU`Q-y{Zn(jQZq*IJZSZC=>2Xu>^@!>vgnc^0JXn2RF*pinUXta0Un}Yk5Ao3fJOH)wNdbg)(d1ICGJf(rO?5C=@VC_fyl7qyyRQa8H~eow9Cg zkn9<$_|lK&(hm)(O|o!|yfXLGS_&A6$KV@B&(0j6o(K$Gw2Tu#&SE41Lv7RpKsz-p zCqaL<$(`gpB`)toiDsyC!a&}>%w(S{xd{r&!zU9ThR;)vlNlOj5+7bOKWnSCS_5c0 zN=uz`hUtJr4lS;8#8!T%Q?MmcocRs-|?wH51yrgy26(@;!WF}nSpHk-8-z7-#9MovDlBhAVo znmd*5Drk$u*b1UjVZo82tda%{`ih(rF+qf-9Djyy?^M!&z6qje)}QTLX$|oQk4&@B zaKhJb1WTJoQ{;(2kzZLIdqPHT3G$B?0nhjZNBL3(yl~cg*4_+gqAq@*@0m|$0tbj{ zYeSRcK7=S50OTd-)MST(FFi-1iorKu_ITU31)%wW6gyDXCK-E^+#t=8tnDxLGy$3< z2w3^VebfEeO;am_JIB_qeVregRqY+(g4_%JIq7YxVBtHPccKXtv99w}7o#qh< zLLfQ5g3#4wBa_u#ultyk)%jMrQb)U&Q`4RBvfVW-Qjl-m#H2EP)jL`H$*bRlhn(Ke z9NHM%;NCt7wIPR;l5gSjbhxn8Wevbg{Ks$7w-Y197b8j7sUW`FBU;>kjpb?24iyB{ zV^_wb_|BpB%#@@ZU6qOUiKbTtN04S^=5^Ov=+OkAbG+2R6SgJJn%276(0(gnZxISt z^71j@<{)w~CDe06Y9~1ySzQiI_@xYi*a`IZ<+h!4O`Z^$b$)8=H(uK5aAod5QyBO{ z*PT~sM}5aI<))HM*INUT=nQP$>&$WgW|DH@OW;e(^-*ABfw+yNVmswNpsrbju5+Qd ziJ|1jM}8f|N_QmW7>!i5?p-nb)wQvOl>(pW-ZU~VO6nGJqBy7)#%G$nSvuUxHGX+@4$df?wo_dhWwlK#=!kwo}N#18;yUN0Y zNr8^S`Bb}r3@Gs2vwb$ZM z=^qNzMPC}6q~ee;mq$+m)F(g5KwAPi5v7H$o`q*{Q#CGwxl0v@&=N=J#_{rlTkQ#y zroOZ_ig-{KU{psc+Ct2h-v>@8#h)C|ty^T$o&u4a5d9Nh{cYDTLUY2Ek$urm3d#eG zzmLqh~CTgm(>0CUe6wD`VI+LC`4ABTb&p1 zIxMAX3N4pGnq#^HUFv3dN+_qUYo?Z)UpBhrtj-v~NoI_d+^dIwJEru(k@oLwSLXh~X7ga%U5d!9`;cZ- zCcP!we9*|RW}Xl_AGLo>Solx(on+#B zWW*c(l3(5k<)#-N=+`!Id}Gt-NU)}V?uzu8J<}In>INu!`sGgc zkiGqJ*jf41vHNh(Zhaqk?GuAShxRPJ>H?kE+B&dus}J>x3J1PT%HH|R^VC?*tYsvP z?Ahs4Mqo{01x@F5=bR4v>@QoD!CG}Nn6+xs%{Ef_Pd=Zw zgu#h3bK27?unqWy#uw6N@yT1`jOCnZu!hk7M-Piln~Et>6`ZEjd7G@4uNQE{@n0A} zBgk?-Pv6nledk=@_*5q;bP~tp(YFa?9hl0a-g$25WnEZe5N6rv5f4()Pzgl724B~e z-qmX#iN8+pW5Y>=aamuR$D1`=;6iRDbz}}r{A1F6Iz0+ZdvxOrzfEZ(C;no0EJ~8w6V*_@J=yR$rR% z=nz0JSa^)%!$LirsOI3G#YX3joSYv<2WUY)4i7;)PAM}lU2M$JeL0UHTZP+f`FAy@>w^T+x1c^3NlMR8#AN83e==7jKZs44A zJvL-hS0BNo;2T;Cz2_1<`TB9ZMYjoG4l~%uhaA9YI`5BlnefT;=p9aQ zqh1&nHbwPP2fYxw`6)F80YE>LCm;=nAXFvwQQF>*HNkMq7^PA>xER=O|0#1P#)L#o z3K{-@0bt+d9!{#mH~5Z0N9BNVJWj;MQCh%ezGB?C2z#kl9i^0De1aYGeK!U`BPU3= zkD@GyjoW+@sM06#0e2Fq)}Dx!-HHGQ8GPHOer|Hg16fu_d3zG#Q1985jPz)Tcp{K# zTYw`xHp&=6u&_|J8H_%w$o$1eo%AMzCjB&o@x zM+OBdX-b19$(etJ*Esr9u)6;zfm@F@XnqYPJD`Fh&m2aK8(0Rnbu+8J^dZdU*W;Qa zz`Ez~0j10@x(Ewgg80B&#vZ6Nl4X8pB(e0=?39RWAR@AuiR*#T1n}&kfq$0 zM-+s-0+WSwWVaSOQUxdB53qrgx)xLPz6>b^s^Fb75(8~;bN_&Y9%4>D+EBghSpK7f zlZNf%-UH=!aGJ8qle}MDxf>onAfWzszMVdj&a7w#rR^eo%UGLGr_Pd5Hq!{q0zK)c zzGMS4&SsQJyLD>GgAZM8IO?SvdrhG67rkV{SAY5UJ*fg&9zB^X{R}F#_nX0Ga6sefGc!QY&R~2RZVCs$VHji-Wvj)bcvS zr{csV!yP0T^Pk-XS`;ITheWUYK82u^2l;k^1ksW<>(1$Ylix$pE0$gk? zQRuiB#32ySvr~z9(8PvOqS~*r%B6f{ia?P!VLU~B2ddzN1`yJbzVG-8JT^?0qv9(^ z(lncmB1ox@KZrjyD1&u^Ym*V!%4Q__N6=A0o6S=6(+lckTc#J`W8W4(LqJ=|Gvb4! zEk=rekB-C+eGb->ryTt0HYZ{Kw3tUFh%Z3MIiW8B?Pt(1w81oX2gj2%PcAYC`M^jf zla@F7KsJj82zjUvL43z(mff)I{!%&N+86C#@G>DdS8A+CO+E0m^{x+f>PR`7n3X9X z4@u_vWLO6l{L!J19q@sTz2pr7w0!7fs*+LiJy%n7;sY*=TEJVzY@bn|z)GU}8!2>h z1dr5a>NUhGRQ9bT$4;E%5Gqqy8}3hz`Pl+0t;{24AON6vI)>wX7$PcB-S$&H^@-Jh zv%l#}=?aLk_)f|?1l+(Il331fb%UF^4MTXbLb>cwt>H0aY~pLE;ETN7ja52Nh_sKt zb*v8R7`s}n7{H7Z=&T*o3zI;w9T13ygCJt}iF;n44?X!i7wRjHlX3DbcWK8?@`@bV zOj_~=Si+MG**sllW2yPjT@6Y29?K1rIo_ zl8ul9h)$yIuaPz>{Sd!IK7i!ibLx=0;rzCr9J7;FtuOV`whxq9ALOeO<;Vj-!YJ(P z10+oWy8$PLigizRx#t+#14}b9DWXYvbHdh`>60*|T@bCTeA>Jp7!_+0XdGyZJRpQG z{Ck~0n5UQ$ZF(u?%M(=Ly>%iJnNYcxaDs7Qv>;SS9LDNm2gVt}<3EasqWXEi688ZL z=j15yIO2?vVt{QKARV!t1Z=ic!ciVGC7ZGxMq_xzAdvukI>Qv@0)h_8A*_=#JRA-V zyxt`R$H6WvZAzf?xk_}o^^;~hLLg&kd3n}nFHWIL+vEUYv!?+s+qHoym&CC{&ZhCo zoSlNx+y&|zRB6ee&rXU?22A*1D`mkGGN<-^mC}(qPJm1?5A7K+7%K~yJR-z28Bpdw zu-d0}wyXuTWw*E723HQDw(+oY+r+*C2SE8|Qc^tX^a!0wK1r=!+8hx2M1o!bm$aYfuv^4tXOy7_1WJ60A!N?rn)t20o@!#C>zFmiG3luCAjAao#)7CVfh z<_MajYZy)A8@%pMgD$;Nc?rglcgz#&r=0SNeBw804oV^|*ED9$soouVF`P--UR@Sn zImcqJa3dIyrhUHO22}7Lz7WoBCzLV=z^kvwh{L-Gm60|PoN|>zdwH_u=7aNvt?{Xl zI@7jC1uX-Y;0y$5{`+i?Z64En>tUp8>y!T zW}@N2zR)gl3lOw{wiufx!XKEVIp%+N1WozXp-C`lCJW78F9MRU@D6=*K*HO@^c6hU zsigEPwB?87vBT6hcxb~hc4N=blAK6Q+di~fy3Qm1dXe4|J~KGVEJ=4kJ5}F9u)vfI(cS+wvj+Fib8MJ4`8@+*t>u_UBn1M1=Cq(e~@T}p*sXs zo-h@~jE=6hJ?ZH9!}-J8&Y?L^;i5wa5{+q~sF=!Y%6Ir%*GahzI9;FP2zrYnM|K=^ zRAFWhDG1NUg}NnzqcSsk-4*jaRk<7>-T z;3BVrgj^;F2f8j2v&bVO&m;7~*VTJ%K_lvBxXHBC%B0PuG|oN+bRr7>z=DfCBrj|` z*5ptVT+>lD`we)xhc-Ek+!M3yCflXe_2!k=Kcl(*unni(wNEE>!p0dkDh^2AZx&Mv^ertCVf} znn7($fgx;DG<9x6HvG(Y<5;$}pSbw>u1gmp`(IfbS9y?&dMtt5HlRGjJ)u}kpwK+s zWk+s!yBq6x%}-Y0mkE66%iIG8OvY&lIcBL>2f4_QPZlF+dje>X;?QtG1f*bXOsPF-52Gq87fMz!h+Tky5Xv(Xp zJLmLHQD3$SRp&SrmS#kz&&lKxI`E+a!R7~?;@fUEsx zUC(!H51LPuCV`?4G)l|YLy<~o`#0dw7(l&O`0k(h6=nNMvw5^Gz7Q#`bI$gJ3RWA= z001BWNkl7JVoPr}J_OX{oK@NTWP9!q2D*9u49D&zbP!e)d6DrP#I?zj zVe7(7eT7$B;j=AH@#4jyP^2wRao18DihFSh?hvF~v2?AkRf=O){Y!fn?iJY00Ygtx-^r;3H_T8wr^ zjmjRY(%RC)pHfVj$Mvr?bA}w&J+*wW=X+U^*x(EIgF{75B+OZx64K~>xOSBeQRJ<>`>@Ac z8lb0!imjbeXu$5z@EkpX^d(rW^Ne_&cDt>`{ZQJ(7FkAAw<6DK%J2fxPp6klQuN;+ z`RT1+yN`WZfV*N~H5DZ07k1}>V(A>umyakKB`MyZ)8>6_a{D2V@>SRpt>%4Vwj!&r z-~%qs$p*A{6G@B9y9z?WVfp^3QE3$I!=O?9Sf1qB!SX2}u7%NK^84^`91i}nI^wSV zNpPPWV}>y6majOJfxobf#r8n5ceNrFcZs#a<}9L-mlqkk7*UI=p0VXX1=wTCKND{> z(SppV8P%MmvzHfx zeoZwzD!&euV{DR_u7IcY28|OCYQ~n)Yh?`+Y+xb1(`$~zcaFvAyyOt_E!Qn|os|bV z$%qrjczsJO>dGeTrvu_^Mu*vANV5?<{ZUz#rkz3!Uc;jExzJvJlta0LS)P?W*6KqT z8SfxHSY$y(1&6%Q840NCod(Ju7KdxCXWI|tkUP@e7OmIq=FuxK5-K&(o@i|lu~5p1 zne_TmfNIPJKgZmnv)Oeur)R(BNC6XK^pyd;Lq}6#_r$6g=*nrhwzf|F)H`>%_&5!t z_qqQlUb%E<8)`=aLC&WU+m+K7v6KdV#P@7+5HRh?wbT$F$^^$E_lGrIJnzYCNmMfq0bdDaK3_%2FpMNM?i0Wg1IQ)!lM5oMW=M8)tjRmb! z3=MOJ+5L^8y0mUe9{fuFOT#ws=yf;>_ua<<7}OZ&bwf?T2j6A*7kr{0YhmXhJ4Rvy zws9K8%O|=?Mntxf>q39-87`Qj9YRDqotwsxRn*g;P(BzhB;4@{j?2Wcd5>p*_=9bf zg#(I3l%m8*5o-ertI2IQ+}1mQR1S@-l6DVwbSNLYk%5?%27+n(B7B91M*V&$#KRw) zO;$<$di|sakVV%yspvB8^?P1fDZiDu5RW+)59}QW7uhzw=UJukIKG8Sw@01tosw z>L6Ta=r;2%D1B6*+#-rUghYBwi^e`EbVK%t9)+SPK5c1P&*(5k` zv&ihT)_&*5|8ek|CD;k$bH*#>bmbxmFP`r#D2g7-8T@{J>rCNp*+;p|p_mvm*yU3= zR5Um-ZLIy1joD(=q-0k!NOk>8B{cghIg!6Q-eSZ*{!+^zs#V)^qz2V^1hCogf{GYs zbQ?=dY2J6IpG5zTtZ&Q;b5jiKc#ToQOf${vZZ56mlTswNgo|Hjr;?SLRbw$_{iKd| zV~Z=sd@d;17p5!Y$?vIdTm8i7*?nB83U@UyGp6tRd4l|>uj}eZ4m$GZ#CfOIfX^kn zOcwSHu6JH=g3vHbxGX=!CM2ryZpK>GmZmoJhZ@}G4Utd};euD!3 zLW8Ptd}2~sfh>`(zRovFH`4BNTyzxO$jNBrKCux~E5kB{cNtf$qeR(A*KFgWtkO>h zC=ZirPERfYhOe{_GtcWtz)4Q&#C;mhM^}FgF=9Izjaimr=**;hdh$CKdr2}walBx; zQRp+MKrB-SKl9^)F)Xsmh!Erg92r&VLK&cU15`a+zRx-Fg293|A6`96^9T| zV$7SGU?}!hyyeK=%}LRuxB*S>l2v4|pJT)d{rgf4^I;n|pxGl)9GP@?sA%lF1cX4e zX;x_X=Sr`zsg!z`y1)nk@3QTj+8ub@%M`R{Xx0#ITN4>=W1kiYE3}_c?t)U+2A!4Q zw$|#91AZuPd2f7HM<>kmsCZqUP>A;jRkxLzE-q4Be<2qhCiy0ImX(2$lA@@z0&8%% zhW}SMPkUcGUkT&nsH?dY&AYv`bQAk3zQ$CKEE{Uhkpf~tCF?l6ww~-JSv2q;0pEuy zVp6NL)Ef(P2oFYyFo02&%*)J+Fs6+^HrTbfsYF~@wrLNf9x=e3vXmScwKGC!_zm-T+7p|G%q~Xkrl;^iK5kf?Cn>ppZ^t@OMeiX$ROOWG z9>ryRtdY+-t}!p>6ZK{l0bW4Eh~)@=ITAvq-A;9ILt3gnq3bZ3+NoE;`l!b-2!+fV zu4w%}K~X!A7O=FC-p0nzspsRII+=mwoPPd{NwL?qdG1OO6@J%!ma%nK$UNQPDDRq5Sb;<4WzSl7mub0#b~hO>Tgz%@yzNAC=@9da=0+{T!aKM5(hXUy)y*IAFZcvYlBxOEpCC=ppvlBfw_3ugOI4U9c%c^g@dw zY>&?Nf$N4!+#_$fr_cp$Hk81n-doz%)E2Up$+P61Y%1H$%!u^M$=01O&Dd8TbCD8| zB5(Z1JU=PBf<81Uq>6L$ddxfw2-j12<`$wgOIgk|p<~tSgX|9=^4OBWZ zzc5va;l}mD6Ws{OTv^J-$b|mdhL1(Sc}|*0ioS}^@yMSu_G?b92|xfwky>`DpO2vw zMo(XphFEGSr9?rA;S$=V_!R92kbv((o8Kw=4QcO}<{k%`$^0rcF^yIv(>K{_l($I4 zgkI6aC5VyqqEJI{>GKa+B87)pn>(^33Jl?;n}uvKWh{5UkWG-6`Ry_Z@oYyOXnOrF z>=hleE^%TH6HjY>2QagD7vpJU!lf-k7A(DY9l>MELIZ?zA#uQU;g~$5_R+srAuv5@ zCMaO^bF4yi%HxFiR$rprlYGU~c}o{9RxMPG;B z*@l4T5+wG>kL!Dy6M@b( z@)z+-&#N3stQo`^WyMVrFhL5OI;)B|U7lNL+CDa?8<;>x4JA@&(}>}>8l~Qz(uAw8 zX8M(57?~l3G>d{#+)9D`td)hcD=BoXRUwsEAhFFdydI_W4xSr%rQ-#@QY#dgW!>e2 zApO_E`$o}CY6OD{;h0Ugdh)~uMPL9{%}4;xGxJQ!(fMcBzxnq#1b8%xKH@#`H`94* zL7th@$V(SzO;4w&;}(#-gnLEW!o`ww<-4-+g{=-Fl8_fAm{%4mK5u5SaWHXICf$0~ zH*#1rKG)jLpwEhYWN)x?4s0Q>FbbQ_z|ejmel3_~_r{x_P?7833^lw=!$ceyZcSx{ z7O660_+ZsgFD)k(YIG7B4Ovs zB)sKvXT=gD#zp@)#y7UV3X8j+^8LEup+UD%N#(rq9iagC?x`{N?iyzQWw;vTC&|`B zRvkg=W4QV%+!UFkdV?bAy~0Q_s6~rF+6t63LsB$jX20{PNn~~nNvV)fO6jLwzA2o& zs1tQzFA|$_lm7@NS!$#Ge5Yp;SG~rb3xzO@ul#C>bz{SrN#GI8tK0}G+>}TynqXzz zM#?$RC&+Ot#r&F>_LDfqVj{bwF<~La5A*#jv#&v%2ddADbEP>|k7g4=Av>qI{4WM% z4!Z+VA?B=;?+~(X6WpC#v+h?J9TEve^D3dvKG0K|Y~6^Cs1ZbP3(QZYfWDrs!MuZw zfQv2oO8_;2~<0lz0`|1?lOv(jX96V4DiAt60Yz4;t zb`>WsF~+@BX2j{AYDu!;*Q1OssQ}eqKSIEu@2s#%mGZyM?uA)>3?;o}EN#}`YOo5C zPgx9p3FPD%^O^0y4l1;}wOY#jtWC|fqWKH68QFTe+!Er&0Q@cal`A-8H-MWeyp-61 z$K6O|bi~|p-SRFiX8!)&OeDc)^+KlANcAtq;=dI3Zil@9QYXR(VeI^p@sBFm36gk$ zn2;7utWYFnK}<~Pqah#v_@ygHI{9Z?kQiW6!{+KJd+Tl5ZNR+1$ z^O#w;FXs6XonxqQ-D?J(Bl8K>90oNR@b3E$_zsh#OQ&Y9eV5Vw+(L5rUvsJ_ zgF3lVq1r+GoWA#&o7--O)dfM!75%Pphp{LQhMIOFKwFXi=3G%<@3R)GbFKcB0=>p= z;wbUe6%`bBSv z!c8Qi-6Tw27BxKaVx}JuYenvP@uuZp}hzAWtMe9*A6Fkg`3{3$d6zyA+;; z+e+fLzUHb+YePK5H;BfL6Xft!adp^@oG-5AaVTqDPMg376nIp2!@L{fwDGSQPZWD1 zKby7o4I5uXCUQZjicHP_Qc^eE zsC0khbwUq)V(VrT2$ePO|6+p~!FI2@m($ArEoKugel zSzxB$DviOXwTdgfAbA^|&%eJt6NL!AC$gK-#dzpUDrJ02oc0r>M`jqu=)>p`If?)V z>2V6;kEM=@kFhCKP}{Qjo*w0*e%2c;cAZW@P8Dv!mZlUxo7od`*P)ZHGD9I2`mw#u ziE8+(0uc>R$ZejmgT2KEDW!gXz zJ<*j9uX)cSW%#Ymhsy{3w_%FWB42L{c#*2Aum!<3n)KbVM1zOVPMjZm3@}(X~O&>WE$7q z-LwKhR^8yk+=wa_yFv(`qJn*xfgihQsL|dhPoe)#Hv3&*At64MvPk9oi_Pz#o5}Y? ze&8RmiDCw;he{2QzRsVx#Np`VvycqUvY}2n|I`hWLr-bfNEIwO^?s|fWyM{?7?%JGH zmgvaxCK?vJ7vma_%4DNd>)IiR)?frZ#_nIHmY?T5WZEsO)c>bJk}%<0QiXO+K~HyT zTNrq?uZ|+-P7;=%r!u;p)1=+m=N>yDNb#`|{q>X~dS1~-oI_$RR?UH2Py`=axu0d^ z&HJnjm-L%+A-_dwR7tkz_2lKKtAktsznLL;G|_Me$+_Quq&f5@YiXlG-|Y!sr)jLvirh@GWTw^r_}?9hGn8Y zD!Z}$hJ5%%I)+d!KDLqpyRUBuqsd3Cpi7`l(&-W&+P4zep+++r{X}w>;JuB8O&;8` z?VDL%D$N3BGiQNqV`^Eb@EuC|S;hxd;o;v@LnR$MWMM47NN`sGl;g}`I;)f6YJk=A z4ndC8>-WA&_K;n)Pcs{u@i*Kq2@)=c{j(7)J{TbUf;(}m(CE|>Pr}wvXJx{St$&p? zon-?Y1`uk5T)U9f&;-c*8I3&|)ZojaOg?+USBBFKuTq;RpZPe&^a*uKy+8Ml+|gWu zJ!TmH+b(j32j;_JFRz$JhBAVKXOuW@OByt3*1JXV$Flh(;~O_BbanWVC8--TpoZ@N zjpnr2)WQ2Jepfh-Z;zqL`HibPwm_`D?0E++}t=UD z+zM5tQU8NyPZ`+H34soMuyv3^KG~ZgNLi*kl2L0NMF%GFEO~!}q5^I{jBTTpg^&ni zZ=jA~+9dP(Pzu{U9FX{3rGUTI9r8_;$1;lCTAeV>2B)$(nUoz!JiKyB$}Uw-1S6Y0 z2*y5&3=nrTa(|1ahmZ$kjmks>Ai@i5g`9Wu9rFs6#m*(kvOYqHD@8uF>JqYTom1oG zs6b3wuN1(Wtajy%u30C9K{Ay-OrG#`mXwjQO*mS_R+xjQt(Mo=Ww|cx9ujZo{)!r% z$0R2WtJ*4dITJ6xq3~cC!qyb&OXhm#s9G+)OXh29vnBy$3$yP~rZUgDo9SZ&e$l~u z4#h3=jbWd^235AV-m+Ox)YIcYz>WoV@ zipJ9>IG^~vna(0+W(`0NvZD1nRvF3bl9kLhB)&+(T`0$3B4lunAdtzb}bxS z&~mC?&qzgvok0CfJ!X4`gBX+)axosb^*bg1+{MGqwtVtmp7URap}U?bkpttlY^KO>IUe#w(V#ljmq!tTrwO1D zxjw`>#rYM4U||?_WKqS1CV4v^Q;#|k;hO^=SMjw45&@~UU$mE1R?6b+S=wv2M*X^0 zRw?~NB6ksTwtGy;;%_5Ry58{^Oxu*hq#UT52vs2dg}1_fWU;eF~s4n^&*igw`kHq zu2>qg^!_#@r=Hl|jHf>t^thi595kJyA%Ze;T?^KYn7U2)(1 zc0Ttln@;I#LS|hw)>~0b|NV%S+!sf`;h_f8`sufT82spyw*&#N!hD8MmT;2ZC`-`vpz{ndcX?5vL=m5L!R)(sxjb+#q7vWK_5hApoK)Tmd=P`q0c@Q3U25MpD1s5DnwBD z=Y6QS{#AeDBSm%;?l_hmSSR))oaQ>*GY)T`Zo%$UvRPK=9)~4n{g$z?AdPy*!oJS& zlmwE1{E?E8V31n?Q%yL3YVd~OAR!AeMEWl>hsQTM%%{K^)7dwj$0;0X@{?z-G;!f= znqAD7hL=*D(#_sq$%#eL9K!BVf8&r?;%c`A1rkRXk|VRG_d9qivR@hm;A z?@zQ$_I%?^jbZNT0elnrC^2NyFkG=bLgYn!h{4iS&y{9TR%fpY!c~?j##Cm#=k4*q z^xxOvlde5`IYmxM_|euku|Moku^zo3I~S201et8DWp*;8CfV_t^okX(KSqJFJUcV& zH|X`R7`ui6Xt6(gp}Bl_E%v1>^~Vz&7+by;zRgHUpT9(fA>ER*LuTcMNWk=JMs_+S zw`;*_WFB?AllI1)Un^rFEHcDotzn-#xBWg6U9z`oE~NFP{Oe2L-u*L~rs+%g9%Mj2 zFByjh;{MLV)_}A(r`6>dvPnSGk8K4ZW)d<%#l-IDpZja8D(<1K-YTu*f@mz@=lgt` z=TuKDOYN>SOCc;YjEjMm?6>ufZWJu4D}1YXP!J)Iv`iCc;b|C_H$r*OIuoULMDbQV z9}n4BRE&A!k7^XJ-Y7DrykW@BXgl+ zQ+1EBrIe1Hfk=n42QiLw%V^H}nYE#hU%AoWmVgl_`6EiF`VYl3%3E zh^{Kff=Cw`-bZ8fIY0sVfV%_S5~b(6U*_3khhJ0OrTc61{ueIz{}yM$%>_Y{>pPL5 zc4$#5Hj_X1r=!p|>_@Y@Ln$=_tUk(P9xh7|cMZ*m@&x*A+4@kZo=#^?)vlRj!eyU! z_%wzExjA!FU*Gr9z1~qd^&H;&a8vS)K73^gr89;tS-~WZJ8?HblTpoM@+}fWD0s3K zuC<52>LI4mx?VkZUW;G1Ecjvh`HF9Qil-^(Xqk)x??yoMNm6pjF9w~!WJz zUu(bj(cm>8O`D5bC3D&Pds+grjk0x@e_3|1rPy?|If8b3-I~OkOk51eDN5-0l;)p- zhj~H<`}~)S+_R-Qt3oZ=g1iTrx+s5?WxFlEI}l~Cg(u4EUCi}R-)#wV(-WBBc#AX> z0+cZICnRCyL_5uCR7P32%DuMK5!GxWPQy?nE(c5>HhT{qRGPen3j;Yhz=!qX9YZ*e~@R<*gtKW8{K>!=EWf)^NyM z1dBzIuLMey$p(Tk#zTy9uqt@}M07=mG1oa|(b!o4GJmAWD+(r1p)J>#Sod=^G72R{ z6Dwh;{K3ciE;(KQv_U|Tog`6emU||lls;K3o}h6eBTw;-A>A;3qctchfSDDda#XV3 zrsvbB0~W8!?k9yr?J^zPMbuNi{>EeCQc?6stN~mm)0~D`cEqcCP~IkHM9ab3duE*0 z?bkl6&lOGc--y(M^a6oFv~*9zdq zovkQ>T?PGR1tsac`Vmc1bcV(ZUYjDOZwujM0~C-C2psIASyB=QMJ9K%S(q%+xM_7w z9euN3hYja@%=U%FC$08B)l&K|%9 zrFt;T9dQV@tqX6rAm)1|sB$QV{{v7x;BBjQPRN zUJ`YiK^KuR0K0M8C4kBc-u=jdaNNedcjT*_`J-KU$PbolC!6-I_97-;1V) z#-@PAlC6DUa!uGZEopyx=&Y3n1lYd?*Wggz_WykB`RS(NqFH=g+JtVF#>m3$>wxc> z$K3ICZQRYQUA=hw8?N~&63&>#i6B0_K+7x>--&MOJ+5t6v0p3nEHC8UDN5q(f|R+Y zyvQ<>F=}O5ULi6qAuvm^<)DtPgb;4%VU>2SJ{T$Yf|ZOCcL$Cf2Vc0^mv{1THE;eT zBZc?=ZtSmH4b$hn*lN^4sdSfPk2w^RMb7p}iYKn^DHgH&Q(8kTdyqiq1!a9om?rI2 z^Q7&|{fxg2#*Zf&JhVkrNF&>5ic#&>`fRnBCx*gyxj4*apW(x(MIfG;BD^DJ z&!}6An#ez@j>;9C(#?aaeCLuk{yu3}sz*@}m>W*dN1J_Y>C}arI~CNg^ox8oVlc(NTELszbG;$;F=zBV3G^x$^Bcn!`LdSIDV^0Sh-0k%$z1qulQ!ZoX3;F0yMCeGE9!9=G5uf$*IR zN5Pu)rD%TbPzf;wxuGJLbG&^N)9x(4F?K>Vv%{~FnlBZgf7RAzJ&~(DIk*C>N)9_I z3}*9^1qw+mJPFnJcK?pG-Z-{>$+_Je?Ty_btsLSDn5ON12IlRV`J|5)dfR}Yi)B!O z2!cW(TbPCky=pJr0TpJC9J8;a&v&tw&Lu+ysRb`p)PqX#0)kYyn zWYAh!lT5xr7bQFSJ|eF4r1o7yu#7Vop3jU1SU>aB{0){gqjr)Jjp)sgi!qLg44bPu zJ3dx{zso1aHm(Rz$!YT{G9+TUJG(D$JoxKr9Yy0#wOq=F>toBVwbxjY0iWav$}~h~ zwYqPMQuQ;&VSotK9ZU@&g8}oBVrGe?fMg4_>3O-^{AT46zWEm)IK*^VySw!U=0`ZU zI%N8ah0CzMF_qKMBs#6899`*3mB=2p9B^Y%_#=k@`-_lFdUEBxFuq}Ew@MYN;5Bkf zLTufFTLB}G)ppdLT8e?tJD6cSz1nlF;H}&5Sbj8~&phh-BO5x0D=`FP%B8rL%aTzm zyanHrS%$O~AqeD#c!kks=jE@1)h0&XLf3_Dqr(bw8V)+RG<)&aWWSrcJJ9#oxtv3M zasC-@hzy`mHIL6(2{=~Q7`M0bJGPbSR0;h_<?Xw;CKDKCDw17<8O_u z5AF2YCft^y3XkRU{$r}_V1U$ZwB>6GO(P**zio*G;e^7LGo4-KwAZh>&=RzX(LYW7 zkxRQV;(OST%UiQaJj&Ot54J2(Ux5Lj!$ zE4=6XPk?4?DE~KRaKp!q@wGz*&WZvZ&Z_tzPat5uHP*~kg(_t6J;O3d&%4yOWXA`% zuPr`d4;U@|QhJnsAR*bS_hpSV@4%?H6ZXj81YFOEbe?>KLsDMjaZ|e6SwIk0nJ(ns z7XA$Zuk+7~aDJq;A@6GbBz$l!8+;(>%jEEuIxju$Ol0KcuBzhO4%puv+D|XaT=v$Ge3G=$Y$bF0$0pF?Vam!OXpYos*K}2 zWV~(x7(U-)p-4o*RZYG?`?28$CV2{`-m6y<)5r23JSaSRa0?Wh%ON zOehJa`~P$LW4eWH(P+;zk(O2mbP0Z%ctv$+$Lq9`G*~Ds`IW8tlNcP7=;A3KU2e#X zXP(^YzCRA}dxE!Zh0;}Kc3u2{G*o%NC9g8lh**bKdtNcpYE;CDwWYEfzL?#Cz~n&akVP;c=SZ3TOe7pO8!u23-Vc+E`EKH{LW;+XY@6111#8~k|8LPhL^ z;S5KimHe({j)z8&vf_C|1f3%f>HuGskk4k2emLs$9GoTe7N;4&ao8r+ormT=nnDk| zp4x?>B|-J7KHaccY_n7snFBzPl>74FGuX@cV$Z;LHUV@tn`D2l=pEy@T-{d554@+d zKOPiUcbB-oF}N2afRn#Kxd2CXGw@c;er~hkcrhuyN`%_=y$y2vaa(3}nVQkV6nsNK z{Hxkib;;xwTA}UT z=AZ4c=)-*V>{GUnp7mdU9|UJ@aHdZnk(z53V-^qXXeRNimH?GQlE79q@|IFSo;7NO zsJ80AU1og_4LVzLnbyeQQ>kEe%oM!e-3d+q<}rQ@B~&Rj^#~V-w!o+5Q!5r-prtfLj^+}8@9ckz`JT#jEsrhq#;|w=nCgH3;_|64KT#w~7kIMm*-y2u zS;B6r<89(^xqA6Pr=cKvsBnFZF7R}3Ixp~i)eE0a{cxNabdxIE$0BioY^2k58Y$ME zoO@@>-m)?0`50MoROnBw6CrUB0@^~3XF(W!6%oH(gWM&+=16lp^XE|;Nc+hH&ta<^ zV`_n?TYksyoXfRYYRa`##hPe=aDw^26Fwe*%w>ZhXc267z52=Qv@HPGxl6Z3QV=*n zK_JXDCbMJ(J{9Rk@vkn&F}YHJYO4kgh@hnA-;OInPX)Ba9)-G$2t=nKL4%Fgnb6dA z#CF^EghUOkUqs3?zr09I>GR`>&4R^vSM9jaLv0XM)pGY*!{)T#i~~od3U5JGnZNkO z!*LI+RqLsO9khMrhjk?me*6G^CVn;T>4WY5G@YwFS zN$v6O9r<3w#w&N{Ond7(oc=w~#I^-IC0`nsu%T9_Rk;T--ZkisxJG80O$Ar=;N9ZB zQh5Ti2SuXyg5lxb>xlPfgx)=^ZgAImg}$ZSOLVYio>ja3nkK!vpZGZLK?eASh?1#@P?0Z1D3LG)@$K?j?PX6pQRUnVU zrZ$YlQ=1&Tu_WhMqoeut0GZAQUh>%y;$1U@k|)Asdw`18*zJP)qVMm;>ctlAhqV`#G*tX*+D z!;)a@1ZCWx$P`>}QjO#*%I zXyV&3)6(m}vt3UOv5uUx8akEZ@vgW{(VGtRm;PXq({$fUbSu1rI(w`YAt$m`&$e-l znG{*_6VyS+?Oe8i1k7e^Uh zw70HH;NzfE($QJ`lQvD@PPOopL}gh~L`_+L#J$^=^ZfT?32gk#4*xEb=(s$msRU2q z^Wc@MOBzpJLI0eKhGxZU3)B10>;_G>^Q%BW)lLG;psBgq#6|yft&KM=Ux1^vI5BiQ5Ly zT|DvRGSx#f3k(1{H-s4KNLp;&PwJbfKM42{*!Ghrof68e}UQi z8XwQ6i)C6}81*%gRP5E6aSPK`U^KufW%3!8lJ|-*2elxltSz=09(|BD&T9=0IHRde$hs zFih5zaw;YQFSI;+W$4rusBkzsm5eW1VRQAXnGO%t<+=B#*PGWdZ9Aj73!!mBn@BQ_ z6|iT-%UdDB-b)Z3Nr03wG5%i-RC8@`-CRzeF4&=X0p(nW&&#lnIY;f|vSaXK$Xm3S zZKCB*9j=*x`%QvnJ<*s)qP zClHZXCu_>|FF%49je zOWamZ!;Bvm4=dnAybJ5+X7JnQwa;{+xAiGF#~)25SpRt=KX|&}%fWtTioYPUqTRi# z!d&8Cf(p3`UnRKPjZwfmlEdInkMqORAdqc;qQTH#j*={(g|Y9ka)Dq!UGS;#qd zB4E`!5#4K6Md0K&TSo%YgxvX4RxQ{p0sd;E!m~XQYt>f=__~H4YV+9R1t@H+8FnA8r ziPnujoc{?7>b&`GT6HcK!ro1jTSYE4`d3#-NSt^0ovwfWeS-uEOlc~xwa{^oatWZdkHI((x^@udYWCco^ZW~S z?Bo!tKSQn(F!A#3+<#Y779@H~e$>Ce;*U)Uo0R}x%R!r<>&D*T9c?J-hKcVP?{SK0 zr&8yO>sHmcQGIjAUB^8YT}^Y>E&6)-2fzy`Cip!yj8}CrIQwl~n`%isl(w_vbr~Ce z8fkv=hf+Q~S3#%ZpJm1F{{20wpKzZmjH{Y^v>aKhTA1taOH_MWN;(?Iv>vCz1ZorXmCpBy%7Noa!eIYDLnA=HJ6o=R26aFV z&G}(p#Xfe<(L-T8TP(3I6N*0^SP|GQi^ z(rLxtcKa==cwD2q63omza48Fo9+qwlx8 zThOV$Q-s}3A~f8Mv!7dRW|xBwR^S!UbOsC0W5-8!T{n-yn-lALuq_^FZ>PvnqId4# zKISp0bAVyC?-ADBih#r50%zF*_#)A7D|$`u{IPZPNN(aWTX>(lriwSm!>RK+dvG3o z|1qHRqE}%dBFFwWsFvdyzHrQ!`)~A(-s0UvS0OEYMK@GJ*BM^6KX*@u0Q=n&{4r@W-u4j z6t{EKZBqTjB{F+#dhy+G&hzYwU@l(#*!w7OB0%nGPHnBmD`2y0fjAJ6cI04)s5QC2 z<3>pHg*csK0LXtI^`O zl@;)<6wm9N_BneSd}0fs?Ueejxi8CZ^Uh-MFvvgtX_+%!Xvs-w3GhDqh;5?90kGbZ z&tD}3I&-z2Q%F+k$daQl(gkI1{&y8L}tFVAdJGSe}OFPp;C zIu3k2dltmC)@v{8I7{~>wuTHkFYUBB{k@)`B^(tEbOCO_-M>M@p%iM>x+~WQ!d~l3 zo^SI?a)A57yF2|Li{tsMN!l+9HGzAmW}>YokJkhtxAO-6xw)m@;;=u$``YIs;Wiaa(s>5@}zHGwb96)jBu@8M<_V7Yn9bVhD()sQ#B zw9C%_VPT%CLe2kI1+*-{ojmT{&n0K~T6=gjw;{220?}gEbY;`wSnB!l5(<96N)wdz>^0KjB0GwvAzvX*g9DwL7s*AJao~|V|_%hhz++snS#tW@y zD-O~lW)!U|@{Y4g6lo{#r24N~nKAarH-iu9A6r+lvgHD6MdW|WoXHtY@K4Iz8At0R z3q~YD?>@22(sIPt7<42{y1LRd+1gc^{0rrvo(oz&%Rb?|svbgj2W5W_LO%BpMYwqT z`dx~C{!d2wQE(}UWax4!Od5RNiEI}Nh{ISK{8Ldjx6~>Ph+2RTi)3Bv1@^1s702@R ziptp53stMSy6d%snCr>GdxaJ{3!hF#8VF1xByfY-M;#(Nh>C6_O&>jbyUxo-kIRpJ znKJhq@P5v}HG>EMt4(0jX$$v34|vs4xdT83Z2~Glb0PW~qOk#dABl6^dz`jFm_^}NXh~Qfw7;b0mZB{Q!^;cEb$lLdaFy|X`zd{|JV+UBo+QAPk?4D0! zS;}cHi$Z1+s}VSIH_%W;2!nEUCGo8(U+0(YRE+`AYca-T!V`%rJiJ8S}yS z6oN3qd60{xW%aPGx$lXbu2msxsBA3t-B_H~_V&2>~kC$hsnVRS#=oxtahYP?dR35mM5~Pva3fn&3 z;xsXizu)AI@=1U6gmxb?B)3w5_2`PvRtgqli{pg>Wdx6B1xLH*L3&RM68BSW7iAM= zn1K6Oc*A~ew6K)VU2pruJ9W`E!~x^}!fK1cLq_K9xiE*w{lhWA^M%RD7Z>b$A4R}( zFFJHZX`*sn^j!DYgyjZ+g8A!zC&UKlZ`=b`&XW{BS}hK)>0-1|+R(zoID9?vuYVJe z`2SdJo@{yLGc;fl-ozcpP2+&Xhx@`3C86-{49LQ~!lr?!MC0uRJgbtCz86NZ5;Mn~ z?6{U6RMB)I%+^*=eT$=~Unup7t|p9y7&+^sdnj|`t<+4Fx;YxZUPtHXBo5+lr^6W? zN7V;%&W(3qw3v$2h3xoFh1L7DwO=X_IkV=Zhd1dztG8PI4{7floN2eTk4|h%?99Zr zZ5tEYwr!ge+nCt4Z6{A`J2`p3{q6m}`*-TpsXC{+>bd`X>RzkwwfgGo>h9aXJ&6p# znGKIYLGxD=Ud1JCPL0|lm(bhV#y{Z>k*?r>7y)sCt? zGbS}1nqE#GWEZ4UtSa*-)s@H4s0>K$%y^t17|z-=1`ML#jJBFfe^G15o8Wmru1+Lu z-kNO!e`Fy^`?`K%cqFeyLr<2V^QnU z7@fP17Y(+zQEr(ktu>W}1n>LK`}vfPLOkaK+NBPaE!9hpbIKOO_qWz9yQgjzk}hu6 z`A^T4xl^ZXSJRIDKH6$Vw2Kl5&!?|EOD?an?B<`E&A0ZUMfy~ei?o>e+f7e$jp8l^ zPM%M{m$r#-kxhedoj+zPH(@&&M{YnL7fZ3={#4=vM?cFMY%t*i5r$Wk~H6TXKyNWcgT6 zLYdqU^z{@OR5>N`CbW@Rw}kYsUyfzust{*vc++5DaN@vbjnpv7_tC}CVL-W110PwMdJsR^IT8vLVVh_{O7^Yjrt9oY%zk^1J<1Jp%PH|5pl zVZmu#$p`b7#zR0%CHC*_$822Dqkzsv#1)$MJ>-*B$`4U$9(yO;Ov#`0`=G@FFy`;f zoW3jS8W&fAtF@=oZ8@*a2xmY;JXJhieL9PS3v1^0BV#RkN}OgFsUf)Vc%3crV_K}1 z>A)CLhNAh3y=$z1n_!0D@x(ToPM+BuVjyBK=7uHL&x9AOay=9e|lo0 zVtBCX8q3$#G--bqzK`I2M^`S+RP4IG9B%iG*t=1KE zey+1FrTFY0-=E6*JVqYLc09?Rh85G4-KVoGjmUbdbv%WeEJb8}07~DBPjK;{y}P`x z6*KQ&O}sDtUvlS;%&P9zhg;sn+E~&xl+&wPXKp|K#RRWMgW2ydvPI=gK8-DHH3RLR z2Vi=^E?>)7P@*BJzRXR)Pt+J3foXk+Z)04=)$)o5*p<-Z>_B5e-R$IP_RMu13a};!X(!w zSxDIj_>#n#FYCsa6WTs~B*$hVjk)3G#KL?lgjYedf;%r-r=e`>CAY=MT1BgmI13J& zW!^u8ySXvbmg^DQYV_gQ>N_q|iuIdFa^=SA`7EehH?OJWtq-zZL5u>QY-8ZAHEeN083#vOd&;|N^RZ`W9?zvO9&PdaP`2vW z-<{odXcN2`pUn9i?*`c(LI2Z5>hNwyC3>f~+! z+D5!Ci|xg$+GYA#>=`Z}-ye%0m#$N-Q?)7e(-lrm-mLqWc2cIf4NXH}Hl8+aRBC+= zDr#N9xo02lMy*Lcv?2;Lr1=$3VSzMX`P88ju!+Rk`9W>#lCp5wn<{q#s_NVWyswEc z2=D~tJ%@XmK$fp8>csw%#|gcA>%BXy&9?>#n6kJPcH?Mt+C2tS?v0;a7(LJ7YMOHS z(_`;2vXS^PXDy$k@NSD6#SaQyWe26`yec?!43ihy3|=`D|Ht<`~9sMvyV zMxM;TdDB+KI#HjX7=GnJnDD-@aybGzrUjZ=4{mH53^%pC>v97{^9S4f@_{OPcG**) z$`qkvA>iczxgD$>Y?B$LzH>In4nL#!5+geFhTuDg35J>xXzq+_)ClR*d%mX|?0}H~ zzdJCsPFw4dhIoqaY;Y-YzrDXSm~4C_4y0Iq})? zRP=f_AzNI$J0_Fo*)|;xIy%{jV~TCb%7h(<8Mb=}p2&WS@^m1VJ67T0aCuh`E`GY9 z`dkMj4fBkGk1b8tn|=G|`(;z}=-tDEeR<5*PE*%=<$&Od<4Yp?sMZ-r$Z5#bBia=j z8zao!jokTrmm42_-E}66r@mm>DQCF{QS< zjb+*29OLL&|Vv?fv;E3)8L0w|$!c6W@DznrSIp-4bQ@t5E5>LDf zyiuKhEI&64QL9e`Ydo4(hBi6vKK1sO+3ryPI831d%6tn157lSzL&KDwXBI#Polq2m zAgt?GX@toCsu<2M5vFZq6M+IMZQ*K)YXZVHs-V*8vvL(n_(dG^ns1@7$&-Y%d_747 zZLdS6V32&2xDfWMuqY(z*KMl4nH0kx6fuerh8$7Nw%mM78*S`i`al=C^#Xdv`<;#8@ElQ!-a{Cj1+ag>jXD$U2a$5rgC&QHR;fy`i*cbZ4d^Ix0yw_V}YcO!UTfk>s&0Vx(q)wl)N zbiFrHk8_VI)-$zo#q5c~ zKkp`e(TTerjqd-lZ6%n{-9S+)0wL82z6`}_D?%r(wiYoXQO4zz&aj(z@oSlZ zHa>;WbgdBt;{0tEW~Kl}Yb)p|p}OxMOS`S9`Xi(2P{g2A9@d!kMhVdc3jQiOp(AoK z_3%%O{eh|yyUU8cK~1LB_;7~}Xqq}*A5#M2R@h80($|r-Deyi2L@6KC6Wl3Hj{M00 z$U&SzCp?jqZoZKX%_I#w?;z8+w8IP34?Li38mnrs%m5xqxtK&j+wu>FjR$Ox*W zlqWdrrYj-O`?2g^QMz^S1nU-8N&f26YRzv9iCc^TQ&4Vq7*tgKkwO!A-h<)KlK0A} zd)T@o+WA!^IA)kl;L$x0BUs=OYbm3wokF%rXvM@H;p5-1F8jvcHI^9^u1Hh&Hdu~} ztod{N`3dLtogB%lQz9qJ$3S1gb(L*G-m7|N_?3hLz6&x|G{&j*u08pW?a0{zo`fQ0 zfnJu78$O-iZtVI#XvU`gEJLHlB-NBO2=wc?s5Uoak8a4MF;*C!^Tgf(FU-gug+aV; zL7!u!dHaG;KqVh?Zd8FUO8~7#a@$4V7B;LpT!R9YqKfbX79PqMb9{-1o8|ngj)|^u zR(TI=e?Ht7BnK9)7nJ&>>)ugU*bZgpNMOFPKCf}ztut?5R(FkXUn*wzgZed=ZP`C4 zQZzzE*9GBE^}hE3YJNpKj?n;EMe(=&n6qti#^YP1>(1nb(9M@0o4r4|x-6>WsS4&s zCIbGn`Rzk57yFZi=Yv_oT<*hR9A(2a+;2@)b>cnv1eM2#C~0AW?H}oG#W-H@c{!vj zU)3E*o370IQm=7TUg}z?FU?q+la3h~PJ6?(y<$1+S3H-rKTdWY0>oO|4cYPk3U&T% zY}_z!S0TLpbyGYMl^RXEqP#EPKkjutQ70xQ0-#|YBFm{r4H@}US)?2z)CE+z$ZhhW zr}s;`?mg+x6v(B4q^?T%h?kAm3)n{_r23P|xe~eNGI&P8wD}7)lxjIr)kg(>qBi*} z5g6Ujw2F`ofiVkWl(m zU!GD0H-%2itVLe~QI2AxV2mRQwE6exXtvax@9z_|v(tZ!vL6V)GIrNP<+iZwU;QOQcGD}hy7QKskTA}*I+p~*@)V(dfi-!X9qSXq-( zGgf(yi#-0w{|>UKDRqXs(5eXGI#p@qBFDhj@*gx=;@FUY6lk@te%CJ+xAJx;yeL-< zc(^aChO|(b5Ops3G4O7Aq9}S(P;JIe@tKQ;OU8G<=M!O8VQH*J3D4EEXkNbAioUU- zb#$T~IU^CK=_gCu38R8*KG0g`q`GOSe+2h!jr&=}2IxQL7cmNKNZCi(X{r~F_B}m4 z`di_1#3VU7U5a0!!v)k869pCcoM}y}wBWc@0ostuNpT~aVYI%N%F&VHMA~6VOZ@t~ z2=+=SBY6n}KjPax5dNH_W45mRg(QT#!kNE>WXuS}Nch?{Q>&tswBmEqbA?-c<7lgn za8BuGk$4)k%cc{fJ!+UC=eK-Tsi|}BE)|MsXZ9w&UpGvB?Of@AF!}h%z4}`zLO;vI z>DxrQz{%JKZ(k%Jo~wBA!N1^fCu!_nmFx z7g9Cp%OuW9lTCEtX-E#*+7w`9?`z?Z5W(l|P@Q;e|Jpv4w4lp%XYB0U#apNMpg*Wo z3JDM_Fano-SX^F`FaZ@#?5HSfXh_UY6SmhUm2Y63cji zXLecE*B`(kY%edQ7|4ulq&f@$am7*CyxDdtDjxnp=zt!Sq5Vl#F1}h6tQmj1QJ}Hh2%DaiRB=m~5Gx*`wAv;kQ%u}X zN6>Jf!m+zhhN!b)o22`-mP!KFZM21&9RGHI@VK(@2o-C?$RM>qsyAYO>eJPJzh^AOCWy}@rJ|Fn4HTBkojk5;9^u;za!YiR>Lce*z%)`I6{ zg38XILpXn@pl)vqZG0?D zgiay5#RBvFxWMV2EswU|FI-AAA3>|K-t-I_A!AO*h5?(5>&=`2Da#X+jt37R)^nB@ zQVAn_z))`XyGHheHY4PaGeqel?{xzX<^eVMBlHRT_Z8I6C?berxbPe|;{^$H1_OJX zY(cRCfn3kvVE)m3PNY-}nMbCm#PxG*Kyu|q1+(n|k%k!n--=YZoRW7>BEssuYi$q- ziW;s$d{QtAyNKNo_=|zZrRv(j%wtJ^6AEXCVWyP6%1dLm+G>#3wmLeJV)U)g!E;q5 zR`9hpb^62hKT7^4;?9Jmq%dI@+BB9UyU+0f+@9Rrb}2&$+CI7~gd4-VZi$z*BQNOy zgMXp@zkV<6VBWSNc)J6kkYUllO#gyj{CSf>i*?`RO$1M~U?aM|@*~%epQqxQ+-7A5 z7vDieXOWM7%=pS*iCsk)LgA4Uu+57EOF0To082Iv3tNgt6H$po9>?p+TTR>ftXjlD zl7CHJpY+SSv)CjE>DZ|8cCS`ag{hHU{?_z0reZh`+Mn~ht3=cHG3 zpqXuN^?+qsYp;^#@`y}?5tzm>#(SFrnS0oNwT#J399?`c)tyJwlz|37$!H1|KD)AG zrWX*C66paLo0uR&CLmKy*5*%X^now@D+l%Ou=hxEug3fHIr}rLF9gBi+Kd%F++>> znUtvCp{0AGI;&bYmH!o>nV=2t0jmM+zHWVbV_Tzfg(m@|QE<#5)gS5bdh@Omz?-jpA5A^vkiz&VNg=-U%-c z!5C>}w2FgK1o+M8p_7}i-z&DR*D!!e3@-e@|F?Vm>$y3GhDZhd*~ZoWEEK`M^L%8OR9ngOcTs5*W@*V{cKSo#Y%eMgYgsEONgj_4Fc5V47iR z;nL{WKuYr2qBSfXm9b2{3NCD;uTzx9iNA}?p(f~GFf;&Jho*7yzUeF7W8xFxHU1yu zvyCtV{1cWV%U+I@6Y>i>n8JyNL}>Oh`4z(Ck)TV3xP^~g>;GE|@cc3tS$0Oh%@!^p zLq+lnfb4@qwu;OyCBrvI{FqB$2wps5Z<&x}2gYuG{uC1_N~}aOs|3ZlC_K7?5yeA! z&L+;7n1EI+=%U5d7$e-kpxzdMUCE#&H+*eMsF=)BoqwT_nR5pfS3a*J^mFq8!+47h z9iht+z|~j=0cXU~!~rtUa6xs*SV^VK`Bg{4SYPO&wIgwY}-<>5c@>IzNF#?KIO zem7Y#1b(NoX|on}iUQMqK~BA23S7o7;~08boR1Vz3dv4a?4WjLJ;#*K&66R;Fem!W zxI9si#Q`>T9qnt98`cM&o}u`_Bm`NKy&wl7iWdbpRkxHLzE-+Dr$WsEBhtG-OkSdS z;D-N=^0dU&U`cR^aw&f$oHFMOe>FF*syvP`Q!8Qvb!7>)3tpY+GxG8vTuc58*~L~Z zLRbK`>ac6QE12^wMzGS^%RZH>Ct#w;~-A|$O z$fD8yP`9VB76{jrvZ4z%FV43EQI4YLHM{;?v|6z`pAX(EeJKVnG%voX%3#J%dfY5M z%CY@Z3A7IrQS+k139WBArgwFOM?!^%&nBRmz!il|%5f{0%7|y- z4ka4E?X6!!GQTjteP5nOHew?xZAR=2E>rL5rbbv77XngJUM*^QIRi8y@h@cRc3>ZQ z)N~RJ%+iu+6_y)uE^*2z1sY{l#;<$|p|9qtmg0}OCAR32V|CQon2LJs# ziA+W)y}@8=$~EuD9`98z4DEIl>n4U=L)2VtyF)h^Y%+EfYtX zQhI8!$^y-ik|4@bQB1~oDI77OWhS$xyA&D83FJnIsdx~l*Rv`u zL4N+zfyncb>j6`>IO|406h~T%Ej7zRRZmC6eJrabUJxBmD`?bI6|Q5g14rgxa5P;1 zb`IN4X8^0dTE$Bk=OB}}zi63c^-=$Abnl_nxgqcV$^pF3Y0C=@dG1a&NU@<#_8Kux zXJEZ$>eK~K#9)ucvYG}*V3hPP?&RN=VF44G?pVLI+2N6^JZ&%(J&>fLZ5Hdgpscb| zcd@E2T1(sYR%^9g=?qz%!6`HM5hw$$I;yV=|12^0+3N^VBqMW{1nY@K?TjZz|9cKQ znX!P88|YA6?j_)*If{VM5ezDlL1QYHBEe;PsN|c1i9iS(;&s{T)pOuvU?Yl(43UKf zmx7q~@LqdJ?c}#aY=Xd$rwzBIiHm|{F${9Hqi6iz2QSI5KvIxFY{op6 zpTH}*tG^SNN9{$4T~_07=*=?L=-zH>%Fa>R0`Fhj{tw3KsLqz-ki8l05mb8E78ZKpjXgC7kT)m>#Tji0 z;g~9HlP#_+TXCDhxTp>p*be29?NJ@=Qj-^va|5nN-FTip?-Z2jZ%A8O5$$inBjRN0 zThW8HzmtoEf7D4@fCKUYc5int#-5gOAi43Q68+_6p<=K&7w^aI*#+aLtB$y6hX77p z$yxQ<8eG(&Uxd6tlU0KZ{cDZeJ40DL9~q@jWZK=c6uKrVgvKM>Y=+y5?E4OXa*A-D zgMv&CGbDrF;u~CFI3?+lLO6;~be3F3!wR>^2z&_ctA$oPz6sP;W%MB_eOkE+_7+i z!{9Z->Y1=w<@Uc>JzV|9n2;r2BuW6v$$kRq>M!PSb}eHGFr$@Olqb$ks){(DiE*8j zW;YCMWg3{aZ}t_2$yp$wynRgn$ix#Trc3uVq0E8up|ITKbptvdnmHH&g{ei#lr4Ej zZ&+)Jew=F`N?5mRSCJdZe=9+P>dng}rpC;A(vQ%9EFuMsqjEUf*7p2d3(%7X$D$z; z+t|P{pO}b-C3V-njX68*{(tCU21vAq@~HLzU?F~@OMgM2T%bbDKmgBJiUzGM=8YP!p^%ZraKVLG ztQC~M0GND%)x~i0MKTsj9$u@^KAKZ4$&?Va~2?08# zV7!3<=c;-njB)B>=moO#U1}h5M*ieU#7cE>W{G1QfnOlUnBxGNor^?t@zdWIvPGoJ zv7p?c<}nnU7n;eQdB7AYJ%*8zynx**T$^p=F88P$SWOmVf}&`A=y#g;tQWk0UoL+= zGg`nv{q?D8WL-0%!+}LcGO%C)sbsL&6eVJ+Qrd%ZlY9vv9itAEIm+suhjP;Ba-oDMyn3i{FS>-M1|NEBvwo;?EO+jM(809K2Fv_!0|T zmBQ9Y7#j8UA<60bGZ~vh=kB)mf7MQ0CxQB7w@8B#C5Ue#1D$+Bjs5{M11weNiE7|q zF3Q~MxgkoFN}i@9Y^Y;BTKUs-U|(KI{NQ%Tkxol=ShZNKg<__pE;^E3rYV07O>_zq zXA5m-7D2o^54+JAceeLN9j-F^Ci}m@e8pi!j1(f%#zg9Rpc*77KN+ zicQ?0$n}DRN^{XD6tlSET2uA-o8&}uwj4_t&FdwR_eVj*t@vXE)Qb1@UAg@mtvGG3 z@_1RE70eC?uCiWO{@%{^S2VhAm?u)ej}5S(ojUeC|00SgkVZ7vw{gG25}HKTUIHmK z9-Th?>m&mzQd;6)%=paV70(eAfLMlRM)BAZq~H8pasop+3?=-(!F^|kbLt^`ri-u@ z2K`Yes}>xzcBO)V%Fcbh4>HwI3Mm+^} zz8@^7&^yyyAwXnVY3Mv5FQ9h%`$WdK@Ke% zE#=@4WsCOp1u83ao#9Ldh$P~%t82TFp`MIWXX8Ctn-*$0j(&0aVtK7H=jgVFF;!&+ z3{zVVCOD;(h*Ju@L%Tp?DLiNr2#HtHfBJ7kH!x`VbPovu^8B&#cwy>8qjQAH{I7kA zg`n7xuk1;a9XXuc^q)DtnMNt1mX#42MjRK?SQALpd|A3b7A9AfM z^_prU=Da4J&?Z~Rsms-JBADYb;(sxMw|_H&X7ZLN69b4<%*;03;Wt5KG}@s~8qOM= z4L@DVK1%sZS4-O|O(sP5J6tF@$Z#wz_?~<9D?XMc`P~FbiT+q={a&f_9DrVB|8v-i zHhj6-n&vhzFs>6y-M03psv`9rqw`X5X2f+yfZdL&*w1OWYL8TPm6psycgQw#SPn0k)7*5$779b^+`A3@< znB$GvzgN%{56<=yFHsnxlE*OYO702raCFQc`u<9*ILkrL%8V?+(64c)N^>DZYXA;H z314uuk4O`giqShP*W#NL>&J{??g-59;h(5cMLsb%zj{%YPXsjO7u6$if{?2JhXq_% zB5Zs-@+B#xaQV{!{S1>#C~~Lq4-&BO9`mLE8%CPB#GKo=s>q#+dUR-uYo^Ek_%)>} zb9Q9|8%OTk{Lj+Ug(#ig1MvJj>(9<&AW4;0MeBH>;86P%H=M%Cbh*9++g-4xZR;#U z)jCJKFGU@k+RC_vdF^9pTV$Sz2Xfl-BR5r2^1+^c##5c;mHY6oVthH+p z$_M0*?P#{R>|-LXP+wiv=jz6w9YHN5L9Nkx?cHtfB{q8q5@1gf`I>TOD8y$sev2G2 zq8F2p)QWW{M{<4|uz}Nbm=~1I?sMcq(w2v@Zr+!$M~T!#B0$HLsy^R{NG_5rX|-X^ ztgcBTwj#{LL%Y1()MyKdh+a=b-OSi^68(QhTQPozHL1(K=zA)7g3Ar%sFfkvj2Km~ z7j>zrMLr6#Wtkv}**90^shqT5H9UQ$mg_<13qi$VuvSUibSrryln?Z!`?2wLf~{}f`0HTr&&)g-Y-VhUO(>SWm5 z)oNP}tT-g2?(XGVzKYmBz;U}HlPLZ%p~SI~+nGZ$mXJRMJXQ-=CD72n9jl9x8wjMI zLzTerU7wh}^^7+Xn0}X7H!%$0>rF+b>#?mV6}{PTJ{MR&&TLRjkY| z&eii(B)0qEhGP)!Vso_*(V~{leeZ9`y-3A#U3|_Z&Ig83wd?XDnT-XWQdoy9uw9-N z<>{06{ zaH<7;FABjSPIlmxn8{i(WC5X9Y4|o1{S1@1tZ!(%5;vhBtql`4e)p#$DGpvc&K7wH zve1x+>hvl$F zx7o{jxp0Cul1*do38e*9;dKuMPC5^oiq5Orcs4WMa8~3gOc>c%SI}x0?e0jSG)dD= zDT|WSWq1C=6B*W;@%B&_zFJ*QmU)^CPOzj%ba>s)u7GdEO^lFIiHh?F z_VJ5%`B8hy10nK_z}dIcng&DIlT!oFWEXm`qc=Um?-98tA{EWXj3gZjmsv0*w-u-J z?;hK?dkT%WMP^#Ac^8FwVDxhxBwqhoT4diuB&TeRB z&$}aOC+&1I^G1T#oM3F%Qk7G06hIpOgbXaT5kePF1!R;)usI@7^=W1AzV7{#jrt1| z_px24{*Wu)i<@%1U<8SSB^=RCJ0M+*Pm$*`StGEAd;~>sLh~?d!UC&V7WXUm=ZnPx zAaaQ>&v{1xOIdGNMUr?y?{S<$Z{ValT1$*w z-4|$3>QwbIi-fbxj)}ng0Q+z#HLyC$MO9e~Uf_1z6Hx;`HbqM%qa7)fKW-u&z5PY+Zy=1PFr{g!^2S6cAw-rRo$`t!iOL_0|r z`ZVmP>oIcS@U1_%0pxI`*z1@UrID1DvBM?bTsq+KNS|3bNIy(Y%Z+3i*L|%$IFc8Y10B~RH82)1s-sEI+t{Y zZW|)(M3rIFbuN0X=49*U#GRi025;RSE5NkvSHL3CIAQYnB0oto zHE^SIBHP0vXU2)Rsqwx$#y^h7kzz2BCFaZi8J^JzTa6go>s%u&ksH$pbb8CpK}G5* z`vXWKy-S2qf^t9Lp9U6qCL$kX(I!7`jgRAL~Ly~BP&rHS>~Ma zBr)1^HUEI6g5GeWDg$E&GwGH(89Mq|#)bdR*+2vL$lHEB#FosBsI-ug^<;BJ>+=~i zsn}wVM^XWrb;=ROi|~TBb^n88%19p(ZTyJ^KDz>=mNqxC%j2KflTI*T05tzNn^fU| zp*=hWFV0BPUbPKRK3n9O(KxAF4Mq)B#u{1B0XI>Ecnmiqjn{8|7B}=l9jaj_R$Ehb z0A@i>jvP9Scz2eYimd}89GKO8Jq0mi_&b^8G*QRNx(T~T;p}$8vqCYcI22t2lKQjx za3QX}-BL8`RhUl4Ahnn8*q~JZ{Iy{2*1*b`&{Flxn=1?*v5g!^+rt~WMA`@Lr~SmM zruHv95>kF!>mGvkb#2S{WWHaULI~jj6-WQ0um6|hPi_JSi|-PD|J{=v1;mLr7h0t% zjm|GQZPF}il6gynXTm<3(8AE~Vi~+xgt&0@Ak zJVfjvDV67TRud$H`<{jA0$MhKz0YL_>EaRdVLsyTC2yxhV=Mh>aFh70nigZG1+*i) zu3s4n+Wc;n!MicZ3X~On4yA@S)={x9rz0wN^M3aGBsQ{*$hskQWd>* zTmc0FzdXm87{;b>QQj}0d;k%0vQe^L@vo(2Sz8rucqCLsgTR9SLt*L)18W2%{fz9> zvfb{x>NFUAVUX7zoJX$JSokxjkMMV8B!<*JNKQQyAc&NaW(6 zzDe+4plP$XaQci&YZ5l37tk0A11?hS{Kz%SA^NOlslKTj5fZX3Rt74eJyiQ2*^xUU zrl@tkvadJGWHo-7ARKOpa7SbYuk>ZHSfbl=Uv!m&;JA7GEt@%BUS(ua#;^(lfsXQ7 z7FaSxd6kytKgl_NrZdf@6t-EvU{S1T%q5DuIfO54xU%LhyVw9nwD0J4Y>rX2zTNql zlPh&7Ym(F4NS$e(efS?a_Z5U55Gxms^FmE>VOS{>YOIT>GJyIkUq~eX!Y^%7bi};uTd0#i$o=P;1AxIZ!(seKDYk(zOwMtJ$?-zT5r%9 zEQ{~W$^T0vDrxe2XB6(jgXQBc+4jS*xyzsx%QNdgX|8UMbznfHVgQB|BIyGXB1LFd z**!uv#waMAuzsxRJR5@yhF{*} zBQT1tlIka0J+(cgH#B(EodlNL&C0JaxuR+ya}!!0*GI)EG+eXRNLOqB$rX<=iuO; zy~a)Ame@GswPZ8f2#h6zTdpjcc|mB*wMIKDa-MVNykj}H!Qb4Es@=-FU1oYsjl?;+ zjo23t&Lv4MJbp2GYije-6WD+9Jb#8qpzCiWZ}8*GRQB-x<5M4^?(SMayKCY+D!a%{ zn2Luz(X0Y8q+^zIBcgR!Tv10shSx5T-M<@dmL`QgK@y@^UQOUjvHGNjOoU-zH@(P9 zGu{-m)9QBqMUT&1Kt+|9I9faTqf#e=0)adHhK8g>kv|a|$+pW;Arf06k33kl0x#lV zBUHGO?`NF>bLM*4KH{9g^0PU5EMt6eysezFu|t?6urtrh?AI}fpZO?&695<0%&u&{f-Q5dIG_szr3vYSq0i#){Eh(p(k zs9bBxYb6U#g1HdH{>cct(b3bhs3$PXt)(5moMT#MPyRYtS3=FSkW+U5tu^hWrk)Jn z=T>Ow#|F>eLjLUzYoOhDur;iZcB4*?o=k<`W&z_sfXq?l#{4Ks>q%~YJ)Ix*W^>kB zdeifQ+}Q`%OGAY6*WNrGrP%Wmb0^&>c&dZ)L+9w*q9y&6ly-q^ULznAul0M}k;8Aj3 zuDb@9ezDY5ph9G6SiF=sT?o6lptrW7w{;*8s%+0iT=pu5sMl1P<0juO^8{`BPOQ%b zc}Op~G;}w2nsRU5{;J3Gp8QCVd)zRw(=(3KX2X6;`#Ww7uJe5wv8t*BQ5u%jrvyJl zhJ6Y*)2GvlNBC48%qIsj-WKB+(iWMu*-ic5BDsh}P$6%XBL zS*zz8sNpR}za>FS@b(^OXH?6bFB*+TOm)&HRCuvtZhdfiDCr6@=~xmjbGK^M{_#yl z|8#Y+qBY;vvHT)u@@Jgc1^3kZHxk&&)#AVDs5gF9J}M&_&K>0EGK3@8T7g;nBFL7} z#`d@$v~EypNrB``0ab=+Inzl9!Nr*p9?T>OJYW;@^{CTI6$W~aToI0x z%VlX-ctltzwNN(<;WD^%Y0Vv9xgwo(aS|n{&+oRC%$DX|+vN1Pf-3h(%*m@yx~S38 z_z~sR?^Fj9EN&btw=_N5L9R<31msB@fGCEPsWgN>L^_xbllU;S0# zl>jb9;~qJH08`HrJO~f%0lZ{WpBw%PP8OIY6mR%C51+)^^>B813D#VuPj^dd%mDWQ zRyXtzsIUN2wul$JX4I#bSuClN&B*HNW(b#Y?MOGug&kda3A)5LK$;IQ&x?+$sTt~^ z(q+F3&s`a_>FS-htIp-=as#FD6!yv5RW~}#kfPI6&TUFm@?!fA5Kh~R{Wy6GwnG)0 zFR8+Qlx4fN?4)6*Q+jn9% z`rDZEqZn~{oKC{)!_C_>T%l>@WU&Rb@mV5Cb+ue?>SNh4+1o#t`zNKWa7Ik1jm7C` z1B{r_Z`crqk~diq&V=Uzbc|N~es;E@xjjdK6QNbwh#IU&Nm}d?q^#IS5r@PNUC*7S zU)Ko3BT7t)8CD$h6^jnt%6*aEM>7W@@Q&UAT?Q z#F5Gxm*oVBl8<>A+Jtw0!V0%t^!lec1JKd(G9BbmZzLCYk1wh0-X)cDW&hUVHH%UI zrIgSG498(Go1@s<+n-f6&(_w~B8Cb5UyQwTdnVo9HrVN)<8+daZQHhuj&0jUC+XO> zZQHzJ+tw8ulY7szpS|Ci`D2dy0(I1?TB}a}azq9Rj&Z(7{Sk?_OZV;e7LPF&%$|d> zLu$dd zSY4#E604}4BSqtrY9eafvN=k5-{`e)tYLJ7(~JZPu_B>i8^cmR-vmbt1YpCZE|fikhMti_%R`aj|)5uq4~c(raLv(mwE`ga&qej#$-pe z=dm_M2Zy`btSjdCxxqMV1o%BLj;1y9CX|+qBNmkC(!Iek&-jE)1NDe{yzE*`Hu3TFZcaUB z$V4a1%Eqi?=L3T_PJo$LvcWN?WD+IeVB%%uKb(#VlHKO}%Z-;E>x-N>_(-U!1)~W! zjAi~FDqVz!{tdq(eU&zYJ1BbeGVN!i?pV-VFWf-0pB5~eyeEteNexb)9(i|(+k7Gq zhc*2unpi!1JeL}aVGNHF880$>cfS>W{MVlb!u0n(1~#*=Vp1^S3(EYToEyZ$zO;~i z0n^DQQprYU z%>O`1O^(0-o%#QLhk7)(8)$yU#mSF#cn_JT*@`#BCB>kknEqS?_geb2s{OdLyRZKx zuHu2s&RE23k&LC_kn7=rnmSSp`%E$DNm(*>j4@wJ7sa~q^J_41_<&aFY)t*K(%8Pf zat+FCt_umG!bXPJSh?#TsWaGof?CGY|7P<3$?)*Uf&bBO>qFd@j+(Zp(jU=+aPp4` zvp0ssxFmKhv6tl=ZG~%s>xTVCV#+ycwt6C(CB;K!2VKx!pCMH&$_aLYH7_jc4q6(+ zff>e2p~3v*=rg%*_&E+U>h(fI(Jv7_?+0r@^tc+kG(##>)_|+l&K-T4GKtYJnm}!` zx)K{shb^$ygu{-W@#OHa=9+uP^~vMmYI*hG<$3|M{P(uRo{ai4Xyq>ra2U!ZV8{-n zE}vc?6RR_kX!2V)8#vA9Z+ClH*@R-i^UM03a3n%@f|SyM;7@5> zLYE-$7{wFl@>7KFmWpo#dO07&$eevk;WbPh?tAmV>CMzq6@ z7)Nd0UGX27xaRz(+{>=l%oAOq>1-F!>MRL|DI; zF1z%sA5i|IIDYAki=HDZs05m!-NGj7L)zcD8PMr&Qu*b4`<>J=b#Vq6`j{OtC4-Y^ zg-o*`5|L%W4QW#Xqn$_G$-Rk$OW?1Vi>E0IOeH= z`%r@f|j-6V!lnK zgyi(w9CfNQ=1(pXP+d>XnmqO}GbaKIO$`OjOK#nn8?u^6EC;cCh4pS@yoTpn!J+&? zs+x4orvJj3_8{d{{Le&O=U(wn?qy!O01IJT@1M3iKeM(Po2z;LEj)^Ef-C$IgIPx1 z1bh*z!`>PYVw3-lA~yX$qlkC^9Yy54sd4_AXzNm^B?@t|H2oEPa#-|I-t;`$7G)&S z-i!ec7$Cx-A^)oMX`&c94Q|SzIk8HjrPH=Hef_xC8%8d_=3B4jJNC)P;qwkUvibp& z1b=A;$t#9Ijy@f7XMNrJAKUQJ`1e1gnU59T2=RfqaJUlAVU_@_>qPEgZivC);`<7Y z2tj%@v@MFmX=yQz@)~y>nnZ^RDLxfEfau>y&iNHq$9b?3l$4kDo~eo`#BD{T%}SobimJ>V7)Oqa z;=<>AJD&GBw%|@@vy}7{;xe#XZgP`g+8@g5Gn$N=vaZ-t5ZxbWjr+&7;Gu!!kvrmDj}0 zfm)XnlMm68P5I!TRv7suW{AXv@|dZ5zxj|^u@?fa^XmViUNhuimPe^7dep*QD zFlE$E`*XfR2MN>2rG{@-AdPyl$=pGTUt>g=GrtD(7};C0A2=tw0vKOv7%IqG#EJ|K z09Y3S=~}KA7Q&XcY;B%Ep@xF*F8>iaNl3}q)1BscmP^L_8kd#ZAs%TLg|hrL|No^N z@m!L7>r3TuYBS-|lk#uyEN1+_EAxFcrwq}+nI<*v_BdKr@+@dR=C!xOXm5mX3@ye| zU7ZZN?vx2ts;M@@11?~NbJ)I}=ae6vFK#yUI-%%*V|yK-yrF`52B+OKoJ$$j6Pe!x zuQgs*oR3;>bl)vYoWpK5UueIPa6Y4()YkFvnK!Xk+uHWAQC{{N@~zlpN4|&|vKw%G z4F2!wx!tGk_cYKNSjAhL+~NDZL)rr=Rpgsf51GN-E^2%HD02dJ4(ta?C0HUdD}y}9#8;{^L5dtP`P7}@RgEJ;`UFC@mDn~guLNhZrjiaU zAz&dcsEUg?=Wkg7S<%RBB@WpeNdCk`Hm2V-Lrk}m?KUoCjlu!QQpfi*9K>J2$>uz< z_KdX{vd{aF4(_Edlj?r1eQ`RP*$5WPA6+}hOajaIOEWg9!`)+sVn3}jOpIf!OPBx3 zP5R|~y5tUN8L*6GZGvYmWW4$-s4N`pkzM^thV)M~m0~36+FMJJLNME@yMNMn&dza@ zM009*FqUu`9d$4kgQHKfbZ_f$@&>e9Vc(cFcLaP!-CM%=TOJzBQ$?y6iKl~)1&?zD z_M6JCWeIpEgJ;JZdH#7HgF|-rDgG}5658T-FtTgZKx==Yl-ZmbbZN6CG<1}03XyLm zj2b-FjwqwPX*wyr45s?Yi=}2I8k=K#0ZhT)z$-H}Td2JS;C?}+NK2RD0||}69aMBq z+U|0woJR)7YQpTbza_y1f^ZXTQ&2)7pb|y0%{DM<5 z0+ddDj^`6I$>gv9bB}KjyLs!=x;WDKY{Z^&yvo|k6kNkO*7uKQ|wmNKGr zGZg4`{M&N6;Uxa5YjdgAQgu9wZN7QOpH3^P_+KtqoBtb5@!T$bsMW_;DE`F86#2J#EKV1P z9(!qoPyXBpyQbXYUR_i#;}n`yShs-!bD6d6>lYyX}VwLe@vNZsFwrA2fZ5;45KF zvitk@!w+1Mv+Fr`K8e(&#uHBJ$Lx^ul%L)-)8IH{%D@ScMhd% zg^q7G7%na>0a0D^xu$HWUuQ^qm59Qm)j%Bc5!&y)6B(2gN*Af~{V9ivU4a!8#~l;q?S3lA0;JTpR#xk;!=H5QaU}!w-HUxOKI&cJibRpL-s+GXf!kP^fAYpA zo+jCB`tcUsdGvwr8~=upR@DDrc$?h76(`6@y?Ub_E?T7r+WwWJ)fOm(3E2{G~KZrh4-+luc#vyb~OqcJ6jl5F7>`{zn)Wm^=!-Mf!BYjp{*~dU?Y_O(Q>l z3q_lx!0{vek9a^XKZCvvLD4-L5wYsE@i zIkMfiV3UbBJS7M~t;(hUGz`&afbYZ28=wj?=P8k5NM4b$&xd-_{w;lyCTJf~W4$?( z#Bjy%jJ>eh%>RRqG~{H&4N@G5kBqIIgqYdzBYu57Lj;ZoOQfy~(p=tSAi-mS!Dr`Z zV_cs^Y=F?0rQit)a)Eslh51({YnUpIRGVxItOVGUKz@RQd9c=c;~t#Xu4?tl1Ixd2 zoN3w|fhuJG1;Y79t9Z20xvCS)ivUbz` zUSr*+EV2b#<}WMj<%mNWb2aWz4Y#n6#Rl!O!$$knZuF|l@~2`8cL_>>pD4%t-4|{t z9^(p?0+pk>*{>&FU=>!NKT_HDT?stW25{0_QX${}VJY>2)rWY*4Yp)=pV zT)xxRXKp)bxLc90fJ?EpSi8d>yWibX7uZ$mr8iS8zNNo9!%xuK5KOhUNFGw%(6+{y z%3dP2LYQW=h|jmWi4C8QMNC0HpSiPbLo>c6cVo_?B*l(`#n6@^>J3}$G6xBq8*g%u z5e*^Va$pQwd{xeTTJ6BxiZMJ}?-WoW@l)z6)I)=Nt;UN9m)cvpHe@TgtNw*rvNVlR14T#EWGQ)(}x<=eKYCPUB{eiGZu%>WdbaQ7_csRUYD zF{$AbUlR}bCVg!?RFAKKX9L`IcD&LVR@FuzA99s2Wjb6jQ1B2~rG32Fy?gukk!kHa zdFRc(_EjG1dvaI!3j5G8x|O)PpoTudzmkfrivjFuWAv_qId|M>i!NW0CBk062Afig zUVTO9+1UDF1FF)VhkbDHuB!gb>6E3DNTKOmke}F{UPS{w!^mA%$y9jWIwJna3tRl@ z)CChfXHRf5b$Dxjv3q3he5KRUE!R#b>`2$jx&PIxXz!NNJ{ptGoGqXH)ma!Aus>+1 zrAPQTA2!GhL-|IbsR?hhF#eZVNUo*`PK6*tBW7Jv$P6lVJ?f^qG<2Ze+9thCSRk_! zu5z66^eVoDYwG8WWJ_>~LRF@QmNjN-&%u&EtWxlC38Su09vI%oRY-i*=m;Pz&v>g{ zETUEEFsIH4gO``~WEuHv)ZJ_6!yh4O5fqSiU3zWr<=M9n!dkk*fi4Kq%!K9ZA;sQ( zMaUIo#Q#b77x*Tt_<8qn=1tc_WNiy?yZ^sThJSV}!*jNs7&}tiq!du@1oY`m^;x8( z3@FH~(bqX@NpCv@y3FQDWd=JgSindZ@BSHJ=HNS=csQP%y+V+~iT>UDWiKRS5z^gua|ujqkQ*Wc%ghz2C)-{G{u^8Wi; zT25tVkRfpndMB4VPpt$cj5H-kXvpJ|$zzY8rItDFs1%F3``!nJR=tS!hG-U=c$i=3V-C}5^NL4jY}LiMZNg~XgNlW$oM%iQ*3P0oR2){OeZ z19YO?Nl=;~O~(f`(GjAmLv~^EOZE?34k$W=Q&Z@Q@z(suSl;gP;G`F{V*M|A3CpeV ztLdr+Xx~spz~f5`rEXR3U_3UCT2Yt9*7rYyMv0Uo!iA{#eLxe1#zEfHy7uyDdCq# z)^iX$3%nQ}`0VPcp$E`PwR#hxv3nPNu8;0st*z<-1KYgIYE*iafJ)C#DBZVlGQ{`W z)WD_?Ol3Tenu}NzZ(uC1;Xhqjklgg8yvSm+%hD4VH?HHWsi!3V)@M(i16v_CC{)Ld zvp@{%&MFdiDXs}qiTfF>@-1C&Q{m@l*iM~%(+{>^fM8@%ePENu2k!M_LU+5I)5t-Z ztm(^E_x!t*?NrSp=w^t1m#5QT{)Ft#*XDDfT|JoBAzkQMAwD$KRwA(wV(oj;W zY}0yIcRN!*MM*0$KV@tC9C$xXx-J@A2u-~8tA?kRvZ@~|B_1-(ctf_6f3*~Yq3BZf z^8hF-m4|Kb)Fn}7eXHg^F4(8Ggs^J?|Mmia3pKLeA8~E~h1xoDEl%X5?w_SU-&guU z5mfekTlN;G-$l86UZClj)X39Lxmvpy`6|#qZ_+~Kj=0vegxf=Ik#WSJ?qCm}-bd*a zO!03?8OKajWuwH0_26*_P2tH8h?(gK{gtys84GCa6x6-RumBv#@m)FJPvPg3^LN`4 z=)S@7L<=BqOf-t^Rt%jmY%eeY%zj*5c?mi?mJy7@Jl-m_f)IF84;?ZTcE*3^zhU@1 z)bk|0$4czEiI1XcF&N@n*uk)}u^D>*QtQ%Vy?nk-XJ7p#M7226(!Z_Il()l)lPfVirfPW*RD zyXSMQEUPUIv%}~2FOuhr5{k+JS)WI2t4cxMDZ{Ai=+81P=;;BM!IFBa;S=oXEdsoU zogHxQ!eFwwH#V-Up!Au2olq!4l8lhh`08`rG28^Xl}X`kp6$=mat(x>@={!>@SAHk zxvTK3bgw%5#Yd4_n}Ix)T=JQYqIjXtkZUhPs+&B^TIW|4WwgN>hhA^9IlNZp1CNl} zV#~|z4-nPuKqW03)JL{)%#&RYnMd5tk3Go*TGykwc{;kmjy!PkwjB&mZ#$*@=D==n z5pYL)SN%^1^K<*$!wC}R}ofJWGNF*nQHkWClo)T zz&}r*HJPq(cWhyNqn}>C@)7a>%0~$5Ow78sIN!5Q7RkP=*S#D^R_+|xVF-BA`kFdB z6y16RWmOw4Z*=^{`~8vCbhHa?Bb_a4+P2jlp}a+Kx<&uszt6?V?ysi*L2dzWn-7Ti^@wSHX{Tc~nH5^5FE{0uQu~xqM{Z0QTp)UNuuL94S>w zy}z%7C#PqcYC5n0k`13{C!XLwt6(29sh{~e`27s%X_5bAA~8Ny00i;MIXJ#bFJW1z zW#qAYwBz9BSF$3hpyxv;h5$pl>7rN!k!^w*G$$VY$AM>ex&>-~icdHQH= zXZHv5uDhecry1;-2AjIIk*_J4BuY`^l@srX7Jl=Ww2 z9`y`oBokaJzsx}-%bM*!?!%vcq^WY`w^ec=>I>~!n?a*iTt61k0yM9Gw+YP`@1QnzdHw&q9jwiuZK8sg0t~-=7 z!z1p6#A~Yf`)07k+&w{UHMs86)prAgtEYHhxH`j^9)w6sTSQh?UySOCL-~g>!hy^4 z!8Zw;kaB`ELyq@uXc>S)swW%tN6EK^XkU=I?+DUmQv>65iwUfjmXg<8O}xQna+8B? zg`a-1T8mqFH+{P6uhA{m_TZ99i}@dy6J~0TScE*yJI=UrEAuymQOa);V^fNr@>^w< zKkRgslU^*x^Q0ccd=r#^_|{&^M?RO^=`DHt^HhtMGHn32s1Q&z*M4}1sD0kdttx^4 z=tO8`Qph@NC+jZwE|MneP=OXZqu}L86x~G~*=~+#`uvd@H8J_bG%^#gK(VA5XT03rb^QIuqg^l)o0XL*ra&PeGvrk!SzdInY1{prdoIK?{`U2BwVB61S7KWVWp8^O!;z!Vwn1Z?0 z$s;ac+seUQxhZzrKTJCC|6tOIUCsKG4+=Otd6e$yYOFsPnU}ny@YlmfpO^Gs+{AcK zoDO~T67G+`dJJPG44>Kq&yK*$*e&30A7^Qo#7af^Jf5Fuv){j6f@DKU7S>_ZQ^VeKCCLDHY(B&) zvThdz)y!;dhbzK--#%A7@t(@Um~7vmB*4ytX%#C`G>yUerfDQ}mwm2G7g$Sesm?6p znyjAHNW^Js({Z$|>8FClop(sfyd-){FiBW|0DQdMUBo-h`a{S_0 z?IoDm#(}TihZ*VV`&o`cU~m1>4wtD+OjEbx+)SpIkG!`CKy6h#sgfy)eL4c`QD4~9$$2(W@QCm6bbW!z$_gBy~ z4is%)WC+`8SP4S165|dK-)z3pOw|0CztHs4Txswfa=m}BUM$fFSUP6$iN5-N z3JW4tNEp<&RQXuyZ#h6Rl?AH+HAITtr}2pCn3CbCo_rOt0SYnU8)@^HJ4ei+Xkik8 zQ=v$Nu*^xj<_>hv=^-a4gzst_%C14TJ5LUiMkmuPyCzbQW)omVGbD-}{} z8nFhafuIn>f?ze?y^>0hxJThFf5c$e5@W0HYQH^8!W zQkdd>eCNnn|yjWdQ8sF0oHAT*Uxce-sr)|C1Yzz%z(`Kid*EAGCTF3Gj$ zPD!^#vWxcwo8#^D43pR9&Hbjf3N!_2I%mrDcfg@!Y$1q~G;_p@a8m^@%ziMy)5b^V`OM~}Jt?6S`+_*6)S+MJ46s3de(fF^a}PIG9)(wTb$6_Y!nGVv zFsY8Z?Ce%B@wH)mBDF!DX<=P%wn^6E6In%GL|n#MrQTA>f%H8jBUa2p2*20-cFSeh zos~_F8%{rzJhqeRXpLj)r9lhvqEVxlYSq#quR|BYfql6it@#b5faz=Kp3$ZQhPEOJ zpSoMV7MnZ#CQ1_&enL!>C-~71@Z*ZZo6;P)yD06@M}xSEUaqd-)Uz|cv%6Q{1?E;0 z<=P1ME{(0)^$w~1aFdZ?%fqo^c-z=?Q|dVJYZVSRTpRc0Y1O`mmeVA z(Imq75F8xwQk@;v`UqbNVrhB&UdlNIh7)iy)o;O>2Of*DDvv2ma9QKYdsZhPyw_`G zq>~|w17J-673(|M6y{?ewV= zE>qoYm+lU_(rywS@_NBD8oK9ja1qa5SGv`(E*=jF>GCW5WuLp=BYR5LRp$nvRTSXv z)4v2wGHhX)R|gdQMq|MB$NikI{+iPQ`Pe+EkJR2GLP9}69QNiUH1OusFRP?zSgpAP zWaiWS6uXVTpGwojwHRpZ|HXIpL+NcBZ#=rZDa}CZv-J|=cJDQdm9OoH!tr)h#zf(O z^tQhpYrJm(OGaUE@34r=*Ya>n-SWe5f#{HL443uu0}wL4{#nNQsp8T7ykz{C#?h^2 z+0}h#bL)HbjxIX-L93Wl{cN1&2uO=Kv0!AX?43=xFk_NoA%S?8rH(W@Oi$ypW_;FB z^RW+2O&xvmQqr(;M_K4HYO10+%_RWlQ99DgzV*tGVDi=`WiGm^eA*<(kw@DeQfA%Wb7Yi7;2+EKQ{PWZsA^>XELp2z zP`;eRU(k1L5?PwAGBcIKmf;_A>XcoW3r)KU464x4s%4!l>-!0LjE;}f%(Sr(SgT_E zIF7J~+Nw>CW8O^Zu!_$HG$omvBrTq{>j|zRwLZirYkvXq6Z%-_YPu0!OU~UF5VZ{% zO{6S+pe~>LxsX~Hy*EUo_Vk}e9?$WpumBzB0wCttJSr(&(bVQWpo>f&$DPwM{7+-= zz`M9QvBZX;o@POrswP!c=ouMec8#_xb1LCQ_&cLf+H9mJ^XG?{U|S%T=cs`ur2rl- z5NS53DU`N8ALz<&A8DO^9r5~NcpsCa?P-pDFST>82ZfvjqR{Itz^<*=4xzsM`V-ZZ zg!C!mWUyM(Q|@CUr34N&CApy;;W8e1e_IH-!5DyA@BQi$sDOUE8%~#|s^dP0&2HDz z(A1RHyydgzT6RYi*&DJtXY+U)+g;W`mZBqpt?nf~02N5>?!#BJeI+q6jC4zySD z_!M<~4U|h;Te4+6K;Sb};i;QgS6?!_e7V4zI3;sQZ9n@oT4d(7U9_=aP5c^czWzEd zb@*ZGl3rHnIq|;skn*Fc+W>d@ask;C6Zv)Wmx~MhDBK z(;PRd!yI?zK1qT=ZJwtG3P)d3*)mV{kuw=l8yk^)xH8&h57{R`VGhodNIEeiexxvG%p zq;U*~3t~0jsQU0cuM8R%haS%GvaGq>c(7B+jP^;D70+!yD~fEQB^sNUvLe=7+*=?-Cf}fo)Z`KO6mmS=Un=_WRpgOu1kEokQHOw&?9LO#vMag6t zV~sOCzb1?8r118i5~z&uBW!iTNANsey7ujZ*M)_PD+jk6?_EST<$Kp%(~ zojuP8PX5$-E{`ygfTR)^?0JT|5`?KFFgO_XeUK60kUwqwTsF;(W}nB^3httWv0ef_ zPkyQu(?MmF?IA(hx-LgU|$nm{5WVo!FGddm1 z-!1gt%kuTlGLX>H(8wq%)e3?URdswr2k%j+##?tu_BsrJaXz?-nV-AMN()vGZkvMz zE|VbMHPFIK1Q|1cPnEi{^@gZfL%vCuFN*oK{wgS5K=g;q2S;*)uS?CEjwTumyZ!~s z2nQ441Qt$Q6P;6>oD&PWygZL6anAwG2a*jQ1R0QRIK5G%36A|+Z-gFJ6j}T`aa|2| z2CD+UH6w+v}lHd15$$y`;90*gVy#P^w3v*eo!M8@>|p{%{{al);9=yGSUUGW&E15T`-B<`#KYsp@X9EjY{Y6d5e(p{> z(x8Vp2d>006?lzl;$IjbaW$Pd1<9r*B$(CG#z7Y2bL^k|U0ja^z4I34!_dqKUiac&ObX%fp5{C>9_O(KpnFVUe zwp0p_;HcY{KbC{td5cdIupXf~$HY<@{4K)CVt|T>4B?~zI=BTS@NOrdFb?uD z|9mz$lVYzkQj~A(;9Ru{S>Y17w_f*4LfK}am!8Dp^SRn5^Sep-S`$B7Iqjr>)}-Wr z)?}mYGbbUVWi)MZR_}yph>(|H&Y-({M z`s{>F$$lm`J1eXy?1Z9l5%(#~W8Msi|MN1y#Q)XMGPY?r#D#ur29DjgVy%JJMfUW>RWFW4tAHS#?0rscLk%)yob0wLal83p~*xu6$Wn_Bus84(53d0o+ z^#;RV@q`BN`0fNtgh@}4uqCbxl z#j2A*N*^{-P_a1;Tj1OEIAmptB9S$0PPR-{RZ7a!Kps_iDSQ;lZTY?jNxi`$7JJ}N^~1ZTp|Q33N;uKu z3(R@;lx%61@l?komF>CQ6>rEuGD7?4P-66bMoUC!9k#OXM7e2h>n=%37pI-lvG7|I zNrfr~zZ;|}23rYj*&8=vl8WNW?ePho| z2}8zLImB^IHXF1c#MlaY0uy)5j&U7mo}EV5P)wquT@yV?wdKrM_Zl15`(7TJ^tTrh z;8sdRN>n%dZI?RYjZl_tPBa7aEk*P?dC*o`^6yEYEMSGPF<7YZhWO=#8UYJ*G>p0z zgk4d92-uNAhX=+;CS1ciEdye7V1Z@Ae+y_g$@Ys$*p~7*$cx6TV%|0$9=2}zTxmUH zWAr#z%lN^mGKr_1dpK33E3;r4ywSzTOHf-eh zU7}Nst8WS=UZZpezt+*KD?)rKZXq%xt{%RmTOn0{jiGQoIFhZU&jV#dqG4{MA^<+qz6-Kw&FS0aRwy{@GP{99HzDa-dvCp^8xf!=Q z2rz!W@vwU8Mpo^t(D)$BK)l1%Y7hw{^8yDRLQ<5H$78FLFwtUBVLgyt-fjgFOQjXy z_B|n#&_Np7A?-!AdV9EAx^I)J`+-t~QCb))S;Ivc@VpWqzT^{*g@jWhASMFYA*}3^ zWUrN&O|w%2TEeB2Yf7VZnD6Y%g*Xl9IRj)m_g03jFt@#z>5IS@nbwhh;Lqf#fCWJM z*WfEMPQ;^%Q(EQ-vzWfDo8<|AXT%oNBs+dXVb!|IJCwT0*^}jVBCciL-`s*rcMG(l z8APiwlA4I3sH?NfJx|vlAX!;XYAjKKu7e*Dd<&QuO{G0>zE`|dH*3C+(TbCrkJq@0?5(qeXp4s?UaHj_8cGusSbpqo0 z7S_1iO3urIxz&;t>l}#b$fO)%nmhVV_i+l7>yyMT1zkxM@2ho1gQ5~zt$)}Xz~v&0 zZ8^z@b>vsR1%Msg2KbKmw?g*NG`f!V^_|2evuUeXEgRp4-tq)GFVfyqL0e-q^zM() zENoVH|7;UW8W&W*ZNOzc4=99P{M!L$4AkcRc0;ihXK-E;<#!% z0JZmQA^x7pae{RtL-PiUktGx^=!G+Ls&KJ@Q{kuXURThjaF3+Z+%;rhU{K8|_*7GW zkWNBU=d9aFt*Ju5K7?*l-Z6`}^hmF;MWBA)X?}G87~*LjZPb0O+t=cp$?`x-uFK{= zPj7O+?^DC6jVv@mk*qiMhq6E(UG=hb%=~4MZt{9DBc-MC;+9|M4t|OQ6x!(vs?l%< z_J?uSiXfOvZO=nnS`pkvxg-TXaPW|cm{e|(5RrZ0^$Oq+=7)@BO&qvIPglicd#0H? z!0lmte1Ip@X=VH5dM*fg<{$Ee09$mLI_oQGO4q0&%dg>cFE;A*>EI_zg;u7;4rmgz zHNSqMp7S}{P6P~ccL3djHeE?45Z*)DRPe0wt$buvn=6D?Vj&L zcs--(CI?xG6o9x}Vns!=N~FwRBytt+tT(?k-uTki@a)$_Fc~vPT_Y##uHQ}xuhezvi0&th4OLP}|bKXwUb^Z<@a|rhGh?ub8oXnBsn|OIn#qGQ0 zH+y#;ZQpbiuvrvgWNI&t(!c^3--*l_{hlDFprjabA7ZJ~TQy1ri4jCoZpJLd3mvm7 z(&F;%lIHkfdeu~sY|J8_;vV!g%>6k1aNXEVc@GC${%7!zIzy9J%fV*lr)40k^LKal zS)dpbs#HJvxA`KR8=6zP8mtT{SRJw>?q_q%E9E4vp|3=^@mvWYvWBGTH5yB+R>jQGw z5Et>ebZn7(U$@|Wg(hwt{)k8s(j8~QFRgrA%~^sX5Ce74-n@=pI*^^eBciJjdv0vL zzsnbhXzBGby|uV|H>KDf*R?agxv1-~h^Ed8Muf;<;ni;_7;hVX-9aRV+99}r^|Ts& zGnj{tVx1Tpjh$%?zqUyd|Jbjw2K22tt92*Pf|i5`i(ts>PG{4fcYZPIn_dW8zXdMh zTVXEw+6zh-HJVawo&&(^^mGzC!hnfJ-i&ixOZoo4w$A*id6ChPo@RzaL+<8kiUp*B zJFNGF@)A7Xiu-4?VxzA+Gj=(4c|0nDqKy&;yn7HABZFv|&A&$nKp~R)7u$?SazV9* znLsk4;PlbhLPErXSfvWdo39vn!}XbS6hPYK_p_tshZAg%(i7?=!oJ|QEEi@%PSSm_ zNXw(cm!>9{n~tN(U-g0R^MJ6y`)^+Re+&bE zKY$ZBu?Kp(l1#EsCc_Lz#k-o*Bp|sPsLQPg0hVg`hoaQ@37rS|It06dpG5??rxL0| z7PA>gMR90^a4LOtR9KcdSKpN@MH59Vj24CGA0E3YFNkR4#@exrM7+OP3a#rO0(Lke zkBg(t!?K`SKh@hWU%#KDUtS$puW42(&->*NiO8wB^?;U)ja{wM#J3s5>Yui zi@AGXK3{+EC0ibjC%?J7v6-q!+}BwWSKeZB_)WA}o@OO3K4&9XNg>MVTn~9WIKs2s zd#8fMpYLV=jrT`8)cQ|3w;dKZG zjT+({o9>~Gz7zG)yMj+RA1%1`!pIc-uOW(c4l@pS8wlc~jko<zW*w^JV1xE;R)T)c{i~g3=DGa4LVva!G+WLHJ>#H@&n)^PnJyp0b997o*fV4Z2m?hCp&k+D*%^AD`(N{KdeTDk#sa9kDC6qT z0m574=3GVW&A*GTERU^uC1QbXx<<|>*Y+-mM8sGe{vtinj>Y( zIiKr1$r5nde>+gOypF%-Ol1P?rl%4Kv$(zHT5}aRxIHDV=eJPx2IX&&nw;uT8_G7c zq>nJQdw1u4=TwBdtICJ%=C28OaOBm->omlW(!+87PX4j?th7+3wWds%+P?`e z%)Pown9lks!Ta!-JoamA|1X3Pf3|j=cvZJSoKx0s%5bu&_(Qf;5Y*=jbbQ-o_Eb#` z{Oola2ct(>b=i3A&AHKB*nFNv>f9IxxgB<@vjJ%jqP)(q=UJ)kj z{5}7UUN(Thf=9hAt(40Ac~o=ibYJ`DB}4cFX*TK2)%bY0BN$v;yS_oR3)T3;d??B> zR>Z;d9S)kZf39j^1E4g{GWmbVIANm(`cZ|K4>+o}75#iMF7Vw3P49%BYuHtEH za4Z_BNG~2a3i-b%JLm6Aw|&dUb_Er^Z=8y4J5@0%72CFL+qUggQn78@w%OU|cK7Xl z_W9xVSbxAXp6^)x%sJP2d>H2JP}vzsnC6Y-|Ag}n(XRauIj>=2G4fS?La_$2ht8k= zz(2^aJJUk9+e0^29xqsk$ftZ)Jll6Ao)0HCTRinLkury`y*8O)E+9j*nZ-^B%V4s$ zsRR+Om}yTGAybFsr_0&Ma+C^*Ve1Aaq6qvDVRsq>H$TlD-3gzSw~xM)7m z6!J9wCd!T#b_l3I5i(=T-MTUVHwf}_cQOJJ7!rkEJAQoi!_y)$KWHy4vFebpxuADo zHN9kuHX5dvzAl=*@J?P3j=`1G$ZW^^ec$!?W8Ln5QBm0W&_mA>O%;k4F0G_Baa0wj z)^cZ~SoP#L#1>yW(l(vMzu`_;&IB($u4qf!g&o}@ja8ZxT=KyYCvc3ewVy3gn9@T& zRicKUJQ?>!?0bP{h~4#1+lAj}nkwEz3FgnDt8^vB%t{*+dncco(A5U#fNbaK=GcAS zOM@pR655Ub40A70GVnd`913G?b|&JY9Pm3yZlFBc%g|ig<@HUXD`QL-A4}u|&Bywq z`6$0zHn$BlCN{fMch4wgrC9ygjjD+C>RH%JXT~T2>TvrVTdnPl)o{9&Gbp6~;r#vZ zE}HVzGPJ4oH?l29&eTBtGVh`+AH``-$!M{^SObT^GzW8 zPW_%dd~)2FI^lE)JzNLNoa2x6>IkJx!BiNsV&zfJ!-rA*kL5{ zZ9kDTmvm1+`O@zzUdl%~P8d~g6Q-tX9Lrv3OuEjPPTz13hCat4UEtxDJt=i$Hv_+g z%D=e3sliU3%%IVu!D6+vr`M)w3rU)S`hP>+NttX!+CQacvUd-CC>beB7lHKxx5T)V zdnNlBjmX7+ehmk`tI&>*#f1>~Gn ziOD3`kYZfZ)L&r0F9z+79NBb?piK?sSja#AS=m!au12f3)aBz)(Gl*eK|Bh=-+WZl5#vhdE3SU zHU{IvI0x64H|-bl*Iss701V}jA9H>+5+R^akD!ZCJV9xRC}w!BfogL|xQVxMgxZB# z*YjPxH=Y}ZAq===V(IZ1)tt!^#>Si)n<`q@Y#8)pG6o!-06hD(?DNfSZ1S52k+$JDIu!@3b7Pqt@dT2fK|nC_k&b?{Qi28VdX&0UzRGp zj7P!Aue>&rX;hb73l)+5)dt8!`(WsYzRn z6Y0wCL2ZRWYDz)FGi`Eoqj2Tht^rnZgd3-1ez)7e^+IZ7Wnt!ha)c8#?C18bDvP_r zUFR906ngT}L*g$G9`X+BJs6L<(uOs6&cH@UqU0PDVe3&u{{^CVRT33S5((1 zu8b?W8Wk@RJ40!46~f~at_ifh8wPKSRg|NbCOhCa>O3M9iRmPm!fDYUJ=tmh zs+hDScPLj-Srj1R2iC<}b&K1CSPDR_0|gZ5x@XYh>MKhbINVebbY(%58|tuAVa zQ132hVs#dMi-3^Vuks{V;M6$04i2p^@r2i6Y2&BJ(g1tVq|7#W9dLa*G?smge50zi z!bM`?40Y^55P+wT?OIDQH7>o?`{9Y?!ZEu{Yl2N|pifPwb1lm^_+m}{80=`j)X25@ zyJ7nCDzL->xlg;yA-R=HWSrAZKg$GfXnDG3sICQhKjrmJf`m>o zOXqH~itGjQa_yuOFJaw+T2hk_6BZ0Dfl#1!2Ls(g>_vY?tzsQ1@f>KQLUu7wX%qpk=?2Nj9gPGgt37d9BQ=iI5R*bl4YPz%W0jA4D>*W@z41!{V4B&1Eh$H3biYd!gxRnMcM7z1hpQfJ>K zH}TLX;f+R}?zNe!2eu=h(lwrCyC3zj+1HF5<&7u6k8`jXYUt~aB^HWjCZ^pb@@}s_ zK%fup2w#$D9Yn@K1$rn^rlwX)g&$0d4L)7q%IX88*B&OSz?}GyaKxu8`MBTY|I(Z| z;n^Q5r_&=YPHBz+$hf}WkpWeE06iRRG2SSS93;|R+(P3|jDCFx0RM#t!;8ZeT#}-zkl`Y`$Wa*W%mgE?7hHAw*Ko9*MzXu7<&c z`op89ZAo&0A_?R(phI9!Xu?FQAYI1ybtTZT~o0^rlyk7drJCTgbmJG|51dA$UXDpxCDX?cEqABFIyakQW8n0@< zou_Tk-;1952Tt*4<)JWQ3S%=Mqw$u>gy0sO$C$sRNJMl+@ODhX%zwi@<<0mvy1UH+ zLfsdm%n51LDa~6d?}uN5`Eh&x6f-ZsSf5Xd7=z1@s(RWvb4|#y6CId0c>Bd4tjtCL z{_OLWgb3GqAfTBbz&jfDCjvKmq0mh87ff3*ywiF=ju1=|`FLN+-Dy6%XhmiRTZzm( zfY3-B14yj)kFMRZFJKvVIM#n_hgG2XA$PtG&wwt4(LQQeM zU1Z)JN(7iFQH7zz6xFYvH}zP!{?H;_CBK1rq;^@-;D2v_SoobqNHD=2K@@Jkm@z~y z4hLzw6BzHxG9TcI>KyN6*KRY~5)ufrPo-`1tR5}om`+|rMP|p~+S@(B8h|6~N=dqd zfpPd#fWA5sEURM1NUB(Gg&71;d&l+Frs8k&J4gyL|G*`&%Q+Cd!QT22MR=;bMIG{D z|F=R|<6JU#PH9w1-%HtH2Hh1yFUr)Q_vo2?t$+=gxE1Ik^=NO1&X&`2A1&0D^1Jne z5}HO6e#WxNO2xw#;*pJ!O7i_c`M=$r{e z^*sj0>+R9!Oaq5}-tK>mI|@hmY)ex)|li3dAp zl?b|UZ|>kmR#HhY|H z3_K`d#yLV|^rR}O$&x-!W%T|wW~9OkRXIik(I7_==--;pI*zP&Q8oey9R0|Efusn^ zfQk3<=CAMoBMocGO^gHA`B;>NE%_b%YFv8kIJA?PtZ~0tBQySb=jdDv(CJVZl!c%c zU3p&-Mjg*+e$>Jwwm$zs<=1?PV0ld*9rQ!Qz>{W{=2-HWnxM#?;hnaBuYM*J%$WOR zf3|Z+uXMO)+foY4!K&(<=uyQ(oF_c7yzH6}GVK6oI6}!S%Y5UA zd>^9G7qUL$3g??aFu^Lao)F<2i-#jT^MOsVoH{vwx@e$|%Nuk7ExBR`mBAxVYZvTQQ=tI%!@AE>DIE!#5 zm?YUVKBjzVq7nrBeuPHg^y2g;OOt3rr^{1d9>p zK*KtyZlORGKgn@05MC8RI`PM*`T({-Lg8>Ik`#!ZOd#n8I5jH^rN>SDf5w2_EgfiI z1kcS)AyM7Whq>AQZ5D!=1@VsSC=M_|C)pNaAjtHPEfE?7eCczpKkPbc2+pb-k9{SNg{#iU16j}VJ7VUA4KByHL;k%SqRM$AK8T(F~v#+3+O>Gcp ziZCdN4-rsF_tf9XVo??&o54H*OLfKT%3J_lb#$0U(3gg)tv+8ZWiWs~>KrWZF z*$8=8`sCf|udKg8wU>o!T$q(=T+g)(}L_^@*qF5uQGvoY?o+(k$H?-C(FvOrlhI~my z*p5^}7LMdt1|Q&23M`sSiY|L!?02?(9@9vAh1dy9ch@Uzv=y3k^T9M*=O&qBJK(>M zM;jbj*}0ojy>AUD+djn25Q_CU(<+h!6u$RMT8p_yJgmKr;bhUboyqC8LPp2^#kTcJ=sA%pEIpbMY=u8Qxs`PCmm7_ zC|loT^qUp zut>~I{JL%9y~>tmhep##%v)zlc6b^f>TqLzE%4*At7PI4oO>?E(!Zcbh+mw3aLa_& zbjxt{D7x_FieS8RBFBKQ>h~Q|SpGCDC3X+9>r6Le_`C?_iYoR{8tgy%d+7oNrMU4$Xt3tvl#doS~?@k5}egV)$qC5xKFJb6u{$&~Vl`Qo9w9UfCRx;MKrqI%6Su_zO8K44xy2Wq987BPN=Z|Jst z@iNX0_xDgF6OVeq=pPZml<>hAzQMc{!HIk<-(z!9@mfBJra@q? z=%Pzl62)K`MSZ9rr=JI1B7b*8iRHqs1^sQA_MYJvMC|QM$13|4);!GAI|WyqDAH%J zI)M`$3Bu4HF#~^wQoqfr&1uF4fovI8>`p&@3Mx}r;1HX1Ee^fId?`+<0CDUxi6F#W z5h52zNb4z?hOPVcoh3`Da16bNJKTgYZ(5;z|BKD$9!8|VKUM~kc@S;I^tx&WOsGkN zD_m~^(BuR6ybP;!JsiBjqJBPIE>8TrhC_tns76uFTo!mr2RxjTKxfhiav-4k3i(T?Xt(7DkuwncTZa(tk;&&`a>nhY@Fyy9g& zv+A9K=1{Wze1UOM%Q!Yzr?x`xy|&>JxZOL2-yta9DZu)^G>sL4r`C^(BODO_+=Brr zDUO!|Tq-bq1Ei?a8Fr%)d1DjD?xjqzX3G7C+IU+s~?&quta_HH;lUGnHKW$t)n`2x{gd$vTnFj7(WJ3>X zYa?OKWNBMh58^+c$DFMo3j^wYujfv0nuRL;l)bZtEF?ZEg3L0Ufgv-<)f3p)?LHC1 zrj_6~6=VCpS7zv0{Th_HV4&er9F%~WGjfulvnU^1$QuJHTy0j3ETr#vsy76KO5 z=W2wtK(?Lg=ck7iw?-fL?w?2z5vvCWCNlc(Y4Du!P7otynIssZ0@Gi77ECg0To3jfa|ewbjlR$?Q6-FM)o{wl z@S=ydS#;sgg=y3~Ct2;M=T>E!bGmc6<#v#rwbvqn%ZPy)g$ zG^lRYz?qq;SYw)KeSc^FllJgtJqP$q)ItCqJjo{TVL3M_1 z_sQ=A)??h~_L60h?wb+5DYQ$Xf|aq3<0hSdYuO!8!8xZ{M3wKbD1x^I_afSqJQa{( zYJtg$Yh*z-{;9oMw{`=#S;PR0DwUHwN{Gi zWI+m`H2e_rzIF65opL_-P*yD&eS zEA2G1^(x4??aU^XiYMPlFrKS`Rs;e`iCcK*ZwHSDM?_qiAGs~?U?`kQNR54-PlQ$| zb944&98kXnWBrc-kp<;GS^yH6DA=cj=|-^#p1G|>+4=Psrx zds6FV^5AITlXmPC^J~dflxLLB@DHhmIMSi{ZSDXUR#>pXcrvNbeU52nFI7|k)@n1O zYDEmfHpzF9!c8GY_VsU)=Ey4>>6tjB%?KsoZKgg)?Nayz z9&OQeh=9*6)?_{Mu1K{}gr_5d+EZNgD0^f?0t24ueqa4jGZT}XJgDUI7$8M61YLD{ z;CrGyYy1*8qHVU3v|0Ge-|xAsae^1TBd5`tRlXKzPZ9~kH^_qwHj9!NdJ-qkdLW;n zFkYpuole@`Eq>zz6Hvu-L4BE$1Nh|o3X=qCOpJdX0Q@Yg$I?NRp7rL|gewQ-uu?!y z9sGR7F-D5sxKFM;B&%+)JmQZNe2e3w5A4|FM;`Ld1iltuta@ z(a}}?2tIWtChJiO0deY70#SA>#s>7-b(n*R`|^U&J?n;t3>nvu(1Cs5X1~64s+^id z0Ee{jVn(pV!e=p?_8+p#j9Dl8L9zoJXb(}v9M6zPHX?Fc zvak!i=9Mb)eV#e#7Bn6MUQLEBzbGoRs%UTlDFb93fsxC0Ae=7*S*QWxPCMG)c3a5}jn2F0^U z>oaw~J__zIhw#bg6OpY+8HlpS98dw|@3YfCEH=6Uh@f4^nY!@0r-Rv#Kne8woez%p zEP26WMSVm$gr$n+#RNqP0?sCx7>FQJBA^36^0SFkpbZn&J^p^}@ws>`fLsxP;(CAe zPVZ*uQ(IM&qeBjxQZ4R8b`sGNmnav3fxa#9^5ni)?So=-fLkfYnin34PL^iKzrM;Q zx5=6IW+F!X#n&T*Himajky%i~l_2a`nVSh7p=*ssI#t|t9)YKI;DCPQf@*Gi6$U37 zscdXGI+Ay7`;YS{4^DL%k9Q8pv~y>OE~D_5I@DR4n3_sssOs9*WqQaS`eXPiTI;Ti zBbTWo1bz^DU4}IrR43ozWFb!8V_J3qzOc(|v&~hgDtJt&XI8SOFBcryW*$sHn0OI7 zLJ@jFQPF^jt0*NtMGBA9THIm&vuc6O4eQc|XSpD>gj2b#`m=%>cx48@+Z$I#Mk=S> zo#*M@`8Qs39N9;7{}kh)oAgKsH%tMjso_H*x^4QfHFMqmBO=39SkVOdF9UeQ;$Pq| z>9wM`BUz~!N;u{wog{T>!qs~ZFb zj%!7b8k$BtpK~_eDl%%ltinEDaX)+4*;s@Lxk7NW`_QaHGUvI?hn=u#3@dnc7-;lK(RJv_o?uZ{bsE5zS0>jH<5PWL$a1 zp9v32-U9+gUX34Gw+y^K^f{kF{kJH=M+3J}n zvsd3TVv7Y`J}!k{AuLQ_)98_hC)DVNB2~%bRW9ir zX=k-kxWum5p}d2AFOxSBHB|#hyf{2k?zvvImx=nUvsnIt_}V(ClJ-}-q3E!Ks)Xrn z_x$HzLXr4y)fs>8^Ci<5qyTGn-1c8fH+N$`i~W5U@|fD?J6a+83;agH_NzHvWm9|4 zHX7rLHx$4s;}5Wpd!Nz%Jodt1e|USCb!bT7L#|G}P+FPN?aye(zf3guU|qk0@QhDf z`9ke=4ai)gx&yrGU)C;bJ3eZziqY|`GX$A>pE{$WB|&qt0v{2udTy^} zlYIprkG4T`rD;gK{dP>z8_3YU*=~heN$yU5J`jger20OeyBJ{qZbChb6T{H^JrYJy z2a}Ld*r({csaR%pE+{xb{f)>(_>{ORUy1Q4UV}*LB{jdCK#eG&&fxen;B01Dojkxr zMM+vIle)yR-iYu=k$J08$m`=dj?{t;J<&uP0i(@!%5z(jWyr3&M{66Y)o@~l8*@1D z)3zdT5JO%E2XBygJ@zlxN@h3^K||5;ryo*>w!;5*u3THwl}m%!EEjl+OX~J6h|3tk2=V!rI+Q17#QTX`!>{8ndZD2>eU3YQ8&xn=k-pMtL0Cmh&0rG zB15qx8Wt(&0^{L9-t;eg(yv8Y-@mhNGfL>ohy|T0t9b`OQIv&!6R>`ROv>gm&%7-_&uEY*c3bl;t1wB6GaCM~@GOf;)&05WEm@JJ!DVggU4Gr4 zgV#F?u%IR_lTGE?X^0yHmY?*`8GCUUJK^5{u=m0rMt=LtH?oPHi8VXbXnKL~yVvi< zwt{=rUW?>A3`kGXzxK87M{DX16>d3=vb`=GPzm?li#Z?tu9cJvbPrnhr2SUu3A-$z z2^ePAU_CXB$EY)ZBlFq-w+~##H^-Hnmw`BOObGnZiF zed_hs8B*PL4PWx~Kx5hpVBIj$!nuF7(KGCcqY9ceCF>WTN#g2;w&kA(*qgY94i>(W za;82fK2uM{nyyv%1EKw(vQnuj{D+h@rFFf^EHC)$tWJkh`o}dRlBUsm2Am1!stEn0 zR6jRUYR54e#-9xhh2;dlvfxc02z1hXP{;b2{%AfKiUlRqSm~7vjg8M=o=)SlqGY+5 zhUqc4Fu_ZDl9|+vDPdTSY9HQP+&UZXr)|gje*HMj#n~|+C+kQmGK-=ZA7^fXEHK=e zk0>Nbav7RqBpETH<9!3%V}r2M0YwU7i?b?;^f*7ra3| zvH{E+Oj2X>QZ<~ErE5mNyN~JePc3YoM#si(ufyGQBD-DM^+REM}rJ_-1x8&h|)KNAW~8eV0}8M;ldEGT%6A&7^<1MV-J@jmEkoRD$ja-6wmpyRxdDkLtk5h_)e@)i%Tv~A!Og*j6-JR=7oXx|Y#;lrBhsocy&n=wi zS369SN?ea+_H$^Y3ing^(hxu1a+fZHFmJ?nEEQUBekx$OjLmMBhMlr?xSb>3INyLO zd>r^0?qngSM9{Sx;Ix^|zz2;f&^4x#tG$Po&6!(1g)L1_8)ka8ASTZUW%VY&@q-dX zb$_~}%Tg`AT9Z}yH?uQWSfjIfQcc<$I9eXz`4Q!8dpvN~U_W72=&A%-2a3 z9v@1fI`R6oe37){@@w@Q2wP`RfvwgP<5L$NZ}?=Ki8#mC-)xyH0`euU)q<9p9g-lw zFXO|uW~#!H@FOlBVjKhA9mWx_huBUG`nEA@J8`}jz~>PST`xubs`9qeKXwA?I;x$B zn=+YSvGIQ%N%T!wVstxZR!LAJ5g9aH;qnO7c@-PYDBD=YtNX1=E09%2@{l{Ce{YLU zJ3L_PTUt`&^GU>Sz>FDsc z$~f#WYj@FT3SDVM%1`+}r$+LgS^ar_BTvjk`k4~O(?QI zJMrfoG@@wxvf!%^Q4dS6O3sxjPxz|7rs`7l6 zGUj_b^zrFAREu@A#bq2Y{+(Jc7d>#+%}|_6PY>Q~!LQV*N;A*xWOqfc2zG#DzVgM> zm@k{8dY>kRMZ<6`b(v>_V4$)0DwfI$nB^nSA&7!3bJh2h z=1HZWtr9mG4gc#DSTcyn<|{f3O$w^XCT|OqVuU=cK6H!MU8q-R-y>GuCk=^B&nW*$ z#1g{0GJDUlWrWj_co;}2e!6;NpEE9iz?)SZKK{LgRwnTM4k=&mJwC4&N~eXify}#PX)~$5ami zLaoO0mj5s%^1rPYAlrA0q}6ztmUdM6CR1FDI@jhg9#unI&61?09g)$sPKcy7h3QU> zf+SCmDtT9{wB^KF3n9*EOiMsKqK%dQhVi{W%g*rs)}QjKxVNLh|i=q;6jDZeb`LJK0{2LS8YA z&Jj!!&&HcAXM9}uVp5)tICL5eh%qfH|Mp=LwLq-cdVcj2K2b*Z6Lo|1{_?DpHGPk=bt*hkB22Bj)V zj%HhPwx?j8kUhRI)@)Wl`wR-^Ex=_dUidojd@t&@_pMUq;GA3)Tv!sqzgK?Vij>mS zFA0hZ+31N)o58Y32&6=f;92ZQhf0X+W9nKy51+$Z^CLbAGSuJOeU=y;HSB%9o5Qh9 zBfPK9mmfwBo_H??Y{I^?5Dvv*oN(F>2oV33J^1DzE_5%(U@c8WTqmr0-cbzm3w&_m zTGw8y>ETmw)Q&kH+iEaH!)0(qlug>a4b&vf`cQi|{SR4ozlO`Ha;KI5$PX`2HGKLF zLc*VJ`3A#J>_~`zYnA>|8h#&NFkoXBo?oM-T2CRtGV~H6bK;?OOx;*g!qB4G8Cx$hRS#2};_L-SMk&Z?`rpn8mF6?+m|pvuqi4 zucWLm>i1r_ETTt+<1Hek*As&87Wiv!AFL@}L$myuY?7AoeQY#BZG+D%SR-8DdW+hv z6Wl%IlQrbxR_G#r&g`bj^)c>0neO6{PP7V>X)&U=_Zq+?E|7i(yZwCGO;YUa7t4yC zKnZ6nkK1T+|GDm2PpR|L=4)r!dRi6nH+#qsQptyB((Atk)!C1wKWkA?mTrhCbE&z$ zvA2+@<=b}A<-e{P8LV-yh@SuMkW19$qM)Zs_@KwOHQoJvvG%R#?b7V}`ZsB5-RLRP z-cWSy0`CjQm{u-p$L+JW%iV{k<0jXeNR<>df9r?ynEKP~7LDyqxCk1>IwhSHjHQu8 z$D2)y*IeH8HM!^a(Ao6{#r3Dz0fQg!k}0APNRd_BoURJWs5Up;YE0HXXKCN*l(Vv_5^|4c@y%jigIi8fP zXmOWzNsV_*&&Ts#WG#0m^WylTRHx;O%w@L%_rt8c?N#m+cH7TLKBq+-)?nVsJ8^41 zKH7XOxUxK8z$D}Y-GTTc7yc_?P93o7rD-E=-NCiBym^t%Yib*HApj0{sEx7vDgZ{Q z%l`@Q@9M`^p4i+5wokFz@0H>`b{ z_XXmWG22lUb)}s#HFxbgijsc!zLzZeumv7VPgkpqah2CAE5K1o-D8>QjUU&1N&wXb z(?-UJ!sfucrJfCI7@(E0?ncTaB133k_A^Y*u{|UU>ITeAv)i9?TxGV!mZ$!(eoP`g zYXLQ63_I)~BdSFprZ)4*g`kYz!B$H;5X>OWh!aYCCOA*+#8SSV)v0eM(^Zo zW}bqVAH zs^EU_-QbrvwRwLIU8x$z1f5tN?YDsVKZp)$3g!nz{R>&z?ZBTny)#)_m@+#P0|;^| z>TW}iZ~mMQ~PB^ z$uiLGOXfhEUu64q?dbA+!*q%Z{inr;DMq+E86ES9eBbB_N6alYD{rUG3SfrP+2-I?3sqPw;#4ojPA5s9lWkx;W|cG$~6#o0|l+K9b&- zMsIW#X*&ALeG_(6#hl)Wr*`NS$_|st>m|I!Xvfjc>kTNyazJglZUAt8cDb2klG@+; z@EqcP4oVu+i=a#UZS;`5apvB~-G0Jy(`)xK+RyjL5+@ZYd1J>F?FKWew@qYR#r?WK z>1I~Kt1De(RIS%tL{p9a*VWOQ*BsSeWuW1Vlo|VhPkpB-=vHc6Ou=@QYJ_6| zFJ7V{GfhA>C4eW?#{?0a@{E^qDo-xI?xY~fkr*k7Kz&9)+KD+7UMJf_?_$LEAU0t5 zVv&!t;17o0Z<8KUVq(@{>r-=S0+Ew-gHWOP|Dy#kbg3ZL5JkL*4f49DN%>={7`vs$ z$0SFi<|-Pf-FroR?0Zbtb}D!JC8Wy;JEeNu&l%7PhkRC=IKoskRJ^TPlFsulbw1e0 z*;O3dB%Y&VquwIEopaEW1>o8AB6_Q5hIDz#Upo5~)h;D#xzf zddV1mBVau%`{O&cI;4BiOHB61!S3A`o_^iuBWLGwc_6=p9JV}@HZ#I@wmy_77PO-uw>u;?0%-XZMH3+5~jZ z1cE3Y(=&Gwb)y);(1Subu&33-A<@uFPBDE@H_z%=jd2U)67fK$n`p4Fav=5#j(3NG z(W8;7D#G#yTYE`{)Q1;pBSKv6Fl`dNpXOrmqN>}PzkXxfo*`0iYgz8 z#%;upr~C65NZZNo@i}>RaS-#>7QBgLm@gU?Tjbf>A%#S*7u?toWPS6y9i{iln>L}KJ`=lRvRF)ETwg(X z%~S1@^w0Abx|y(5Jmirp!Uv3}k># zh$N}JKQR+etxQ&@Kg8fFN^kz5tR35uGlj+eX{d2jXVHxWnCrL%i7W~B)0CA|)?o3t z-EHvd*xw`yohI2yQ0(dDAV`a$MRnhLiP-(0BBg^8{#dUvXF_#2i*YuyLcdNs9o8Kgy3MF9`b!QZdj(m$v~P6B0zG)#&)#} zHc5jd??BoOo3tEpdoJwC#>+UNe{4m+V~z8?*^bW>fsgtL&pd?hc+buiyBv!5t35FB z{z+%(06I@;Lyb1QawF{>?@pT~`!Tc^?wT5Fn;eX984`AL6Y`!Yn1oS3I_mII^3{+5 zyT<~rTZejAQuKUia3}MP9hiQ933B07^PO1AQ(r8-^Z+TA!6M$eEiO7TWt&@+w98+- zRbDjj)=%`uhr5Yk9F|YsUWgIhjlNNs4xbzQB-UI2-58Bq0Z-#kylz!hf1U49ZD(WU z8wzeaI4SN+BDM!N6>r;Ukbm(Rc>Nnr|DSH7VxOSq)andj>>%ysq5n|THGwoH{#|Dv zb>bb57lttTHDiaZ^Jw}j^rYV~Rv#?GTv=H`LD990yTG!IV9#y(Vp#02_7jK^ZpS|e ztU|5BXDW;C4@$kiI<>JgWq)h`NtdmGO@?lmfcOrL&b;UH?}5frm1(e% zaa0y(slO3)o6i?GC!~zWeVX=e9l_JR}L41P+eHNUc*4Dh6LZQxZ)Ej0p!}ZuRyHh+hT5#mtH#EqrB=FTHu5uz3=A{)eEm&C|z6MBwbbg~# zOInYW=m$n_v*a>Gej}FEnot8)mx?}TcG1+(8pz+*gLX0|y3nl@_mfUp-<#8sSpp{Z)HX-H?-qN?4a;~689LK>Q_l8ino!*v6baRK@O3GtCR zx14DJ7=EZ_TfQ4$gX^KXstw;L6Z*_NIo-(j!P&f-Xk$TtA?KlX91*wZi4wBiK^5;5 zyj(hWMo;K>zPY$(4;c9hc*TSg$@&+7a@bCYP?stRn?1Q<2ahz3eHF-sjB&g+PzT%K zGLKa>r=BkggnStCRL03tX%f$33NziCF4P?w`h*{>rwNDeoGD0N((8gt1!M!#OKq+> zyZS0m94^(E2A^41{UN6nYD65rHfXJe(rOJ309v{0YEBl>f`wu!pJ>3(Ds)u!j|ADc zde9f~YAH5#IhB+%bolw<_R@gsv}pHA3$DW{0|}`a0g6<{%Y;dRr&Tp4_~Fcdff^-E z%m z%K(Fysc@YS<1HSjV}L6=ZuvXf!kI3h2SB&5*n6Op3;TpSimMLqm!^~dW--QfnrX^% zm&f*Lu!AHJ2ZPo&>Q2plahsBOE+eh6mxk4VgpuS-vBzOs^J zwCbPlV*7#7jb&Gpp9}WA| z(@NjhQBKw0`p(k>G<`)m3-iIrBbo@}LF|Kro?g+j1ypq#6txAEsS_ZMQWnAO^tU-$ zw;DfYU&a}&10n&a;HEreWKd8vTJncBK7bNBnVvt@?M*VDhao!S7H;(>O@M|0a3Gyc z^&N`8Dkn$4v28t2NL41z@e$WFGl`hHt^UpBX)3&Ln!ZrDk@YOvDndYccP#UaS{gU7 z=jdUG5^rFx`XHDb9DKu#Z4f)9r50~s6C*x;P=tLwvgiii7+j&;wrzVqK|YqI*7@EX zPfLkJN}>uQ{hU2>8_DO>|z#JL|nj4muT%Sy-Hx1hyI7TJ`4ycI7Bl(Dl{=v1!0 zZ%SJj#ojN(9AyMH(ag9EY{u7od9*QN{(P5UGN$E=n#pBk^lTYy1wUbNbTKTZACP1? zNmw#In+$_U7!;DaB!skvqk8@0L&YsCJudx^wC!W@?4MYV`{k1zm(08JhJ~jrBM%K1 z8(+kXyi;iBY4BbBZdbgCCif*q4qKDRT?3YpT9tt5uU7TA{4zuX25t@+;o}rBV_a+! zHkN;=Q*DZvT?jZaQcLSj3b)X=t;7v1D&qHosgoI0&O5Oj7BYV_T3#chE2tA|yp-+U zi`*OYYE@@n)?cXxMpcR!PT z@BN%-zvtuo3#O-gbzj|8tEvjDr@1LfI;@k{gcRG`Ov)FD+SWt5=X||Sx}$VB@Cw(;EkVVvhhN) ziFXEJnDZQE_-bO(DV$?hF{G%xN}+=Kg;h)T@EYN3Ef9!hry-%84tx+ zg3-DkVvBcHl3O{TBJE0lPM)T{LH8oIW!+nCzAllgtLVm?J;mTNNvgq{Qrx|fa+-zr zsn8a;_|R$PcKM43x1)B}{ZN=XNnVbCLac7eK!{?xA zkKHkC%%7k56;bAM=KizCtZzvTv_D@5<()mIekG8u9r)U9zU3g6yU)2ll4vC zwvpPyHk+Ljq`U(gF2S2T;>Cp$#B>Jq!WkpMQ{z$6B}KlV(29qzO}A})bbWXeh^HzY z(}+!~1xF{0FRGd&>mK6kIP-Yl<+R#bwe2V7n5+eu=jK{WX=2?X5a~DWYEE`1lh75l zTJa9l-QDHPA9U9$?LO+Ykpb2fu`LH1bk_A}vtH$zdH^Czjgj1>MEr@!34V7Cc|Ch> zzIT2VXXa$O4IShBSlH))WhI+d+fn=`@UuOe%a!z8ea$Y>Gq2&*r$_pLp30lj0(bc* z3WhI$yLA01Z5#}%&ME44pB4NPrf(GjP4{W=*G-HbHDP;81+rWT?vpNcH6~uNw0`?{ zY{~{f0;wMuWr1;ViK?mr_4n4df^@@T`l4qu1t$!;Z>rr89{wo2dKZE6N*XIfnPMby ztncYg9rra)Y-iJELuq_?^Sz8EqrsS-iK-s)51_j(`toM0&hs;shOnb0LiL|jA&^l{ zop|ML6-I8jr}U(!OJ*n1zHJ^?L$&iO{HtKUt4w zUyzRQ4%0Ui^k*BM3oZcUqi^u1ZQf}znO+i{mdEk_MRUX(flF!9K^Lfq`3sDzn&lP} z>1UT(=4rZ^UG|3Mt8NYfeAs6$Iw}}^vr`fGU96NKlYm#(btZhRm}CZn&B8&7sZxqn z7%YQ*!H&5`jLdr)CMBvsEQf$qkkI4_8k()bx{@&v+OBI%&D1j>^es=eESum7-jCS0 z(B~iv|4Dcwt(cD77FBk>9ARjDtY3MHmo0gok*6plM43cZ*70Gme(qJI{W-B|h{-=~ z1V|aD#^SDeWhMN~y0qO&gp4{ki`Ou+uB;sIr>9QK0RH2M`8Iqcm~7evyLk#nKd3x! zFHUwNxjK%VE}b1q8jqD)zTd!%-#?rgZ{T||@om@3p+WnovXxoW;k9F2Y}d|q?QotJ zo9{2ft1wq$V6u+g8-p!LToUVH9y;Lm_AoxGz+396pcF@=CF+*CDythT+s>75+CGH# zTQ%1ePR#=Om!G4VchPK+!XJp?Hf4)=Q9x}OiP4csb z!|h40#smfi+{ymc_*Qbg<#49f{@dzHR&n+vu%~GCf%3Q#b_NEc4HX!x=Lk%bXm-2H zCdv4-@{;{HsoHt5n9tm}uB^5Y%G2_kEIMnO>*m%})Al(8#nV^Km)tVfaG7bggIYR6 zhCA&Qbrq7auq@SgQaUC$+ykLek?5k=2}+}tUGSX)UZ_po(QCS#nD*vI%K{!fEm`?q znR_p4WX#ZX*k1&loZ`h_2lJ@!rOBTHB}K|BM|J;4456@aXn8Fk3P1D+<#pT>3v*Gw zA9>f1+#fbqNNl`GK}VB%LLM;0nU)n(=Jr3wEyPJd9a#WwvmI-jE4<%? zH()dlCn(tNQ2v_6{jo#j)Yb7((As1N&jS``<|oP)%{jD9!Z^qFWurk;=ts5iOoNToy47Aht~CWy5s-!D+V|kl_h59w;nMX4pWoTcE9br_>S>Z)dNU zsqku(yNlz0lfs*sp+;reptY^klD9t#^WN>5(4|znzTTUO;&PmLu#1TZo%sh&jM6KS9D+&HFIk>*ei$w-xLo~z49B!fM%uJi zKo*AjWCKPPLQ4r$zxCS$B7{8K^Rgf3sY-^n9|S|6Pka}kA6y?^Z5;#^o@Re_x|v0&maG$naTv+Zhr%&D%!;=}wNshsGklC1@%gBZL22MhQ zxWH&6H9J+H%pVsucegWA3cD(W1RJ`MKvfI@)7^ zEPK|nZ2P`BqpyM=e9WuaJ#FWlHP!E5YI5zOwc4AO5`(WP+=WRILKepj-^04L# z54)tt{1Pg;KUC_+eLi4?VcsL$*Iwvt>9nR_FS)NbkZt1iUnzT(D@mXIgEG}=1e($}w zFHj^s*m#pf`mNa-*n8<=Z(7MhVz9dU3w%zvf0}YrZcGnCK&}A%K)O&9G7J#Z-0E zVeiDIKi5~Y%dKU;QadomX}QpM@&n36!~yHU#f(>H3-{(-w%mI{y@V`3mw}UhcW!BQ z&+8`XV;U3XF4o)je>beNNkO27aC?eqruQaac-=&OvsiDY*0%Gd z#_-@|KNLr2G+yy#v@m&Pl)dtldxEPW-Q2);Wws4CINQ)_oMr91VB@(Hvef)gytI6u zwRFEzqnyo3E9#0#%*@s&)kN;EX>jatSXT7r$D3Zk77j{8`$bbu5D`k z8BgXa%UK>+%$DDc(IbdRIdwV&=Z8m@R|=-f(#(wM2_VnlP0}kL(lvuCOnkP= zRhY*zIOCY{lAu{XVCAk#@XK{wIUZYm@mKnNg;;rJVhjsIV$J*STvIb-XVOuF_t#BM zH``t%Hb)5;3bktVFw=)5=pr?K)ld`S3-S=;j-n+a=+gb(xDgc?5AvbKeL+;o?3cbF zR5KfXg)7YKL(Estx|99ZJU@VJjoF8Q`_1e?3+jb~P0tMd;i&tsyo;I4#?p7g818-= z`-~*vO;fIHrZJUja(7WwWa2Nxf$d5M-t5JhRJMTNy$Zt}6P2%3glmGrc4;H?RJ_-T z&>cx*((dv~)lYyT4aoz_hbSWc-2g=9PR!r2oius3N`F&t3g|(pH&PLuK=wZf*AU+@ z!KB-O*PZlDPTj7}A*LS7aEjazF9aykSuVRmKplN#eLOBr!?DhIr2of8WCFa-b8)-!httyx7vSty)=F3a*==^r^U#Y+C9 zLR*|*z?@&s=$2T5rsTK*^`t6YqU`XZoAlMQ=Q-6ldus*NHPGR%76FANw9mPP>;dvLwmj z&_%GjELRIV!Cwp6h4KrY%8J!Lkf`- zJ?QMBTN}ym9%M2KYOjXJQmoY~i7_h~&h8k>z-wYkd@5FVMAR$x{|s4TNxGwKVk$4c zVbygz<+NuBA&C&jYZ+C`p?tC8WOa*0VxJ>ZkU^yv?{fJ5nKOp2fr~Ivc;ls_T^=W@ zO{}8bXOzUsP>o(=`7u5J>Ji-X;$0mi+uHw#@r8{VncET9lT6(9#uNdUKodiY1lP?B z*it;8vcliy^+toCXQsfX;G$rr$m&_!mkz=e#-YMk(Xx@kUko7ti!wAxm-;SUM#9eE z$0qSZOd`lTCu#!9Ply$=EDvEbmF-DE$Snk4Ato#ENAFKYq#;^bD%lM(MweeOV#xWW z{z!Pjjz*=afj5TsQ=#kqyIr9|cC6;I3nbi}z|rA!>~?usYXRF)k0w5B9HZ#%vWd>< z_56qrQuI&mXiaDeePHz8I7tQjTq39}@+n@)|I|*%eh8R%o9)%TRBD6rVg@c^b=`it0}sZO)i z^2w~+fgdgs+KoOCP=QdfaO6ygv}j|LVHcQU0WQEAj^O|#iAYqW$U-`sGMJ;QR%NqQ4gwj5I9+sfU&>1Je}^;F zzUZEmF41BPb2@*SR*ZOY>U(C<;vLAnFzhjt3-E0>#z5`fps+=Dtv2kTwzRN{B z@8Pr5H6K)Y(hx-y{-yB#KFJ(;kmMJdC80>W0cF&^pPsmT~V zqBj)5du9AzQ0eK|VeXr-AJDfLw@eL^4l6!a5R#+NU1V4e6~1W8+~Nu+EGPF}7#Ky;x=B(xUM*2Bbw+5QjDWYU*`0cKB3M}HQ7t&&`VP1JPI`+vPD)^S;@C_n-cmVC1OQp(lVr=s)!{>;?L*Z5*vWec$$4$duP^Y>v3z$%|^%1bevGmrz z-Ar9&vynB*Z!!HnJ$m`shJMdEF|&7tn<+NKeuJhX>A~dX9CCOfXaibMIeF=3-*YdU z($_0=FpGXS{l{`f0>z9LO>y|qO=*V15|wXXLjt0#0CKbz&+*cji)aUZ1)SFmZeQO(orpx+o1HG=xmy3Q3&sP4Sr>@e_T z*Q>(q9}6+xFO$H3mg@v~r3ewZvZ`^NTEC^es{kHdMbbTJ4K4Q?N^k7YXESVkM}UL; zds02hSbzT>r8x}idU;9mEk=Bjkd2!8raTxZ`+dYlD3{f2pxbzV#sg$Pg1bY$xAg}F z1(r=+Gg$N7ArRT!;qDj5b@!PEWE5l!a<_T08Ok-g3taYR-{dTRui_F>z0I|5 zm^wlJtMR9t68W1P;Xa1W&ORd0HsXbh;Y)K-hI(UtZonso^C!fsVhreI9adza%X&7_+UR$Bd znyo2ZQ106f%les%Xn3Oy5JJI-MIaW1*+{SutY5v=EVu@*n~;Ti+N${Ff)qM-Pj)xt z8w@1?MVdkzr_%;cst2P_!M?|mERo4k;xXAH_j67u<_!eRcMWj{MYSa?3BLzIC63Zkuok zyxjf;HLpGjZdc3i#um7bwCW5R*8=g*Holg54yRe~o2pn*g0GmxZ zhw^vqo5j`FX62t$pSD(o+FD<%@{(%*1MH6W1c?61A8aeJVbe;m2=Mym5tC6~Jf-lt zMg+l6m-#oFY}mqkyuzV-Ct`drpT?R#|3W>0kb&-t1;$=`Z`NcCYW6t*kxO^vFpm! ztyWJPo!#qZ@7y3`*6A*I*YouBb*J{lt6koGnp(Wl)v--R3bNZjgO>kI^eH$8c*MQ4 zn;ozncdM1mx7@1_Ix&uE^j&w=On;L6y)|TL;FEHxUBW8t1@fK;hDf`H4crcqkWn*> zQwOkVVy4d9Ssa>nD*BTfi>`uQu+l~PslW=)T#~=rQT_q@Bt|L2^n{ey90+w`LSm3m`m1*Z{Zt*ImtMu5!$O!C>xy=EFG0DIZ{(jC8uq(sP>6_KiUjog;zN6W#WOo;Pu9 zN#9xkdzq;Z$ci42M#T*%OFj$w&kWogQ3Hix6NPR%>bp7 zddTkV-sYlT-_%e|){$_gcoTLc+@Ze;-6oSoW#gnaDpD5l(yz5=Q#@_=HFl)5Vb6Ki zjAr(cd?x={=jk}$*GCz7^ZeGu)bO5Y8)u2PjQSXg-X=}4@kba;8rYporq=GP9QkTW zsdm5YAKH5+$qRKSZxbl$_%%(UTEg#y(gLF`s(s+127QWfY5Y2KNoZuj+yfPKkKJ6g z5)sXvh+9NbI-r1%cKpM`l||746hLuOvHk_~$q?OMa_34$_zIt8l`iVpYccXD^y)BW zjGXULa2;RP$ueUOZr~?#M*5(zVpO_aH!}IE_CR6P5lFn6QsHFwBl6zKN)_3dYZ+TG z|4{O_qi!TNn^pXbAIrDf1R;AtY2hF__Cy*=3tmYU5zzOwOz)?Nx<7f7wgtUS@nqM4%cJw1HmeV!5H6ZgkHJc8Bd zY~!dJKGIn%zux2QmR4zWEilgPuTy>bvUeS*l@R&#yw5MB;}Yq^$*oZcw2mBiS2G$% z&8gV$yZ+t%CBWEyuR0W?b@qsDtKagN#OjGdO=OA6NwZWgS>~vX!9*B+zPv?QPJYC6 zCd=5-tHjPKH{vO?+Yqga7KXP;a^Z&$!A~w zIxqW+j4j&l43~8P!VAmh4V&~D`a8p@E(n#axnxd#XsD@OcP`qn_&o(~>TopE2SA^J zc{y3u3aiOURzKOImri$>KDCSwmXkNVnx4~^u46clou(?7c34#68%yC)v}mjt;DC>X zXu7jbMlBwB2ah#YtbT-*=WKj#?^D&+XC_EPIjK1jyxzf9$fVjM_(RcE+G2vP)Pd)# z90;|r(Rc0ht>YH)>F9d)xm+{z&K@J%N=;_ss>sF|&im^ zg!m?7C)-aAOE7n&$)AHiob{o$D3IM+mB>&5cD6DRg&;kYo3Yb*-q=&9%AZgXzX>TL zK_gE6#TZyi7Qd@sL>+9Xw)S0fR>aHmM#Ss$R`m7lcJzmS@2)lXPYa{V2l|{?6P)RD zt}SC>V}NvnO|Q#TDYf@o=9<=f$(q-p%zbkL_Y_!Q)7opTS@@IXopWJr>G*2PyJd~Z znwr_ra-^ATA7D)R>zDLCR3R(iZH=5-txx8V!J{$QF0i6yY8NeYdzgWq>q+bO0IytZ z{!jNZWMM(!;{O-dYlX3PCHdlq?{q_v(PcJ5AaKP7SU3S-##Ay(PxY5>zH$v5QQ3djkKmovG!8?!{#9e7rYa-2nn|A2ES0T!*ki@ zUGU~0W&R8OcJ^aV>c`#6XFm+H&k8YoHHTdJ(Q<+JImklza9sKhsczz09e@4rWm85J z^8W``$)CJx{#D?;TuZBFseJu0!?W0}%=0l7XY_dtd4K8vf6Q1%pNqillvTO)GHt05 zq@%R4;Z$yGT@bh#PTyZzgH?Ddvrj)T5H`uO=C!D=Z#S-QbL>4`bf0aVbQ?YGth?!S zoS9rV)VKMF^t=K)Z**(BYu5*~t(oe3y+F=*TsD~DcIDdZtMijjmTo{s^Mp2qdrV_Z z;yUl^3)8ToR8Y(1_jIxcWwQ#5T{$3$pU_)9DgrKPx4o6`jwf%2WO~#1c-7DJR%>52 z{e4y6cYbyPN*alejypXpmT8AZBT)7w;O}l-3_W=D*w|tEB{A+EB)szT$_OagV*yOe zT+?%XqSkov-*GUcyu2uFCtEv<@-*0_3*|MHR zt;KF)qw-sQG?hGASAd#tjEuS{Up)xw)yOi7_rvm-3R+aDK@sWO(tiV-Kbqe7F<_TE zVLqLZY;#!U!3UMI@- z9;u^DU{HbavW~WPS_H0PaDwF#9LZ42){vrs>XaSS9AeP?WO+n~`vu){CSUS2 z{xGJT{(AmD(@hBN_#oKDvG#Py&;RGb5e`G@?<3wpiE~qw?Ym(3SORYqK2guBgA|4{ zxJ?0?hm$J#yX&^04c~s=35isiM1FgCT>Xz3+&M%JX@^1ftf$dFhE;Wf`LAOzJ+xUd z;1h(1B%+wCSDK{%r1sd-z$)M_Jw_nJ~73O=2z3uAIL}uofMLEJUY=WTjy8mj$~xy4 zczU_~(t18*82|M7))eL9AE=5aniFiZ*Ekg(+;TA-qW4{rd>>+bdTxScd@2pMPYaUT2BbqR$`z5#a+8M}b9SpM=yOf0sj;s`1yuM-&; zjj}f4FQw95I%e9~j!*puh;j(%SoCoYv!0`Q6Tc4f3jJ#%!cDjDaQtR_`5>=N1|LH) zCcrYINkZAzso%e?EDluK{-8qndrd;JCWWC!>(m;>n&^RbYjsXpat~|$gbnSFKpHx@ z8FJ&`n4-Od*I(;Zggl)^-dJxQ|svy4Nk=Kt=rbbo_-FN&mqdAvr`CG7c~a(WAFg8zZ8?CIbVX z%fVt&q8?FHn)}F%FUv`13d4Zt6Lh3^=RgU}0H<>@&#AOSNIrBksh}comMAcf{HN)D zNR#R7kZ|!uY)$lE+M$*(U&3;Jb*p_P5W}0(8lEQs{@uF&^P_(k<#t8{vji>A6Kq(% zyZAAty%n1WWwZxVh-i%=od*@?iYT}$k@^538GwJct+ZI88%(mZlG zc@qjzun4)mTUBsan7C4~H-+3rw%I)s{~KNY*V20+e^Msps5}&PFiJ`gHD*eG$RTT08iOqJP2E1BlDg(ZVsHm; zF+~l_@zkLdVd2e7>#Wf0wu`V`Xi6bioJjolxW%#ORNW)C)sYd8!W)0eKzJp1Rl)DU zn438*z7!FI2~{R?Y`vLD@L6bs4&s^wqUCs9nr$v@l)vLd0vVy+Q&j=X-TTZtNx!6i z{Oj@g54QgAalmtkAnieN2Z;Gn;~F>q{CbIo>EveAccqf;@+S;n%TUGY{ z!LUXC401C(Ia;(lUbjn0G@lZ3cCH!i?QJLtwgg)e zmq6h`TZh^!jIMOd5u_}16n3UoC!DY--9q`J%TV&sEUO$inFt|XiG#v-b1DU4unm?d z4ezE+$o-lUtd}EpVTY^!*30;ksH!ppx)qN%B;o&13;(kW&(=AptV_7odJ?qYM66g> z*v#bIh@b96F?1X(XIN4%Wa1~n&IE+7vN^Z4UKdDgAnvDLEtHMO$oSPG_(e$psy`te z>b^(AVQ=+@huPqrJDZB_|AJcoSyRC|T9MIDj+ZOV!!L?)B!=^!v*v$xU^zG#Q!WkX z@fL=gFybi3DA1w9#)zFHXz3O^3JuPwr6*n{N90qikRVa%y&=I6`eO-n3|)DTqaXGk zG%)S#Hs>MEOl_1ck|1PUWeD199}#DC$4hGjMe`XeueGni6W=pwJnu=qQxWs-kD)nF ze=|H=9TXBx;r@RoACx^n2*X(5#v_3yc(f|E3jYb7crGNE17SqKLz1JvcT`&1o2ZMN z#8blo{zLZhs#1f`k@(dLt~WF42vcO4GARv(+k0miovre5%VB-4e}0f zkSElpu}s~K4RjkJOqY@+&%}x_7~!RpLRd+9Y7>en1g&(VsJ}}X!g<7*$pWQqep@L z7Rs@>dHyPeMNAbsO^AP{+4Os!Y`2pt7vn;>z8Jzi~A>ItuY;2S})LO zi0&E+st&1*C8xP@6mH&y3*QC~^9d|GH^`s=y0}U0qMi2soCJ0-fE`(}AXImu%FWW& zHu2tAFeGYltDehpXAX#~Od_~2S1U#BXId)o=p~)u;5}SaCR8#rUD!oBL|l?4D0`y- zun)`I6GqA`%x0e=YPPkHpRad23ZtJtPB)x=KWZdxgPMwPA*#QNXcn5EGd*dmQ$%sU z<=OMOaPa0Z(9zWj;K>ZXWTu;_63z%hPNxejeRn3CZ`z!iqn9_^msokfy%sc2JsHqk zFxRT7+NB`+ty)(qxBSc(zIf<-d=ch-aONJkVUQV>A%0dfZuv{NdQ{c-BPi>_<@lbW ztep(5a1r6?4VqcEOW{BnIv*<*hsY83D(&L+$PYgY%$t<}V8kn&Eu5klg3v>1Brpzy zxCEofhCZ(XqC@qeX-;*=zog84W0yq4w$!P0#tKGj@_KsZhu6aU{i~KlE7~ww;I@uo zulb}Ro&i9YP;WfOb_T+B@%1&L;&EUCn*`(zj*~p+FzH4aVgPzv^ig}J2CuMm!z1_I zxfYDfim&29uMPG-1Bi0b(bri)O6+8K1dB6_o*McT3wq84yXz}Ch6BggBRq#`h6Tu1 zn+u|JhUzi;=+si*Mur7+CN}+s4yWh`I2ZINcI=uo-p9!fHlkbD(k}1^+sdzQcTwW( zEw)mx&2JXuWL1c(eW3`>CK1^4XKkb3MX=Rp<+p~lmsE8q8s3&n#fOD&dr1uRQ1vv5 zVs}8OkMTUxlE*g|-zCdS*(Apue*n7y5h^qI~xzPqtw>d-kM_ zgRuC$iDzuW9m+LhB3bE^^!I+A?+l9I9jI`Yr+L}Q7r9@1fm>4^9x~Y=zahxYDP5dT zPuYoDs}GqViNgJhR=D(9+)^C7Z}aZ%Xacf#TE5G@ORA8dbm2r<_kxduP}pL-W$3QY zi_lYDE6s{K$>)-{=zsHJ*dMI>#0odZ9cj!za5(E2OtxB6V~h(E>=rSY3|JInqvjRa zCT2$dbRAOdz-To!$?TOHT!u{Ay>vHAK4*NJ3}VtqX{Phec5{5#or8u>zj*IHzk)pI63Si?M}{SQ zp{@5m?}^)C;Zt0y$aCGtZgO-XCDnYfb@e=}xg2=KE$U%)$m>lf8^hsMI8+QXEY=N5A8 zNo9v-cHsI-;TI18FsIM?eM-wN0_+JE>I5o?_K4^7$IZnxc^@Dfa zM?*TMz&i_*U#x&tK?RM$n?(1*;FDrfS7P*4sBU$$qUOECkMRbUO-iGJ_N5?%Rm>d6 z4{L}zwO+MOEkZ6SRh}R0(dW(<*}Shl2$tj#J(-qpaCu1*h0;77$-zBfhZkEZFk)7} zeQeCbx9&W8Do67gHQF}y`1QF@8rp(RzSN*BJll@#$1=5yH6MTkU9;S&!N>Y+Sm_3A zW!aunuiGV=W^-yQV}S$>Y1tpwo;5z=ZFH4OHpCvMmN$N7T&dHp1Y^~~Tq)iRrDJ}- zfwPQSRqL<2UDBf;&krr&P8f~nQjnbzkD&|`rvfI~^7#B?SN&`NffY<_oYYpvbY^=~ zX9zJ?(Kc8G6}O}zCR)~XcRQ}WrqN_;{FcZi)wqKxOtqP5n=rvNEojcdmDuQ6R9j}& zrlqAcPXqcE1_6SQ@cnP;4InG?XuKRMvX^>UelGgS-r?Z89^bp>^fb zUIKM*jweZ7=Am3Gu;0q!k+-c*!(v z;Y);x7V<@I?0_ z@Z>FgI5h{<=#4l_MXu{gpm}f7*x7f9)NczihdW)a8{y#QPH*#mNNZU48X@F$>UD8( zNkJ8Q?7Y3bO<7;p#|V{hNE-U@{>hKlhmW){nk-IhzosRD)+0}~yHU5B!x})!@pJMh zprPF+Yqfnv;BLEY|0r3X+f}iH&9(BBbK=YsV*yxmch@k6_#1X?+<=G;(0*01RCf{L zS#nnFdDQ7`hEsvF@m^=?_0s71^c&@7xN9Rt1EtO0jpUePEVy|zD=>5a%c_CktN+r? zxxK%*Yhy}J&uKh3PpHz?-kf>!u7E_)mEGGsqs0xW)Xg&q_ENt9YYiV~F?pSDl=4AQ zgL@RM$-!Pvt|r+NVv<}9jI;?zlXI8IxiGkPbCJNg;#LY>3SvfiU`b|#s0^~b%XFXh zGZUDZ6QSoH$=ce;4ccuZXR%{!vUI)2pq_NGTebSGPp9qWf+$Fwb^lQ$8U6l-gc~6* zrY5cCG?MS^o;gdjk^6OSPuOo+s~sd&8s>dbeYNu5LH0D@yYtl2YI8m}e1IA zq_W!vF-|Zgh~;#Tqk8W>w?5VDUKT|T#b$%SPac8s3Q67kGV{lLZ`9Q`jl-;bqe+eF z>ipLB=!9)6L@9qTk=#<&AZsdGulsz2Xe;%9)lUa!P{(mIBNq0>%C0E}UmEH-t)4E$ zcI^KxJxbEkg!KI@J!)`y0Fk9lw@1YtZFh5WmE~8B>KYeGnIi4&HzQepKt->cC9xp% z2JRl5vAub~_yR^<1)h4{#wmqAVu3M^^;#_AS0!a9lJ3yfpuw}e^q77glSrAYM;@I3 zNF*8G>#?v6WY!yyo7=dEGp!9Y@Z+nRs1Vxy^Y6WoJivBw;a-cAoPihRvoP^5UOPeU zY9DfXZH+E-&G{$SE2z4|-AyXK>Sv994EYeT($Q(rQBir&0N3o}H{MNgduu*Q{cvxR zT0m-hwm$&(!#-J%A*N080!S!A;j2dz2HIFBsKdfM$G_&L7W9riKnHg=z&yA8{EIZn z(R5Ln9pp$B5>6v7P-5H1MykB9EC)Qp=jB&e@mI0%iBk_|DUT*-a>gI4SwWZKfpfK_ z({aTV&BWAsbDw9(qN1Kc$1(HBMoFFv6ImkzCRjG!Fpw?)w=i}2T_Lkdq#AK}SNPzqM4_Wp&5Q}-YUs7^w2pklE~%+T;+WyvOz9W~ zN*2`!&WP9Tpnr9Q97&0+RN=#zxywwg>BMt9d?_TT^%Dg1LHLoU0pmd8A?iHV4g1^t z3wb>LJ?o;d;Eh*G#|Mha*)7k%5~zey_E}dD(w`s3wvAweaBg1rE;H@Wy_xzfua)lp0~6>j_HQt9)}VI}6t6>l~b-eOv>NbrwODxwcLLc6vAaWSCma8|i5 zbpln-n;py$o$R$KaQfc#Upzb8^ktSru5FSNrPTt(###jK>!(}%heJg6I={H8^=PGD zDZXWsk@qUg{I|p?)>CY=(|8sr{`t`spKn1->>LxjfYPHu&p!tg+KVbd)_w{ zW~I;X+7a{|;a5`c?Ltqwl38OJr$DIo%cj?duJ78QTN1z#RGw5j0mt(b$&IJL(6LMJ}?@8Wvx z!*RaCsjdc__l0<|8QgaDILpWs39zzhEA-w1hZnsi?#umf?W4cIe2BPV`;(bF4y8%ea zl*k-A?X58gRMi0qnUa!53IzvPY>O3%>6sGyX&qkG+9T_& zk7d=L7*I!W)=DPWj^u`1CNms%IQD8j+8*|ztE+d9m0gGGVR2;72xvG2wqU>hw(Ep| ziru^Jjb-F@*!({FZj=5>NMBs(tBD`w_tox)QX0-{a)Pir{f8U>jF|b!ubPU5J?REO zJVy3Y-0Pt^)_R7hQ&&TPIG{ZPv2Z-@`fsP^oE{q;(dK;?4Db9Oxpv^#7$y0}$3oMg z(j<(T=}>DNf+yqW@w)0~bJW3UtK}>*g7W^oy@F~JI@2#JH0PmuyPENg?_H{qLI}lhVL$)VV*MbqI9c6H1Jia*5T?$IgDr`_gXemQA3 z-0IT-^l0OQ3Pu)^Ib<(eruXL-oJxFw(fe!vG%j?kA1@3!pScCx)JQH2YB)Qa9yiSM zNGm^wg*G*o$(gDsY&7TB$}PVkAjH%F{2;RPCge(!p>}b1y@~=|-N;KPBz*I;0V=u< z{4JVU3lG$eg00?n3$;+8&&|eB4_$ZfP0!6$Q9{#|-C&7fP_woUOKXj!F%#0X5HfW1 zk=n195`i!QgsG4rr&P4t999nZw&+NI;#oV*0%fxMp7!q=>^N zFAd4^#du-|40?6@aP${E!!v3gAH#q1xyKrQ;nx=@+-I=cFs5%<;SEO>{;2kRe4?(w zcE(;1YC&|yCKx>~GMMBIXUM8`#`X*14Ikm2H5}h&-}jf!W)s%ym`V_Mh1zGDKgCHU5(2%h?Ja2Rrtl{WE)# z_r?NEvdUZ0YfzoRGdai{x6Plx*e!~=jJxV;}f@QV7$J*F!LlUszoJq)i;vHM+o~CRn!3%e5Q^^ z%Q#$9Yh(w8_U#(w5rOpHKa*hLOZ7Py zzjYQ=s$e_7ju~#(u?#QT9NZ_Fo1+|Efzn#M(<+f2Kuxkm8`ZI<8 zHfTjn9CKrK>vnrvPO|t&(NMR!c4hX+>Ouk64*t3BbG%>oRIhYp$FHx^;c^J~kP6X3 zByo{adFt+p!ut|(d(#BETzef!5Bs^a&&VWvHrMwPBppAYkmcuL&dC&g7f@Cfy<_=a zxUkT)0sHWRBN6YMuIHOy+419{vl*GzTa5fb=+b4M5qr(MqomWPua4kk^tq@$O>R`n zr`)cn#M3VMX^+c(e7izpj2%>P*OjBrW42}^WiMm(3yyWIrf#ptIiBF43cGK!*0cS8ghOlwAOG7ObU#)1D zvbpMM9Fb9;J|t()N_Yx~UdE%nDt%iW ziucMC!{0w(zp!s>QU-c0h)v3!lM6m#rNyEVaV5w^ySTW@FBdBe^~MZt>Pj^8HB$+) zV>qK>92|DP15-ny-G^@aibzJ)oYK$+1HcvV;_--(aG|z{nJ6#=osg&uSS*L9BCpmw z1jT`eezV0cp)L<{fL%iT4t9XK1H2LqwTzPcThj@1h*?ZVQJ!@}hA5%!Gr1Ui23w#R+G$=JyW(3K{c=lX& z!wqLaqNWY^+kq?QyZi2UL0hmkk7IFPge2$U1el`aU#fLSdVKP%<5T0SY|W3*2QD3c zfBEEqCsyh|c52Km=^ID==l`3#XP5qmyQkOF*2b@==p7Z=1nBq5XrAUSOlMcdmQ`kk zkeQBZnc!=p%)|nDUMVajC7y3x9%rP$imsLl3BX6T{%oZrhInWr2lqS=W-qtCSbxF1 zU?qko6dfT)BUDUKh<515xVWnL77M0)m0f2{uSuz*N=+&u1nmnImBAxhLxq@^6DMB} zPkL4KE%w_d9C318!D&=R^1NwE2cMVDbXDyicg6okwL3k_{|{I17+qPjsEu~Vwr$%+ z$L`ox$F@4QZQHi(WW`R$>e#&N-RIl;>~nvvvHsMkSv6C-IkCOJ%tK zAL4P>dr|19h#LUl!^hw}2T17O#Dhj8U3kYYC>y)OvR~J){N7{uO|!?!!$3pf333*& zqeV);TM!PbM+sA1al?P#(cpPged=JV`rS; zO?2o`yQfD50p#5J2k(-!%xDaYeeFx%LW6uZi?a6fQ0J4tL!*q9T%gq6(T6o3-OU0< zl!pxZEPRz1L$?cFZbYNR3kjlwBfuAsWERM>1Jt&W3zvm{E-bOI8IKG*L34($%8+7T zIo$T=`w`^F_$4y84;YH_<>%pG`~Dw3Bp*WH66cx;bxFwnSYPz#kd=U$ahKrioP_%4 zV@L)i0yv1yh?$8QS7s0vygOoFteUi6GTX5%4c5JI79(aZJt<0FIUs;t7-6FtO%UWZhy^snpA(*4L$Sp0s;XJ1kl5eG9Hq&j3Ez@=h+Xh{E?X^1IzdU98}&+hXx zJp4KpaDR(BcH5H5)3z`_*KWzE8@f^4TrIc0j&Qzx3j9$p zOX!R*&b9wusr$~5{elvlE1y2t1uRqo#hpX1Lp`R#q2uvK^lJmv&jvHA=Zf-x-t2PM zf@tT#p~jY@|39*!jB>UD9@th+Ta!775VFd7p|#8V)WnPkuZ&u-JBBk70{Pg17;IP4c! z&}FTVl>a@;;04?1a>C$HGFtygRk?_%R;b50-LD~{Q$@-cQGk)vY&tF)GNeT2pUF&^ zxQmyL)53?ZhQhwx>AAsYoa{>sh7hUzJ5M+Uk~O8=)}Yp6pnSjadC|~s!Yc?Mv&c1n z_xY_h12LUX`<7Of!LM19^^Ts-L9broLk?TYPoKnZ-s%O9!J0=fV)oI_Z;9quyzCjM zSpulh^@*ir(1ZB`;C%z;IaQ$wa7 zJXT?iLi44jPMYr+!7+W#O^7r`s*5luV`N}n^HBzQw?pHGgLK@J_FO;hZ~)IkHuG5K zkv}{w_sAgMot*Fm<@62TyhAf9Otz;^8xG1_dW2@(+VZ8%6k631r4r*xDx?H{RVJJ! zI+}RO$Iw+Zc#yzNrq>vLo$nk=A-p#pPv70jCjgy2@5*Hdd$>K+!Gc|;-xwjt_RNWq zUFBtk~uqkNOePp3Z2JS+0fY34hg@{;sxac;6r@X*7>J8JG z1CxsU#`kl1AFY4aR0p#{nx=)%hpbgzcwvx5R{rhSFt@xCS)h4jbpPUP+W%H47l&+P z&Lf1i4cW1{&75VX8tj(%5&3A7D1_>>*;SF`$J46%(K)5HO}7@D>K)7lzbIpDK zxX2ABu*+4YgY4*>*Q>+tSd>{SX+3P2Dcl~{{Wo`VgO0yFTL$4>1YQ7iA%bSctV)+c zRgA|9MV`Q}M)NL|VaH=dBAoK$nPHV`fMZAxd!?ipu>#C2K|}xzh(4duoxY|VyNfCC zo%Y>}1y30vYIp8~=SMf$wdrReEA}0e1X%}wC}kbj%!z8oOklZC+JiRE^x#9MeZpj9 ziIG#F=G_HCqvrz0U8J1$bYO24C$0TQ)e*Mx=kvmm{{yhG^E`)%98opX)QW)f>micn zPM?-$T98o-t{tLd<|vtl;&5iVnKC1675_DsGsVMUt)#AP?BKNgo6%?Qmog6jO<`Th zJlmBu8-28o^XFlW-)UjG{_f!<2k(dO7ja1Meh)wO$OQUOY-tnL1%-Z6wy7hm^wd#y zrd)lF?Kf@*IR8pGP#HYby*!V+7OFZp>O0VLkxUuc8aE?ZS1Dr^EKI?L)9l>#-E`48_MImRFn3^UhQ({_}oCfn0|IPb}H2T#8EpC|`%u56CPO|JwN;N0UR( zzm;Q6gk|-A+Jj3PS};soDO=F4MPVQX)$t#AEFdNw$5J`x`Hv1DvO=+a1aDVbgk&YK z?FR-Xy3!gd32vKoJ9k12xU+d=$T~4Gl2KXE&@eN9)C!J?@w$xfS$57mLidz+Ax>|P zOYni00vU>{qV&yQUkNaBUk5b(7C2(VK9tm^n9FdNMTy@%9~4%{S=i@87WT5OReura zpXKHS`L^cG9e8!jFl_HxGcxERA`w&c=_2-2<BD{aZ=-EAOZ+MW z=JCDBKI*Cl`5x%BVD{>?o3>m%OO6o6TBWa9>r6h%N{Cg1I2*WtV<8P(~PZMCLEXUYm1k_Jn85K?Hn6R_6M&1 z!qhd}E&q?Gv$2YH|c=Ttn+RI0t-p(*{j+EFq>W#6dO)Yo=1 za}i+XfKm$ddXQ<7pt6*)-Q)Sabt8;$F{EctpGWF!?}g5 z5t8&G95v}=n|M|#b5W-drNQ8|0k`2{#ew-?omW|f-pp0A zS)8iA&%@niro|KfjOM4Cy7VsHTYxRE(k}JJfe?L|$~#=Uu{S{Z?Bv8*@o4|7s>HN> zM*ly+yfRNmnl{ACdC}!F#<=(^ka5szX@5368+uyCvY^}NO`v@i4u0~C-u&I+(hH2E z<8e8n3am{nm<}~LeSdLud*CRi8%vQ9^vPFFnU8TBUK1dep}87-@VNW?c4t^y^bZg5 zOMj6wCm?$pmULL?Zvpy41w;icdB@Xi|Tw!{1A z>bQ~|N5W5f3D8;m9-pjL@+!y3yD?dep!=qSvX!_Lcx31YuX(fuSkZs8A>o~WOAS+G zqZ@X`pB7|OnCncq=&ZOdxuI_EPuhs=?j>A=+k-VsIGS4E(8-%kjBaDMzWW3K>dwfLUuM14wVOv%oXt)CN!&zeg0UeN{k3{ zG~a4E$~nk(xkD2119_Sg&%#3Pz*}<0!vyaJM_q_^dvLCqz%#V|SLZEukPDtRH7g>P zXI~sP8XnDC4`PG}mE=4W7&m1+?Jp`?VB3UH+aNVOnEh)p7Mi>Ws&|x$MoLugR7Nvn zhY4Pe!!>@%!ix>d-48ok{FsTGtwT-ypeNUt*nvZwruQ1n0Ax(OulG@(QLq>Sn-5YC zwAA9$W5VQ`C{r@*2dD7okCH#%(|L^jO)!(j{bt^j(|uJ-r?e=3%zNUSo=EUq)(;A^ zW-tk3D;;7MpL&SLUM!fiF?<)4$|Pz{MjI=B6O+H#p`T=Gl`}~L5+AEexsK)^<6yLYN0h;KN>#9XqimR- zS7Bcj!$&gr0!#wv+3*n0QSU)t@@5lmm*w%jQ6>|5Zy7RibToDaEs)2S- zl>LhH`g~ky@O=@t@S4_Qvr^wACG+%yqfueRxErSqTc?KSb@8FnO|N3ei@a zC;Pgf8g%jFBp1KQAb)ju|LeAx8+PE){Y(f zkv{G8XpCTRPRt5m>hu7|TF~d2c3UoT`%?&CuAL9dw(Z^nnAinX6*^W=q4^$ItcAJs zM4y64sYjCgvZp`EY1gJhvB?GhhrQu{Xan1%`@l|<7l0VR<(%`tDityn_?o@LXS2)3 zql<*Wx}JYDVK?}`xmPCj*I^20Z6_rMeuF3i;z@a`cTsQ+f5poI;NWYam8}( z#qF5B?=vQ=Xx?ZCL-j4dN0%{;M~0!IT~5HsyK|GhdLXvWO?x%^FyY^Rp~o^Otev z-H3GXCsAQ=^(;To$0s|8s4`9yzu##m<7g=xV)CVo9VO}njFMs%Tb57#5X=q6{>6l` zl@Q(4{U|Yfi+!72FXp&(uDb4bB;Tt@u`@>K7ryb{%HVKcJ?Em8+Xm|M{h{vC%ExCb zQBD(Z_OOB|l3U|8yp*!aSq!Kdy>9}4;*aqif7tr-S|i=8Kp^Xdt?~Wa_x(=|`Z4yC zw@QllUPt#WOF0e}e)>zVyXagMNt^kPlVi-Wru0%1;YYFCcL)vRB26^|6L{SswyjxL z!X0tc8@NG;+V8`fc!&7HMCRUsf4+eXDGPkwUJ20Lp`rrB;xVH*GJZ^04`mHUfBsWzQMI;YRH5&^wYcl)eO#H=`&{7x=Y&D%Wf)t&{uoid`PcyL z!8mA7WwN^wYjnMnSN7+Qw947GRa-II-eb&V74$P|)YSW6JE9XXy{z2?+86v@uOS+( z{~8}{FQqlMS$4Pk%x}$!kgQl(MqKbQLBUaX({{o&mU~XmGza?_hE38}u}LIPeYeZe z?6Elg%Vy<*VA!%2HFng{^TOp39K4-HGB-`n3Ur@k(B(=@b&DQbb3NnloK8R0-yR+w zYx3l+h4=2VkkK?^9_76R6avq`=fFHNX46NPY3_7}Y!@Q|ydK>BGh)~~%qPQ{0-mWY zTE!193#O}z2JA)*{6nWD;0}u6%L%td@MT_5g*>=F{`>s8n|(L$y`rRyw`2CRC&MT2 zOpOG7^7jgUm z?g*37!^Ydlv~URgA1=>k}`0NEV`Acmh`ywx@Lig83o zz}ETLZH%+yz0wun5jhPRV>Gi@JAK*83oKJWNlJxLf3061&>JVbDTIauTd}X8I>vEpS zT}m8#w*|a7J_;gsUuH0fzrj&IFaIY{ELaXiBnSF_Gw^k;Eozkq=)DWXU}X5Swio&s z+#x}Fq3+5l=!OqIA4NMCM;PEyGRAoABT1nZ@m}|Xx4x#I(Nt3urFGWC6e^}Pr;|CF zyhZ^=K8LNOuv-GOv&TqMw6G%JRV;!-PA!1x=-;@6iLJrxId859Bb2 zZ?t2Tf(6y@7{UyWG{M4PGzJ(U364277EgMU%`JU(&q{}{d?3mckJCxpDgBE{ zkY%?99u^>1#*S+~j5WSX5WxeAqx-n0Nttr(gNjNW5J-=3OZ2~4*#mw+$e=!s z{K?o}C-BfByeq>;5$4fGRz`3ETMLLlAGb{#&4|kT^ zQRO~gTkhbaBf+ozLbAQn-@{73hbzBhgo3sq-y=hgtAaSbaX(c0U`Je znC$h90U8ATr|0juNHzEQXd%xLD}haq4%n4CIKn^JaG}MTgTzDz>riChP!`7%V4j~v zk-|DjR0D(<4F4Em&TxRO7NR2`Ng9cpp~DrE4pZyiThk{<&ufvzhjE`42o#mFC$5S7 z$8HhM_P;4z3>TgD7wC=;hsG%6d>?gDg7L}p^dP+LE~kZu6G&9A>s$(#$3Wbf66Zw( z3AGH?PY2_Im?}U}4{pK1Fa=OR%^U zfuC4X4jw;#gEU_reeWa;t27Ao)0m{E3?oAU6wQK8rSXF5i82z9$2bG9?X$roKqTQr zX`QlgK+V8%@&diFqGRX-Z(;u9Qpr;QwZO)l6t!c$@0n}-GZ}Y-N|^&cKs@bK<_{!R zW(@Oxs8xI)5FvyH`LV{dgH;NvRMiZ0t({nL)u$apJ)S^uo;K&=6cP5E&VVTG3X+i^ z8gYRqk1@Zag|sLH5FZnwTxbr#)KbE!kHjI%B|2x#N=nqLVDN!J4$OA@aXrQRam2<3 zbyrCmd)UW{9R_Oy)8YPqi~)*8Uw~k+To}ciGFHepuh4b`UtJFnyixR-RbgHjy5s`L z0klNJ3Zi%-|H6PDyA6q}u-Z4+gGiF3M91_r5QjME--Ong$03g-w7`=~0WB2LBO0Z< zNC(sjEzMD-IPP&0NaNYK5*?R0VQ%2kpAZB%e~AHwZ5{EjOZokit?8v+z9_BCy* z&b^#|p=>rbaf7fRjf{Y*oc#{>IC>R6{?LA@b~;mDP&! zE3t3ns0wy>l{0dcGcI{cLFe9q>HdXpsBjM740b9`O`^Q?dxKp9w%Y4%aBRn@u8(i7 zT}~RmNO*H=Zxn$7>5OI(`DaK{bGq!E(%y58&Zd?Y={M#x4wBH1&!UeyhF|=s4a2?) zPxCVMeh%L15Ay_FvkyMb3>$!5a)LA^VWQt8H;LWXQumOh4p4{peadhp$Z?oC?J2=T ziPX^Wrza~;#ns3EYA)!km~o!xL?SZ?EKo)Egu$qrQx=gJrM0OZE$U_k%<Nm%d|WtPWe;)y7WFBH2b!v5ax8 zWuez`)f5Mzu{1?O_o!o5`Y9W|)l+DQspL{mPL|C!5WEv10u2 zEA`4`>AI3xv{S9U%zq>Ci~S6n1z?8;qt+{oa^SM)vMSrBISooMuv!O=%i^mdZ7${JBBYpUBN~+Y-tkGg? zhecMc-BPPZWnqEc0Op52D^nQ!jYmCIYHnnC`zJB&WrxJFD%()C-QYf=!INyf+)k&a zIU0CyfW;R4Z~`-}9LvwA&WLJ;XptAcyBDj21!V!hbqitLen+p5Y^8@em>15@;Am&O z^5o+uLqv1CU2o+z7^%07w*y^n3SKwPkp}kUt->}sod!`1Gxs)kjYJKVDm@a5Mt6OW zqH+FVFC!1e&PTQJmp`q>fAUJ3dt_6}1P(HjiD@IO>Mpe5_W(b)>8{4&;DS6D)sony zrg@UUr!LplgWwS+>~o1S9a1k8NkvPo_1)B~7%ZkZt-=?*AR_7>>*cGxS}fbGMYKlv zPUPZ(&pkJgtK8-YlWW^6RtIT?&EV^Ogtz*SgZ*4}?%bXtZDVs1D5LT5mAaW>>ywp! znysjWN0!n=^+-&!4>y<)T>Dw>vf@`XHtJmh-q;jiCTWW7!<&gp={?3i?e$V9TyQZB zR*KI}vi(2kn-Jt|c{F&Q z6MQ@l(N4xjPH{pl-{dqRLf8y$kGYXo`JiZ~nxuC(32~^kj)%ao_7GDdW5B6!zSW(F|IFI*quk&`~9Td>%)1X;+Y^loTHtS?k2_`SNMUT~?@{MiJ2 zbnq$t@-U5&IGd3;G~{f20G?e4)I1TNZ8L+p390L)L5l zrX_+$iI`YuIMN_bMv%`MUmc8e9~TvCP99Ma;*lSt6Xhh2b<*!3O-V8MT&iOe6n8$q zJ4zg(q}4c^q}FSevk1!DuCijtnaXp=6+>BZHf2{IIEkuSn=$G@?_nFe)?nQF!tQFn z)>XZ3J%YfHN#mA2I+4p7E6}~b7qv7@NaMAP#;<9yEU0HoK=qRSGP{$M4sx-3I|)=6 zeeVCEKl<8eTdNvCU=Y-QE7L^j2!t)8*z~n?3wXD@ct>{XuDmWaKTHEJA0k@17-;xx z1}~3*%s_8D!aa(S-bmi*?2J5!5xe_w{^RZ8v*&QPfWb*0gWb)>jd)+e| zNx$rOgqfD0yJ3`em_#sO1J$26D>B4yS^3xJ2+-}iZ@yu_Q1C1Jty?#t>zHb|QzF3% z;RUPNy77tr@@*IPp&Q{G`=YYIj;i3V54e{|V0Y%%y_#4(bGHcJ3jZ3|`GGJxI)i->_I{I!s zCn4Rq;Td0Hn)UE7Q&#E$8TH|B_-jh81MUx+jb^{X?G3c8HF4Q(j!tt86OTae)E^Nn z`8!A#6{aLP;j3?$3J%xYgRKrL#BW`WsSRVdd#7iZm{)F|t2>yN1n7_2=r{5!;vWL; z57AFvXb*G`R!7+2vnG(Kxiww0)}DJ7!1#8UyUZOsR&Cl`Rs}w7)11xE0FSemR9O$z zYLCZZpiqt-NI9*0Hr^%s9KCegrB%F&-|bmJ%1wWY92JL%936sQrLBWXOy!IW9xb)T zq<4pIv%5@Jm-M{wD}QmsxNUPjgw|_M#=G9V&Ld8rueJxTaN67SAuuoz47`R0V*FV3 z=p(a`2?7;SWsrUyC;A$u3yxDZi+F+@_f>C(aeRfl``yDr+07Fl?1=W$XA08n9KHBu zFDqlm6Tkk{asQsnDzf-!l9bkuMy6-lhicR!^`0G;_F%5XdnrkMn+hM_2C!T&`6Lr9 z-Dxk!;b3}WP^Oq;ah;sP9LJb%OB~NKLvVKb&_W~xd(19&EchxfW1zPe|UyRbU+D~l~L0!*Q>(zRhbKUz@?OR0Tzju%f_kMb>%ZAZK zvq0!m1H6uvbD}|;B0@j)yE}%8wD}OfXE)$7Q19m!iFV6HimqixQ?b~27R}_@as?4) znC(Wl7)=LHQQ^65`iZ*h`|*LR08MDjL(Rr;25Dcd5#u zUOWN^0D%1sJ)+TStvorM@%65Ifs3MM`>SWjzKZupPGe{;Qv=@~ZUXD^#GKoq?3LH_ zy4b6=8G6!0{9Z5NbvB%ppUu3Zrt0`&I1#t0!Ppiv%?u@5g9G` z6)sK0)gQO+oSLy)h!=+7Wb{{fO?~QW+4_L*iV>))H?>UnsnsMDq^lf;(Sekla zH3rUIJEw+z`qzs=pK4-V3oD`@0${WovxLA+h5E4oKAwKPqlF^(JF_2=px9?1=ONyw zy$R#BYf|3CaEX4cAbI}st?E&{thi}qx+*KWdmM^%Tj4xpJi(Omtc7v21o0p)T@sT% zztERzARtVZcwR<`!gghp;TL)-)O*WSR&~E07#n;}H4ke@huWI9 zTDZ#R8CW(a#jj1S+RjwkQ6ps`yE5I|;mvD;-0bi~GU&G}@;|0|FFArE`4+2&;)1JF zSbG$DxeS6@p3T5`uy)_ADT(4=<1Agl;J49MueCTI(5JN0T`B z-9?z*S`>(^)0DVd)$k2AZylXs&03{hkyhu^SN*%vUDZ;jC zcwX;$BGY>l*FOPGpwsVd75(h!86OwSE6Y*iZosKnBMA8I7sBXBvbmWm%oyPgZouOoij{Rs*&z~>5N)*vY%pD*|3R-!wTRACj*!9nUm ziB3s?b;~HhyB=v?7~j=kR~b2F)~|N3F6JpqF6udij6x3DN8R?G2DjBm68EAN;7yJAm=J+uJr9r>3z?BX|M*;s8oK+(^tH^00fl z!aYitBb_S|bR-yBMk16^+*@8Bh#gD}l&e;n+w7QEpKD9}M=!KTx)-idi+e&&7`(_B zbE`B`%EqEOoEJ81%E=un316|C!+D-!=O}emwnb`eGWsIW*+F@F=0ZYN|6Wa}T>8)# z1EL`nYirEH19W8B0EQs?a%I>&wE!?gpA`dNvgoNzUa8=vUBlU_+GZ8}3bgVTt)3(a zOm>C$1^0G=K+$@+j`v!$9}-r+^gQ=9Cf$6R%B0#Wn8Yoj1iozpWP1vTcR;xj;I-Sk( zF~W`Gw!8XvJD!A=?kUxI@w~)By|uD?s!v)xd#RXyOPP}rg4)KK96P}pTAPHcER50k z)ruz2r1kkCzfGJp`nKJp?(2O_!|i*Bc{uunDP;k??n2~*g+0EwjOXy{!?dc6ExSC8 z(Ou{;UvAUT=a#y=iyvlQubs19wf4OEP}fi;qPu-I3dm5Gr;aziR)sXxLVIP0^2zkS z)!AL_|vNya#(l$@gKhdWS4E$KrTB!Q7t=yWG*U$B!mClA%faMSyU~}~a2O$94 zE#}oz)*^8j>G}2kMu2!8LfP7<2P&HCr+GPG2^Tzhxm-7uVKlz~8)rSN%-fix4#96m zfcA@i?~MfcN?PR&qWcO<#%)LEy}6A-clmF<-&imkH^x@iw2`a9q^F08IqWBebplM! zvVGw8Q{2@%c5^=l{*#V=i+qa5PGbc8(;lu$@VpDk4L@&8Z#PMgjDt*lyy6oF_g*@b z`Wn3#ftN<^XaSIszEN&1)UR9-OXloRryIija(e|B`szPZO0C+d9`HsB2K?|NB=L=H zQY4;Lk_`MhnYYwfsE;TLS&o-DRSFHss$OA6xR>3Rq@tEE8q3*5hKgG$4_1OEywg;Y z+z!>GpgFf{bl4OfK1(WPGP|LUQeG@9nMvx`f8Eo&JAkb{yLl#jOdFl2{Cuv_y4yH* zGv3^rG@Eo{Oy9#_DtTVRlyxvoUQI^!spT@e`( zXJw;ep^dWF2+vb=>zUoVB=F3kV2s@fwGfiEdP^fu`M3l^YHG-8?RYK~$169wyuRtS zdzksh#7?oL6<2yVF)>tm+N5+t3U^~+u7uohmG^T!D>G48369c{f`q+NBmL6NPNPrlJ?6qDk&6!iX69QnFAZjWZLVUh6MMoa{8sIy` zu3vkn9IW3I`0_tQ+~jx381j5L2=?>gNvx0J*FOo8mo8o-<1UJA78-0o@&zhJkZuqp zGoO9H4jzWBC7F(Lm%1DKg)2y#_4q)DM4pFDVMe=*IhB}-H4&fb3U3?kJoRaGM7uol zBigj2B6YR)A5%AeGj0dnd1-s%ztHxjuz{*(x>UBF>Uhe&-$pfq9*o-85b2f4H)SS% z-xe1{HI#mf0oAFq=;c<)Irm9?*}&4%OB*rp?%J{V{DeP#E9lKM>{SiyS}^QI4E!K) zuy&&|hvK-0dpBn>3o(y!`L`C}W=H)K#2908`UoJctkkph-r`v5@xFE}cls!pw!^P) zJn?ni;8F65-uSn;`>37&?S8enwZH49U*F<|*TQ0#?_5_DbEFr}4s$J=Vb@+}sN7b* zF;?v}!w7AUm7bL?b8YWkj>pIKn;Buuhs65*FRt~N`iQ?~U-KLb_ElSCe5!XiAtV7F z0j>$*fQPzsYz&DQ_5&`l8G{z+PY$23h5DGJLc%vO(yTs)Upk%LOr2F;a0~Wxd|}QL z*%zlu?SXb|W3#@_sZ01zn{-cPvI(YX;GQN=O7(a7`!EP$7Z=u}z7;2XQaHcXPjX$I z73#goO>1;Av}wfLHi=U8IRwnk5NR$)aJ0jH?p+aPJMHPb5eZ) zpn--i;XM*VsM)hD0t|YFJdh~_QENRUa<&t`z{{dO<;UbTT%Js9b)C2(C-(1q{_rdh zX9|IZ(Ld9X;|edJ?xA8Az4iD8!oxyAhDhPN`;z|r6gg7e@(eDXi(OcwsS~PLLiUa& zXp5J8!uQBOqOYu>+*dXf2iQv4mTfI#nY+W3NHMq;(g=O_J+RGZor%LZX@HE4#8Wv& z6*9zvlJoOx_{0arn;@T-<|P%?Lz=|1K<>C3BQ=E0Dk!JxaGJ(UF<2xGXj8sD2zsr| z;7H**Wvf82*$#rmF`>TIVc3|r*px1==SU?<5$Jyhq9QLkN4Khp z$8h68cZ@Zo8NZA(X+qL%2G%$vFN+RYEpg?82fx0_h&cXC<$A#=x3x$riIIUw#!nFf zn48$>>RwgJMQLX4h3(blIj7@+gpcQ@eniu=xZLg*X5(ogd=Kq=@$cjQ$`%kbz;UHr zT{utVXg&~5yk$)gl9_L!TxI+n!tKZ?16@sbn4T)-eBewVvw*bE<;dIu&k80ioQ%mr zA0LI%ShEzJR{+MupWSWLnMd!?bgDAr=ZjaX#W0?)HmMe(Y5)RzCj8s*R+K}7aw-oT zT6PW%Mxt}*Cb4*DX!``NG&zGT(R_-l8c%+ecozvunCOKi{p@oU=KH7^FW8Je*v#Ln z`r7G<6t^xWXu1=}`=!88eTJ(%f{uV^jtB z1|c5)!gH+bku!5}I%XBiGIT%ywTuxob7iNdZ5r&a3o07e$_=ep?M#1y^ zWuy42mAnh{pqh8WjBtLxt*cxH_JjUqDJ0-?7&O1zr-EkWdw zh4J-caJJNNHtcxxh3EAcK7lLaE9sgUl{L;P=k~Q&3g2td&Rj-&FN|)2tNt8TQr~(M z)6x{5y9_0!(XtGYrfHvVRaEyp^dMn~d7yd4I_}&ygT8lt1BaNsspxXq%;MLtxR6%} zi($IDLb>Ig#WNcsJ~npHqK??h_=bqr?>7ts|8H*oHPAk6;$0_d8fYY8!4qYX+!N}n zTbO&PylxtoM(QFnojBweix22x#)I=c)pUF*B9BZ*UY(Ci6IT)KGYKnV2#Nssb(nrG zXl^a^=3Am9V3MNHOy)1+$%ToZDFu3sRGeU`-b?TaiRt9}Sxjb7fO!%ZTo%*yCJPpu z|IjDrxsG=@om?)?k8_Kn+%!%-e9A}FcNm`t$Y8Tci=}lhXyadvS=nS%LvP-{lhDC;~UmN z=$m+?7DHoPq!hfYjCXJ)1A|85&{xsTbj3vS;Qwq#pu>{WlZpTUN8`)<>wxg#^SmYNtKXn!=4a2F~-e!y5ArlSY5=xSQk zed~4Ic3A|MTD|^^tD>e!m=L$lKH#;^zpuSZl{r-PXsE2wvh9U|ZXT^ex+7w+DDXbs z;rLOe)bj*Hm1Cr>=iPQt(&Mwjn(L`+Fd+63CQ^(&K?GUVcF_TWt36q_5Xk?^|8XBOqL1GhTbjg(h zrq2UBX0tx$d_#10* zjCRGc$k?Nv1^LlrEqDqHSQ2l7DO)T)B3i4|jjNi7mGI+7ISW6p`ucfR2uF~IK?N)B z-kp{xvlOf`!jeD6Z7zinM^5JdfP?z)y1vFN2D1BDRO2aGxoFbeQwd$sJBg>DoR;CH zH~itsy~O02)Jm|o?p$DzAzt)CEp2(Ap&vv(2mT z*ZlV^Kth3qXL5vMaB|f|R}1J=3(vxm%5kSiuO+6EBL(vL$Of3$q+}|l*2ZFM2w)r@ zL5ZPoqHycj;M4UN(wG2DI~1{N2FE^h+J*_U6NGgBJ}zr%(*fkLOY(mvNWZt<$LoOw zAh3uy?2AKMTLJ^z9DGxB9E1*@{kIw_%1~(3qySMIn%;j6V7tgC)G-u~`^#L1i)@a5jB2!*`f-oKaRx&(B6o9->!xelGW zH)!AiCz+VGqWHb!kw+rra8caF>7~s}e~8X>8~*bi#{>BiLxMu#RMQ;K65K>=k{%>; z7Z?R;hB>M~1;v%2D1DFCiEdAiHS#PN{cmoITQq(S?FCE(sH)o8(EEdQ2YU=`7XvW@_1d^4K|dh`qKbq0H1y=$TxxYF~>cpi;awvS620=l~hh&6q1I6bb)m^VW3qZbFS*gz{EO*yEFA-x+vDoYQpAEPf z;Dwnu=^BciRh(jyKk_4!iA{?ER4X4fNt@~PMbc+9D`VNoCuzEG;O(Xz%`6zEv&jY4 znOO-rl=^S7e!TgqoUW%y>b|V{DYf;;QfG&^({|fL<0}kF9SKkK$yxOJ3|dFj z(@6=na-(hiE9`ih+w(Lp;Ag1`teUU08##_2AO{lpjg$zIjo`z)v8i_N$~Bvc4YC;4 zuQ{^8A1mDd^-UKTfN~E)z_h3NS^iyTLYzp-%Z>UVafE4cY-{WxDU+FSQV5Sdes7+L z%ZThL+l-8zv>etLTI&8J&0kvbQ}VSe!WbKQC{0Xqu)-atv+K7_!8x|HA$3udX_U%< zG{8B$3kpX^v&>bI^e-CA%R%DdgM-w{7=+3C93=qJSw>1bGYXqkx`Vfasope+jPi-~ z_@%(7M(XzA$6|4vf(aed{&I1{2HiiGiJvDxH9K`+N~_ObnDS&aYJJDa-!(_rJ9J4k z8@J@l7E~^xE6^g`sl!79F3?V*1 z%<@7`HsN6Rw}@OALv5!=6{Wd5BWC+EAW<^rS6&pF!(Wg^uH z2QEa8>;2^sLSk#Gc(ik*j-;qe@5n<}N$X%f!EK(+rcnww7SxM3(b8Pi* z;oW1FwnO8r>H{cc=ygXkU>XS2lYtF9pGfL6o-OP{=)N;$^VAeMMw7l?oy}g&_H>0y z$KfRR`m5yn6v~j9K|VawG99vJI;chkeE9tG%l~^0^B0r>TXclHbdE?2bExA@i`k?; z8T)QrW0!Df8`oM??`R~qY)T;n0_%~BT9`P)`sqR#y=z{Kx4x6BwVKm3b}D1KN` zvsFoC<7vx$9bPt=O)iFwR0ykmGlo2UYV;<&6v-^L5y3P99_!}t{|@G#dqw!Sa6@(VRFtknN(G}mNu!Il-2m7{!q&qa;aC|gK-yS+sEI$^ zf<~zdsyFTG;nYn;hIjyk;7dk~`TaBI%2Z1T{~Q|vI+?!m%F2KwN%__s-2!Gl9%a*< zW7>(>KxK@ymowonj0n(X*0i!8%rO#0q%TFOFH$&Oo_X-1@wXRK%PDp2&iy}ZqIh}# z`$)}Ii$wH?gpkK!FUb449}=*(Rt_1y57ew44!|-dMKW|qj>Hl&8jr>-aWx|aP{t4X zFMH^Yrf|Jh<%#({YKjykvP_fq*D(m7M5E#r@vx{>fiJ!uZZG^zkMYmD zw7lH>JdcC26lS@3u~AB)#1qaS(M1Gy^o%$Tpl5br>`xgfRg6L+>1!OvsZWLSY*k!+ z@mmAv~xVl z+O&*Ek_nQu-HP=2%7w~>!+$wZmN3J6P;}lUXu9TxRTHbVE6eqIs1IG>!(X?)MY%WU zFghr5x8y4GKH?0`ex(UHH;S3)t_p4^B$2Z@&qaHP6>}SK;XoS5%v5a7x*>-x&MlgC zrmqi&mL%WwmC>45Mzd4>uVo+m^FG06;&7;SE&})pUh-KyZDKnVv8_Dfum!^%LTt3C zxJhv$qniPYE4z|`e(gp7vY>dw8`D)W^n_5z=Jp)~`6J!;>EHg%+Js`LbK@!&zet_~ z#^Z@qbE;^-u;H}<0Zu9L?VYW(RL7fVPdL0l_;p}O1^H&3jbjkrhM*hCJiU%y;J0hRU9|F z#9>@~khHkeS=jErKn|w_AcJ{s#KVKmkcH!@-L3BzyIvI4o6n!Eh5L47DSI3 z$@?xRLWd)i6!uE^0!Du!R(XWnq21&1t?MHm1mS+gT#5|xu@OLo{)Hr<*;t+x!WJi_ z3|T@j-9+RN?Ry&&cH&*ONq#WdY)UzxFewa&WcFOLC^rX_ts9wOKlVQ^@fgA9?A)Mq z4)yqGBYx%Wampax?2<538}2ayVA$=RWI)7<074#bHPYu8A)8LQpN}T&32NrSwah|f zm}E~Eq6ZU}ect!GLyyh!fNsCztnCxTF~ua};vh{*KBMQ;$>jSYcin;r4e~uy*4!Q2 z92|6?_1GlO5{A^{3Vw2_YhoKw1%9ch&JC|EsUU*Nxg4`k5KZ6+vx^`MEtVZk?(^5n zC$*bBkNuSeTB1jKM-a8^hpdis07+Hj9(1T2XLnJV%)x0IJ4_gb-o+orLOn_9k30oepoj6td-kZHH-r#6vplh|Tkewr#&m_c9{~)U z0}t4(E9v16_2ytuzQvRLB+zo(%u5_y|I7xL##O}}E17su47!d7! znu{bBZ#hdc$G)kPz@6ol)F-l(Pl0k;}!%geR95wqC$0U5&qj~8whN8 zh@Gfp*xjr!z+$YR`pFBOkQ6d;{Ahy>yznyqxoxAPB6o?z5f$9BX_8H#|gq0$`#v$u-xE_H8Ge7QTq& z+w&)X=h%HFmsK&5b_5y3w#I_rIbmsHgi&B9aRkvAY9sRhz%^oTMR1UDT+Bl#!<~hY zKT>8#kbL{bVaGvLG!X9CE=%nnar0xNE)DpMsI;RA^~$zqtkY2NZBlA8!u086D7?Ki za(nT=&%+-JBj@Lw!QhEHsUECGET=j_XIeINv>ufn%yK%WBg|mWDxU~EUk~Es%&Lj# zS%=fpJbZP%PW3lo665zGF>j6_I`Lv83Fyk@)M1=z?7qVb;nk2-Fc|lIJ{a31l#ZO` z=zHo|FYW|?g*i&KU4)H9s6ny}(c(>mg&$E`#${-ajy(p(E ztZ5GZXM9?PeLPDGI*Xc{q+z1sIYN8mgpdZ~@k2>dkbjATI3e&a)p3!;5cs+4!c&kW znp}ii&uXDC!ZfX+;6E2*`bFSvpks*r;^DxA5KZ6GmCU_b@(0Ix8Th<_J}OLj5UrW} zIHLhu)LE@4%V?OO>&Is9$`c}n>$$bq!JFQkZV#QV39gZ6?4HPEK%lh$;myAG zzCP~#V-UqM`;Y#g8raVuf9yo;$iONQ)8{H*5;r0_NzHvhqhefGHr`bP!l#k&vqg8P zgMp);F|VkX=Y`@<8#K%ED1$=VgQeoV*qEPwLnBE`-)(tbh2WZ2e9qLrlYiFg_kyVP zeoZKALovXO6T{&s3j70;^T{EKFN>=s^VFK!>0Dr_5_9DAc&-qvz?yw&1|JTN?dUkK zN!hC5OD<4|3w$Y$ZWQT0Ai%X*!h@^mcb{X!oaAM*zM1L!bvu;*Y)zZ7KhSyF@^Mz% zveh+8%Uv_eeUU7n5UghMm!^m@#M$Cjzi>AS{q*Ah>m-!#I(-Ld*QepyDw z9e1EN*)3P3{#cRe^CU@8zjNLpS=A}X81Fpa_!Nc%E3jcsRjfBN=T!;rdeyQb3FUg~ zCT;NCSY$KP%T1QHv4D;!--5Xz7P^tq6*RWT=~SLsbC%Y(%X(6U@RhAjivInFm&8yJom(NeDQ#Qz^kWA-qCbKJ&tiIT@ z-}7F!^}f&Fedy7GtP@^Vq?mHf;kzoAZs%$koZo=1*_Y#KR73c;Qtd~OgM%Y+v@)H| z;QKRm!+Vc4MsFF5)#n@9gY5?8Qj+hpznt~YU+qu+Ve%EX_p>ewPO)VuRXT^zX1J6! zcXnwzJP*0{;d_TYrWCvDRQ_eQ8-=kv!xpZaL~@27>xtw^`(izl8Y$b`+(vO+-l*Cs zbE%GYgCn%VWA{%D1}^8uJ$ra!PV#v>rPPU5d4TGNZ$F+WdCD#Vj^Fw17!}*NUe}Hp zUH&x`|9-^5##D>Hi@Y|?RNE4L~S2u9hU;Z_aixLsE#-VPPs{%jO`!nlCa`; zFH1Vz%1x70X_txU4Sv*@DC#V8XvNt}w_22P3QT3S?DuC5{UFv2GJ zgf*_saclmJF3lsRHo&!&{J_gCIlCQL5?8;JG8%5i0oK}*0g`jdW?GOXCV%7MkC5~1 zE8Vf)(gDLKc@WiA&C)EEE<#rdazLY^6P$p6T*JMu!qeBz;={kq$=6kAnxN;mLW=iw zt>DY{@yGGQB9F_}<%l6(IKgf9|MAZK5SPCrwi+cI2Ww;MIz_%bzl!4SH0|rP^*W+nC);eF((sWQm>+AQgWO)+n4edzUwgo&tud&E$9yh0RSkw#ms8}IQh znbiBIXH6PLNKfT2zQ5rXP+YJrR>svKyOU~B$ois5w@~B$k$)gD2H#P};a|{|nfJ@m z+k1gCD63O%_1^w2UF#n_yx8Q+v7uIaa7Eik2HA>ZbZN=KGu-LAHZRwTO-a_e7Ow{M z1iEMq7HQqZ`ZIn6&VhZta4dM$H>IyZBJ93IdkCWHQ_ac3h(Cwm+n4~l1DzRHGqL%x z@xQUPJ^NpF9K5jIy*w3)n6Dzdh9H7MY%h%0Sl>;gyp7 zmtOAtD|q)2MmCDK%HUhe5mRfw}b*(FPtPmbbmHHSG@#d%pb=Bi=kO7K)8OR^;NHl1nPQ zBd{nep%#lgyuqanxR(BAS1YuERE+Rd>y7*KoVu$kNq>ojo0ntmSl`r&n${GWq3b9JiQ*LrTjQhjbV_PX#mU z`ae9j`gn*t^2dkCMGU;$K%K?ANE;gYA?kFim8cR!b!)x(b7ua2ik9PSXOJtSwAIWP z=(5=HMcd^KJ*%ukshZ=MULHJJNkdlx7x0{W8{n^n9o1{LV+rPrP1$Zyv4J zH=Jk{WnGP%UYhF+P1zmPqI+8d`=+KLuSmnQYS>dSgx3R`ET$l1&l@ZLH-YDSWK z$1$$`+xlL1e$hL37>~5NUXSdJrj3rPymwoYyDz=mIgclk4mG3{WV)_#!L2D@u7n#u z<7u-j>$og(sa}UI*13PEgCx#NkFG&p+j-^`d7etw?BDtu+X&Va*?26s-tFntoR1P2 zzyCJai)_#w@omtQ-sU_{$?rK&%O|qRKRkHe+n9vEaXDUvALYlksl#Q3(u_(IEW>G& zU^Mr_%&Ebd{n&b`|nTqQ>2DYu?Z^NvD&X_^(MYuBH z3{ewBh?h+rLpAn*IYuhyT9M*Vcy+8TUuK0Dq6xZ5HC`;_T{5vuqLHHov!c07Xzl45 zA&M5x)TCnrHl=H4pAOh@XS?TBd##y^GC$z09nM)tqm^HxCOMN_RW^+#D?5}FAe*V} zTEcWrHX8v^2e_L4DDfVI(*nMu95$yqM9PpA0<Wzb|+r- z2*XAwmgXD{4#`SsQfYc6*_@*gaj~+0VA^fB%Q2Xj>9eI;#LlG@v88?r>)!w1_kF)z@s!pX0m6m&1XklQGH=11@;OsyR{0 zoT-74+e&$=8pa_G?byLSf{or~y_kcqBAG(o4B-^27$h6Q(&(WW?D*aTqeGVx{l7zq zbP0FIe1T5EH5r%AzWL-S0qZQEY7t#qiKQFtR60ZgGM1c&Y3J|}bE)0E>T`2D6B)x?;I_9PI)$M-0lTp39EQY zqK-n*QzH$Mk0LwWVc!JA^}89YHvY^x=hI%iRrx@p`><_lJR@_0=%}pOtPTpF@KLW0rD=Ub>(zv?b#Izh9i365$BFIrGPvSa2~f^qrK4go)Mm2^ zRS%3T=VtY*v~;DJZ=SI3(JwDwDnhSj10^roN0MH$s3&-F{TO}@OQsV45AN*`1rf)T zBfWm8`{wdiY0}-%Q7{XsJ6b@>?iu=mkgNB>GIxDL##YhhBaUiNz5`Wh84`k_U0Q$N zRmVK$K7Jz&N!283hw$KsdZvlQFj_yJe1qyPIg?w@;X8(Od2TfjpB~?6&uZD1($sk@ z#tuT!^Vom~h0YfYM}xGf(F#0h?l>8L`MwecJzJd?^g-~#K*tbdm#7^g7s^dRgfiI8 zKx2f$tAGA}Mfv1D>Vk^QK(#0D1OZP}# zx=Z|;)h1KpdEBy|v=zpn{&SyK@~ggr0&jTq2yzoWiXr{8x8$Z^$!^xUp+qRxh1w0< z;0NH_V=g${)lf$>Z6XEw*7AM$Y*py%F3RjcZ<00yFXZvRz~Jk}C?wYhdd_m%QqN+# zqZvm5#lMlNZ%kl7Wa**fBw+H#>>v+FOIOgH99t29J-}Y zsM^%#dR8`Xil&fVx*v)@2Lk#lUb0^v-U!J@Z@(tWs&6Nu-=+gZTSYJH=<7A_xb7C|`_^)S zetA*PZ_PxRt7l`7uBNx<>SwDRQ&4A#vy>{5$~E)%NLN&kR-x{ZF8x97>*`}o?qkin z;G7u>|A+w(FbspFW7GCVViVXcg1L(97S>fz4^~8Bz!Y%h=&yvaiF7^a?Z6OpD~`$1 zMM$UQOrp{0f+E5g<2HR|O}KlYs?guBjHu!LvBe3e$pbug>=O3kxPRq0&%->X#EHv- zUb|2D_acsy?b233uPbMs##nt+Pur*sZ7&qv6{#jVe^3livOG6Yt}RjesLz(HGDY4W z6vyY2*LhW~ygFVXu4n3bbw{?!7m|Ca4`km*r_pO3l$ywCp4(h^E*Y-H#L4@pc&z7a zXz1a2?y#;qd(uWMUwDfx!)zli#n+=uIdqYb*#UOG7CTE_Ez>Dr!Dv?lRYIkgc*+G(?-(#HW+ zbn<|w?ZB5O_$D~&mM7iVCj9Ao=C)6}bt!Q`P7%gJRfQB|t(QB9RLt@4D)~namZ=_O z7wh6(mr_&UR?*cqevt? z0e70Q%2ViKkzTBV^gU_eztLXXY1NIr(Ue5JzIAsD%|@z|BGti;F7bjRAK7{Es_4TX-}UN2iO0#kQ&*?x zHI36;Z8<|+;Y~!#?-y~4m-=O{Z@X>z=FV-dm$RPv`PLn-hjpw{r^nKJiLW1y(MJ%W zZrPXI>nz3HM}741z1G{<>pbe~tRc0%se)56Kp$>{Ycah&Q3XHp@l&pwv!B;0-(Je_ zQUfw*XVa|fQ2bWonznd@sp3Mm)?8JDo@B-0dkl+^J0~O46ZATq;OAiV8<-c1yN==t zDrsclQDODn;P*nV4}#X^f>xI&oHrsN9}lZrE|y*DwF+t*^qC~K(8XoW?h6cBv;P%n zZxm~l^ESn_fxX zS$S%K!3XR;&$#LmTqBUQHqT*9P2|s7F$?dUXp*PGd6Rdf z(;lg;&X9Xq=4|*>z*{DX9);}+)*LT7B<5fejTve|I1+n78X@4!@=g%74JjHW_6K*m zlAziI{dMWY&lsM;J(}Hs1I(`Z&u%Oi-GXXwH}3MH|J!f5s&l# zA`eNP>v`|VV02fVJhPJ0k$_rLd#*+m5srHj0E7D-WMO*3CMJ@0jfRY(_3b|w80md*mM=_hPO8$T75cwFhCM!p=dpozvZH4 z3nIg=;NogY=g8-eCesTqdwz(Q4Wx{lz8=|62XB zKB`8Dz@A|P8fATGz<-gP)U@N$d8k2_<)Ao&J$FwyYD^YRnqO^25vo$K8^+p35=Ld+ zP!B2WS$wD$DRdU!$N2mv$Ghu*?!rb9n+yLVTu%wzSMNBT&9d`eAN%`hF#C6tY7#lN zQwEa#ttfXVGXa6zN&HZv1OtAIYBI&1#FuCT?8AW-*g^arV%^FcGbFX_(_&*T34M|w z!oNj%25;50=;OP&s32ouYmh2cFeXTv0bK(OLh?f0=9fNncu)D zGi-74!g&;1wxHPJ7+KCiuC)T#ghjt+-WqK0J|?WB&R)V<*sr{5RBalB3G}P$Uqy^Q zYCDVBDV6;|$^Pv1N`6J^ES@w7t95WEs~vBz=zHDfxev))FiSvZ369h+3o0@Rj)rad zPGf)#9!DEr)Wh7+Cr^5|j0ul|hUWK)v?_)v_TkoN#nCaVyM5xqANz48jI+fd>!iRZ zUy#NGy2jvh^Ka@3g*zzi-QIZb*u_|iEr!qJ=|U&+G=Q9f8U2M4P4{5(lS?K{nAgDO zq9jP3IL$A|1?D(l{LBV!$Wc=9BMFR|uWert^P-Fnk=S#+3ZZw^2zureC}Ao&`Qu{= zwNZ$0RhRI^scvC~XU_i?p}_&8;T-tw&Ye8Z|1>=f*B!!+O&qd<_e6}5lP3vAHV`j% zkYI8IYiuMlltp&(L;8wZ5=j~AE50l0UVJ58bY>9%kzDFCMlKO4QB{CZJXe$+iGZIV z%;2SnU#%;unA%e^*4ULCu#N@Rm7YwhpH4ueLPLH}uFLE3_lV-b;tz6YX%&?(U==~D zgZ~qIb`AT~a9aaAR{c939ebecy_v5sSx7MT3|@K)+Jrqxbj&pU%lXuq@dard>)l42 zx;kzju4c3qMTYwmOzD>-Pe=NEv84Cd9zF1^$KnsphqrZs~8)HNfX!8GUCE?#Z*| zjNd{i#$>+?CWif0Ld2K;W4`%oXf-rF(;4f-H*^=+u$y6A`p%R2dDM|I8~C~%PKYIB8dgNG2YCuV#Ra3^X+75@R}w#KiIW+11Xs`VRk3$`SK zAtZwqj$t5ln14T%GHd7Sn0B(y7cd)^S|bp$E*^6~Dn-1Z5r%kb0lU0BlR`tb9yrCH z%P{QHqCbdwDsy{;W`EqmlFNHASkj3F*Gk|ZO9JPglsh=t{}cB9h~V-fqTdpTp@rbL zM9cbo#(|nb_Q%l<@j9R<6_FqWxu16Nbz7^U8$dRG!e03qSd%y#>jyD)P$#+L z;vw_Hb<1mtYhZSu=OVg~`>p1W%y!~btdBs{^HavuEo#@^FF#i#WprbCEIrD?5ro$R z5Lvdsgn(X~UraRubppiJ1Ui!+Hi8nm>Yqbz735foFSOX#S5l|i@yu)gn>Ubd13e+Y zhADNq1GP{H9A(H3c=--(BL>#haiyGAO}?!4LAdys9fHQ9z z`K2OQ=3~Y8i&%L}bty?PJ7lPG$kV7y;!2by;AHl1zR395fY(XFi4|v_J~RVAI(?@5 zk<1D|G9&qM%9rPWMh%s765{o36W-|NU{Ye|Nmrm-bYIJ%4wLySL)@RpK6d6^*Vf3u zi;Hv<@f&~ou|8KBl4Z8!;x|dw|KS4$l_;tr7lJ^tx z9{1M`Ec(f~dy~(wa*!ci_)oh6X?#J_dxFV9yjlrq8t8^7R7EwbBgk{sM#Wb!p9xfG z?_=w=28ly;t2GR=dbePlh9|?hi8i1HZ`_Ap&DLL+(F)7{)>T>r{KK@^DVmo0j2Key z=kkF>d|$Mj@yBd=yU(6e=XxTNgltOAvaD1tjkrr?%Q;ba(TT5VYgcsm(cS#kfu|~^ z^4|>+S4!dGVPhzn-}pPLna5}@tpb%oZ-2y!%!vqy2?JAuhxZSj!Y`)t3z}m`_iiKw zvXSI=4E$2rFoGSB#I;(}!ZuBdccjpw6b%DsiVH7OOLM2Ar_m1#vDC~^D}<+x|F(o9 zgE^7w+&|(zo+OqLp8w8j!eLb7`D2;%9lO6}cQopU-Hl5ywzW;t-F)#*7(CWT<;8~p zsdTm~c*3E~344phgti!I@_7ZnKt-Q%^=-VJ&dC>COJ!(nQJi#_(k)`wi+Hl9v^=^B zt&lV`keInP=-h-*zIGeas;gMF%%7)ahRRK0Y)C*!Y&aaT7gvjYWvJNtNLgQe2;Eev zf@9zQe~1@tf{Uh(Wa4+mQ^6*VM`%cZ-@6BN%PJHbAL_14H1^3sERnSP&X%(dco8XTsdhUVqjT1KETi*h88sWD*!xq06 zd;XS>osEsD20S+aJ1N94lNkm~#gO>9qKXGWvDQ81b<#mM zm_?OnfFnDJ9a_R&_~&o44Da&}FW&n@|E|rH=)gjB8^!WnJ84Bmqf(TIEye?iTkf)^ zVH|b>M#1-ieICc66_4vt2G1vl=p^;(zBQmtE#W7^BEsP89+daG{iIwt7;!RDweT#I z%ds6@rxs%@en~IZ=rVlkp;|gPsz|9EB?4fV)tTo!Y4v;b<$bAZdQNu`k6Y9 zn8g^bDdNz?0rf7#1-VM7L@E4lz5HXv#xfMTPT1gm3LE(D!s&m*q8Py&VZW0$jij?H zmIY21bWjAtXoA$SK9`phJv)XNGDgxm?eN)NtTFKbfddgvpD4H4 zJ-tI^FxrglAl@LcyIWCO6nXKtpBju;%p~-knR-V#zxEwmhz;2GVxlCM$=-VsQE~;S zqr7(Jlsm}_U@!(p8lR%%$zmqmT+E0gmcHjNds)IgFTRzQpStg42HuNFQ$l9G4FXGM zEGy#n?s!!o-WGOUPdThg--T{p3|1jkqyC?JPM?R4sq{QCz^nP8wqdfsyI`wvrIqarxYp-Vw2v0yy^4%AOaMhGe3$52ft1N_YZ zw;4tda$zuvhrQ!W9L~WP2aqd*RI^e+G^@ppOJTr)q76)~x-j|PE_u^~!G zA(YcrJWq1?@2+8D3_s`;Q?b-3@dazi&sJl|r}L%bQ>LvGO;X)M*N;2id`H@dG{Y!_ zzc=W$W&0ej=uk`>%S>-mc9g>9!cH!KXe*}a5XUJ~=%j>bKaFWLdBWWM#+{~PbDSFG zO{(EvCjq|0$EBqlDHk_bc~Qq#H1pdr)a-=+=zEYiaq>J?PvP_~=bB`zq@v(C-i6Dn zf2}OnwD_m(lF1v&S8=AD>W|qMJVQ{pgk)C*Uzh2G`ywUctMeZk;pNLnK~y?w&GJ|F zjEJ}oP<@Aoc2oa0BA-udKVAbmRrKnZ``ry@t4H^Nj(>Ho;*M53Q3p)Wmls4r+{5bt zPnvu;|3|K3i$?FC6jZiKC8yxTF1wzu*v!=4PYkqrf;2h(PkJ`J2j^ zy6N19^kFlsAp^xIMf1s$CSX5!{jTn=i6yZtrnaL^b}iAM+TPh9tM&ufFcKF@UGuMv za0NRyZ~3PRA(gnSm{`1f*s0*ck{m!dkGXjNKd^_7h6b9lw!Lx{)rjsg5}o8fuoKhv z{A%~w({D*sms68>j>?&1GK=kJ$7(Ch zrX3f@9YAU3q~q8AK#rU#by=Ycn3ZSD3x`>j(g{m{q}ww|!J0 zDiZuDuRQ>ui34|S!@%@VpK-+Y64%0v6M!B z+)-)ChoS}gA8__Xtmv{#HcfNBi8VJ4(LIy+&*8{M<4Np?_JG%XQvCpe($=|DffOe9@>239 zr4RKxfUpFy`Pl+Mwb_L?Tbh{29 zMs8ascsq4m$r(%F#Y)Hb3E6CuP^{-R^+r`ZqjYVp1ni?$Rso@FH{9}0QgF#L26!N$|Rb^ zEbB#rv}%Rju;^$A=Qg1?^GWBOFawlow@33q8hQD{&w?$+dV$zWpbA9S-N=R7R_P@U z)Ik?YLcLv_1^Hs6{{j(eWrU9@jaI1Flj@jGyI~&0{t}lXgzO8ob4ed~%q7;e^HLzY zWstmL@siEI_oKM1GA3!3Kv2>a9X7FURJLyGvUWvQ86T8)4DAdA1hC^B|0}VyjaO0y zMd=QM7Yn4LnX3bF}XEIF>D!+*< z#G*<@)8;ak7x}a_!7c-$5)BP;S(g~;ziY;3T^1r!t3zHX;;%ObrE<@=YkKkL0AZ*k zulX?*H+mPD@EmlFbDz6I0 z<86xC?+@6I1xD=EsKjyBrKG7I^v^d{HjjVA)%wZRxztWBjaWG;s!?-F&X3&B1> zfMqS-CH;J9@zlSx&J6igKvL%3S}I*n$**OL)g&xLMBo;Z%)(QwL$QL(#P^^0$NxBC^GHBXbfVVII665Eec(jTf9O|ye@lJ|A@m_syn00c8GcBF zc%A+vc9UT5${=;!y#pNT!x!TSd7Q`PzHY+UQkRWk(@C0Jq$Iki;fFw|QYbbAmr5(f z*^Lclpa-Lhi?~Hj2B6SOv3}ll{i9M->AoE0J!4n`#h^i*sZ}IsS-KLOS(NKlVCpLd^-I%g;LX8aWLQ`!{e%c6}6^O+_|Ro%VlwZ(9!%EXjJ)K zP{Q6i$%g-I%v+m=mFvNK5pRRpe)l+OZ%Elad{X{fdHm0uBe^GJ+OiscT#f4hrdD%m zkv#YnO49OHG3x;!J}YpIu(yLOZf~q%_mjgck6TtL&(AVohFhA)WgmM8zv{)wqn-Sq zep9tk!3wu`%FNzb+$-a{Nb0~1M*MDfdC$RTqcUM!vf^0x_csm1is9$UL|(MV^YcO4 zGayaUp0Z@`J10SHYr?oT6dd5H?A%L-<{_yA#EPr+`&3f=wbWt$MPshy)~3y}D23dn zbiT#+s(5Fu1Lg5Dqr^~QjpXrCYdy(Et3hm!DyK59;6rHfXfk=sW^jq~Zg^F)fN;*f z5lycVIDh**(Y$h51r2W$4d^+Z%6^cHZM2RkJ9Ikv5HRx(~$t zg@JN4Q+DQhQ@IG}T{myYy2+*~J^5!7(DikFpltqb2__eCG{Y|kG%@%UNc zTJBb{c)5l9pmQ0;ZM=-Q zcAz~m894WW@!iniU*YaiKQ%R}F8gu!!y*z!9LGfD8vKr#xrQ7Trm|zXB45vH+1JI) z7xP*IP=$@#-jYU{x|X>1dTWt-err`vGDyqw2=o!!9?En7sMnkNf^YqoaF^9E{;p$+ z7MA_f3?sKmyuOlV3`Y3)$e?`dZDu9c2570mll;)tn77!>M+R6Li!>8ju9>s7WS&lF z`*jaX?&ENvk$v_h4BLm9j&c#tW$dLHqP(j)7i|V(S{bc{7NW7^_s6!e2st8QH*Mh3 zpZTifZqwsXKZ^=G5U;J9Mbt4=hlQq=0+Hr+8C5KK!jrznc%HQXPi2CQoS@F@ab6p3 z?^j2E;YPUn3r14eOeQTYEl5<6>$_!}H0O2C`PTQh=e>u^ZOHJSJ&n#1)5}XsBjeZS zj2+akZLV8IcGpUeHGB7o%hJ8|`y(}+B_mPA<~7%WNu%XScgbi@9p=UNGbu@TXN+~X z&(fzBaC@`!SkhWOY58xF{(HCi#x;1NJ!$93`GWl;X`|G8qYcq1{%mU;EBfDY&6tCu z+Dz7X?%G}VS6QY#ZeEk$#g3s!^h|CkE3Mn=GCgAC zWy+ehuygeW2eD3vBMV~^>C7g{nX`0?8JU?ul#Vxynx@jung_%-`S4}yk8blDQ! z$WODT;Jq{8RmpssDR^SdT86=GnjFjEeEmlAg384E+~0!M<-c8hqw_(9*iDI#Q%Ke_ zuu)=qT^h-h_?(!S?1RtK)|v8_aNmm*D}as!wS%!A)R>Wj*IS^oGA(5$0{8EA8vmRo ziXSBOd_7r4GJznTzgAqzlB*t=`aFXWL-7=6?;~^ZDW2?%h&_G$*3!$EWRS2y7>1IZ zIQ+M~9dujz?$bS)x=_$kbAx!uRbESn+S`pR32FH9Tp3kC>lH@?MViwIb%$5??;nX; z4ds;mRNi^6wbg3n+@*$-JL{{)Ps^PBmbn3t7!gcm)izGmBQ zm}kMG<6Ub6pZ~H7R}kWZc6_f1_pwPvWu4*AdO)}YuK12mJRe6<37a)U?x~^t;OYph zc(yeLla@5G_!9>pU`akh9!b2tAeDh_HzOO+Ky)}`qj=0whlxLcIV$&cRfT8Bj=(V) zVd_jVcNQkp-~d7|P~j9=X?g}FaK)dP@^m%W%&EG*B_&C_8P967;6MY zb~klgXxr_oHSF8#-K}LCT8(u{bvG&3Ae#jRd(84l_-BI8yq1W`Eae58UJEbSCpd(3 zLb@TH+r*#6*YqP4+4l8@`inhJ5_=;*88~s8*z1cRiJh?oDKV3kfA_~nOMp~vBNT7X zhj%0me@SjvWhuRlo}+%+xPQ7!46j^k@=q#8xqZ`Ga~1su5K zdZL)QPFz;`f@%+pWn@orpS6J0kuf<#RqNc-*-r&uqA5tZo)>?J~^GUQWWo$15E5nCBC~VgbmBUH`*hjwRlgZ^H~6LAkPpieK_`* znixTZ4!W46LZP^~1o@SS>X*`9;{ec}58!KlFkOMHK^B^;?VqFkCyEmfz$BTrDA?T= z*K}kV3Ho3lK5-ZjZz6x!SKq~%eN(8z6vX)F5dN#XTwR2=D?j_WkD!3qFm_Y`{~BhR z9XXGIbv5pr_N{aS_E2y>!sPVfubHNUEyf9tRi8`=62~9c8)0j5j6Q?`h?5f9!l3=& zAE}y5~C{Xekz5VrDissEVU3z_c|2;&|aBgUcco@gA>#w(y@OH+-ODG;f=fhQ}%3 zq|3q2r-OIfK?r`hd9l76^vKw__*Ro20!gF&4oLMrA|*0?GdM!9!v2H$BEX%$>XWuyXRhD`0gVwMCm{Of=Ht5sWD@TIU7est82 zqMT{N6I_KWb`+#BLv?rL+em>M;`Qm!3Kim;jbgUq*mbG`CO_Zc4h(&$p(3UR;(rl4 z8-jqOqy#IZo2`HH^V8QQ@6skrr^umFR9Nave09XlKfKUo!rZ=sk-Nq>KYU*r!-+i z$SOtssSn@|KqUT|zNyjhreVgyq8(am(DyCbvQB#Y@63~0Bcx@zWH zK)pz91%*RI;_C6$C-TiERWck%_K=z4SqSKrD_-}|pTaN-(19Uca1H3EY*{_#c+JUO ztO$Kg){oudXT^JkH~L#=0-`$~vKNOEDPJ9K8mC(s_UFA$=hHbOdzQO_!>cdamWXUs zB@NfUL?w3G%pjn=)ua0G^FZ&wq!iFhoFQX6lq{OrAFuf0IwOV~WdleO^aZ$^vJ7BN`zpcY)1Onxx2 z;rh9NN7*nX+m%23ckRc;tXFl`tj7@(kj_({&QW|nNR@fXixOQ8F%jYqyAa&X8XWv#%qDnLN-e$P~vC*2Iys`uk)jH3ZjqV?0~2d7ZOqddx;^D;ya+3hGGBsPmLf zQ<8X8{y(<9!MhId*|ss;*f??0*l29qwr#s{a-zm+Y}>YN=LC)IlheOJ@bjP@KRG;8ERk+?rIVU|-%JWqqYrkod(EB0P z-aMX?xV!6=4Dxu`oC^)iy8nT0<%tULTJVTEBD$lFtmQ~HiPh;H)Zf4rVI zBGRiOj!w(Gmz3@|5Sk-|QFza;$WD()&Gp3eG~f?af-sxc13Gij199y>eC6`TA9<*Y zWYc)H#)DeN*BEv((=5);Z&V~0m=9XDnFSU>^Flvx0Mdakx1GOH!KdsYnyu#V8#h+4 z(QK3o-7g~i*78CDTBps2OHTO{BXW9Vqi75CejQ)@D);{zNImjI1Mn?78h&4`Z-2OT z_cYmWcg^vUMZ6cA86hlfmerW3vH7FXwyMh#4P#pkVMrRmLw%V)pcK=}x!t&BsO@Ad zn(pC%th8KhU_p7RM)(kfI{P7*xCtWrWG&Zt>TGepilR+CQ2qpPS;T8cgl_(yW8trX zJ)>Q>YiZqRK7}#sG5-4$vD0si913U&&jtAH=rUqowM2iDG?4yyxY*Q|Km~kB-h2sb z`PJ97jllRwV_G)9vsdj7E;5}?zLkGw7Mi) zSPK@vJg6()@V#vWjnOp!$!NBvS+;9!+Hl6EY|viLF&lQU+idDLNVO4&P22VqUv(J$ z0uuC}JBCP~yCr8o@${VPjGfkAY`66j-8U8KIeksH0mYTw_pjyMRv8Qa?m9OKD{e33 zpjw=t{K$_#giosK{K)m8pELD_kfLH~?rGC`nitfxZby^zaaTOPRH;2q;J%^Q_0U+I^m^Id?X!=!eax@_ z`Le8Qj#OnTDTiL}Z=&Gm!MU84rLq7CEQiKS)7bg=y7@iYG; zC7i2ugDU*MuWM1k+mC2Svy4GdyEojS#aOs9SHG{2POZ>qDM?y&D1)v{*% zC&z{}khq(5mHh6G^fdTBCd~}jzjj`%cIBg%4_Gu69~ORZ^uV@ayou!gF$qY)>b_GI zRhD=j>i5pTy*wI&04-pB=y1I!T;5{sK&c_d5QB$d1s3zapMcV)eayHXR*c8xKMv%R zb6!ewsC$6?A5S+2l>Du4Be8E1TsI@6@Adu_jCC&~ekXHU!;=C*n!q);Yc)&Gix ze0hqE9FZCvXeX{mB5paI;f=Fv3wh@wibh57U608&s;5vSY>%>*D5FE_iUaT%3`2Oy z&8)c(k-leFFimxn^U@o7`F>oSj?-{Nm@SHeh(}j_Uro(8%d%(Pw`vPBeYMoa&E)th zb!rT?EiE^$r{C}E4!hr`^5BcE&pYm=MklCO#^&WNyC*L?UJsp44rL^A*4{4aKx5xQ zV|W)A_jF|=J-VPZSM!u4N}8$xH#1Yu^U#oOAl81T`MCA?SWZtZlF7++t!^?FNJoV(H;w)`DUxy4^{ni_k-RDd-xq1f56ojS?Wx2P&kY1M^K>d z&MY}v)5rJqamsFmG3RV&pPylIp4*dnW!o&EgQe#7qE~ZrH1_$I%crA{;hP-%RF<*X zNYKOt)lzr{b_!fuB#BplTMFlgd5(A0OBfS%+G!Jd^*TY{qvZ=l%(U>T=XTR~(Am6e zw9nN=a7CB-IFu>KzN7Wc*R#gwEt!DVZw-53Seqj}*3tK7%n5g2*7%^Io&(=V=1-l) zHX(`G{0mYrwxAbcITT_5Rg}bC%W!v=_XQniV8GAGX{8% z9P*}{e=&6GmgfSZpG)434|wLiFj@k6R4OtUqRuHF`}1Ruob~EzU6AZ2vDzS#2)pYx z{8rOIb1ZT{Q)uV7+Aq4AbQ~_M$H|uoc`lI9*&4!NefUYPD$IGydD7)=l$DMkt^b(B zkC59-Q;1cGVh-mZTd_@PmBQiDM7-WxnyQfM<6+Q)YimLHIh{UK`SM+#Xt&Axc(wC2 zyKsD*o4>X09@=hx=deHLdl{8?U-sQo-gM>I+R*jhm)#7j{uFuKjrHqz-Erk`0dyRO zUi^#I+19=9wQi5e;GgTfgaBM@xi2Q$^%p(5r*vPwZ*o_!tmwKud$%`tzTR9^Ui!E{ zs@r#0c#p5spH973AB}znk8csE8)Wm-sO2nHc0SeF%@5mMK18})0%dzEwdQqeJ)PZW zHOn{ntZ#vOGyUrf%r@t0h3Snl`=^d`o=eY1?$&B_9G_*4+U@69mEm1h@@HD>F=QOy zVa^^ej;FM1JG5Pw)-KXBs!pc#L-@(BtknCl9eGg{)^7JY zSRSM8I{RREDZK2Wg;76|ak0LYrFpLHYi2PPsk#YoKtunJaILdZSq{B*TphCNAuZ)+ z(fnTPW0jrkzHxWQ810ekBcZ`#-(mmeV0n30w!f=S4#{m(rcIskUM7E!Of%4tT_?{F2)& z%8Z1c!)q76zq@;mRXkl=xVFnwQY+H>nBKTqhJmw9!@8uM#twbiSp=L0mABtc@t3D` z8%-A3tXOgteYjnI$Mf+&y#UtsPbTj9PIa*~{I3T~Ul-K&Rj2u!rP(!S)zABJ zb)9c}^X9p|ZQA!EYSY%;ue5#tqT0pX=Va^RmiyxT;_b$Fb5sA_t9gIxwVmK`b9>Wy z8{)&~*&4}B|9MFGW25}NQ{TVjb&aEIakF@O`NF?N`zCyD{`tA;Y`wfte)FJ3U3pWl zQoT&`;Q~#^mfm(c0w_HpwKCSh$J0gUunIHH$J+{##aEX6ekD27@h>(Uy6xGwfn2zH z-_SOZTn#YQN1$;i_q07Go06rTjNrIgXirkrvKi0^SSXRu-bJ}Nuhs@rp5Kt zhc(tb)^FE+op;(m!PqLZ|EiMYxyAOFj4;hr++nvL1B$Fy`;+rjOPO@kaj@oeIlQEG z^}KvW)$Y!(L%8)`=X7SG+B@-X@!ZVt+IfS9q^G^q`DG5Qin-d*!IO3U+y7Bq6fW8+ zxe>H^%0T-GGGzr`o6a3o@KJ8pb=Nmf5VY65r7<&)%>g{+xYlLL$GQC7fa)mARh=yr zy5%m{my7Os*SwxurM^#?o6`3)!;y?2y3fn7v6%TiE_Wl($#bmyJSrUI%OR)ptxeO% zE%lUQ++UXU6>k^ZZFTn~Hu|c6j;(Au@_8Pq*osFseD}=|IxJp`ZrYsnbhjM$Wgi~r z#=EsXZi(MAOhygw0kvIMuAsARoI$TOk2LnF?8mx+oTS3<39TQSSnFY@O+H0unR~Cn z$}fg%Pnl*%M^)>o9(KMZHX9z&ttUQSzAD*mHyEb-VM}H*F+HYh*l#aKP3bq0-K{Mh zFPRAA4{O8L)35hJk8;%d=?l&F-Bmo+QWM#J;rsRpNb`3DEFkQC0d8nY_Rg}3=gZS^ z0?>iehv}y}mu0%MvE26KBKM+}s-DA~MQDq&6u-=AH?QfMBde!h8mD~pYOBV54KQgC z8!BS~>FbqHmLNQ>=+kzO5_?|;n?8d5?!L>g9*@MJ8PmjiM-X-V0*HQ3Bo2?822CF&MnkI^h7X2-C$QadpfMTp0pTd zfuNG!QYm>0@tr4_K&gqJ(@rRov-R2AITb)XuYyvbj@Om1M$|RIb4aF2>>Fi_pQh(* zjy?6~sZUXwsXVa5|EuT|O3BPfzXd^uUljpZ)B> zwI5T+Q(Brju(ESAc@sMjsykTqMLnE*fAA~&z8V$>^y1u-RiU=w-xinx<+pvH zQ$F7$(>POjTY2bA-rZ_j9Q-Qw#xCEsx{)q76n!(j58;w1Ro9X3S_f?tCQM@lAFZR| zv*oYsMH(z@ojz5c>6RWCYwft+3tDK)V0YfON>hxHK(9tGMKROd+o;l*p!P7w&W_Bh zKV|?o{c$P3W_L$kBwRJv@nz?uxXtqD(Eba+Ng6r$#*67xn!@^e& zKD0|rnE;rJ%PW}I)e~7NdbJM~H)#6QpmqJW!j5>n!!f*Z#c98mj=k12B;42D*DO#I z-ArO{G=@AN>QOlA$mo6fW_}QuUwS*f;7Q^pn@)F9O>8w$}=smktLT<#9*e}mxc;g`NIZ^ zv#HuW&f@AcL9GY?XR^0l1sa^+sS4lo4h00esp9c_P1-g8@N8B*g5GJyaX?Xu`A##k zgEZw2c^{qTnp_sl18_=UU0?rWKV5q4T#M6ZtX{qhQhHpHfphVmf%mEMW_~;+1pl%!KdAlz59N8ybWO~DdTdTC8>JUXYqEL>a=FVvea(-gZ zgFmk`{SCA|-vRm-5FB04_onYJ_n{=gt0~v16gu&3_Q|p2H$j=dv({Q%;MY9%sG3oSG$jet+P_-Yv1xLl4hHBgRux}Xo3==%cXf}3c zzSJ!zf5%%&;pZcLzpff*OFq1$!CNZT-@015Ay>5Zn$8$+b3Gl$|B9 zpG&ch{B|uLkXpVVcs}fg>$0vKy8olQ|FW8+tGix`%~9hq`(=4kYujtMfzCS! zzDfavBKzxh?5^cyJjw9i>&~qa;oIvtDT$BB)n&<4Sb3ih36=&W?o%D?6Tvl~5AG?g)0fX zezkkr{vFWPvzw#>CJZ)f3d0VThi__e|T|z`6}@E@Lm1u9ypr1U$<{9 zzuy<)wcTH~ba>ycZ>@HoR9+r$J*nHDeq4Qi*hlFN`+J;fg$&aazx%ab5Vv)1ak#t| z-E!;ZT{wz9%GJF2-)0?QO00acZcO{?@L?MAP} z7g|2YMIU_J9xW*|?A2`&?j*AG>kPP?w@qG?>2k%{Pnyq7dOU6N6|uAS?~fN8G@Ji! zr!J@Ne1YZMyzf?9pI3nF^Y+>0pLwE6{-5*_Q?Y zm*C?)V@dGhm*U!@OJjr~B4>GSHJj-_lR#OVZgbl;CNZ``fCyXVr_19YxVF@&;{t(^ zpQcOpOs6jrfe~;)c4GTohocfe=*okYAuQ!S`%oL`=70DKvSxqvd#DX?_j)3Ck0={6s{VTrDscsCsc|b{rRP zqv3y>y?7t%Wj{@eN7}w>_6j{v-W0cg`QrOC_}4dE&M&_(;NinX(e&2r`*~%~Ws~1U zmEF3n{u!V5Ysy_~@w(p;?`Ea`o7E%NX7`gj_rt{p1zB z)uWR3kL#Exo$n0`W4)D|N!1HuPOLCDL)bc>x{x2fXz-@UQgq5C;?yQ{9(YCbZ2HN8l(F;e8J%Qk>z zA4zYA>o3*s!q-9$E?hXHOF4H@n2Av#_Gh%zKZMiuP zU%zL)!w7MVQ_;ycz5Uh_1qk~V1Jmb4^>^Ra!w>DTv1SEJ z^A_n96t8}7;>Y|x*+jRD@#-NI_uU??w-89 z9t7*_0k(Ixp_8^N8eZRbs>GbLs(B8j-FF_MBZfI$L9ae-!Jq#M|DKFj=#t0Co097* zt0flsOMBKCDc{ZXus`Q1x{mAP_Ma{K%@tgMDEVzi|-zDy(yVk0KxLE=Z(E192)Bi=DvN2D`{EZ#SP>ovXEFjinkKk}F|AAn*FvSibzv zKXf+};h3JmLC<(){-{)M6;7M>Q2L1^S)J;{b8j_nR7xpNbCl(R|wYQ`oN)W4?lHbmmpF7O;M^ z@E#;zNZ^0BHSpi*;g1Ej<^vg~51~$DB|VM4*0F*%79mV{n;IP_8bZt6mHCA2hvNM_ zuHRLX4#p|617HS^B9F{qxBaJ%JzIGfF)`MP>52vMnrzwORdP3$k|lb8FPmCmFt+W~ zsew#)c7hRFFV%Bnaxd|N$0S$}pG;60Slq}*nJI;2&0LI|5;;5O8~IFNp$o)>n23v* zjK-Euu-Mlk;HxA^pIJ}Gys;B)w5*k9FIN?#)1LePF>Oy-$Jf%+6;wdV$Oc`Br8mXB zVRCbj@qwvr<4lx$+hFVfP$p`+f?4E3o-B9YPZV;-s5cI3(6`J*=NCny%Pc%e^`YUt z($j{TL|51bPZ&v-7TiJWX}Y4&D8MscT2-V>Ty@XI*mT@!RDdZ^-pwu|r9h!s@86-H zT@EB=^vC*Gv0)6-o5KI5X0Kr5VD)_O7vJIcj3P)PNC#U}*;&YbH#{rr%BQrP!)STT z6dtNi)&8-Mcj_JQAC9#EbEtmkkTUo81^!mQyn>*n>qXDCJW9fM8aF7A{sAsp)J{68 zGD{lp7<5|4N;0CM`3W&hZIsNmh>42LaPSb!glw!+p@IPpD&07uYV>0km-6z|c}O-q zFOs_ZpA_7Gcddv3dkO|>csH1Vls~#EWR}Dui#kTj9i$~T|K)Abwx-_Deddi>eE-V_)n`6X>@E$)RB$tm_VD>QoS&!fLecE4_xXvsM!c_54j6WG#>7616tS(meX>ZH+o!1k*Hv5r)GQF-?2Vi9e=!pzTg zIA#3w<3OjJfc<|{%UX~>?pg0x{kN}*x@q~H_ayx=e#rB`K6Jm7WpdGV!0imS}hauzTx6EtTyn>7|g8o4B1z{*#L!ecqaw;L|70o{f#EVLdKi){5Au9*a)k^t%tC z8F@Q!MinV2@o=2!X@GEHx5$69!Y}-rr57lyCGkrN9JW1$wa_F7vR;BZKqa5=37f+B z+1G;8Wz%QUT2DKLubzjRVl)wvJclezhntcHdWg-l+SSUwbM9RIc?5;2guMp|swyBBoL& zVb&s$2nSEMdZLyiW~aPDlyf&26=&wgl3WiakaC zRYSIN9_f7ju;qOb@zw(uCCgq5Q=fJelWCw$(6U((WnAp#HVL_Y=mqT62D@o#MFN8L ze150(2wK&s4zVG|>knpOs1%z`^fCgSjY5ev+$v`jazc#bN=dSc78~92rdb74*n$=A zLRr74EWQoZj8)30s%xXK>)=Q*$Ax_`9XVtfNm{ue-4Nkp!r6*8X92>#joCmdcN?2z znXb2t*wCJhY_;sXjhd}ZB3^<>4jVWNYg7i}XiB1NU11vEwPhy^m2vHmu+Z&r+29E5 zp**b{O5$@!7{9Sl%o{?0-pN;@OSV-C4O4qfoTvZuu)mp=b{=*gll8lpIG%fHzP1bO z0?g-mY&s#9YRxHfdU~e|E*2ii0%UT$hDNxiSC;BPI98*D$}D001&!59tjI}To-EZy zfFJ<=cdv$kFpP0sk$)!CR2&eS7RBg2EWR^xZ301Lt?3&}2Ary@@aKkm$`e!ePWg2V zY*b@m%_7*IC)}$j-gbt~xvrLvg^H)K)4YAJDTubVXX()0c^K)~C?)b$#k;gg7aA9g z(c@Lfh#Xn_hbHEH`WY9wHQ&*yo1P~`uG-UEm+;R0sfeDT2;aD?bo`fISalvO`Wx4|`4O4+TgOePb;p#V3Z>%c{1oer00PJNy?kc+ z4WSGV^o(cnm(iYq`RXY{A|oFf$Z19}M%c@gk#DFRgoc3&y)`O7DG(N=ay^d~W+$Xz z*^2*+kOtWX>}K-Gf1c#1ebeYA<60=0$^M=AdtrGI>Q-=%47mecijUJh?#=*>u8Foj zy=yJt9+5eL(`INx&8F~}5e~9l5=wiQEry*Cv7wS7Gg;ZPLfn85on!}{QeiHPu8AP{ z#@>g!C7k137R;b&k5L)E5M znKWC9x~i-I-Z--W9?8b)RG2JowD(8=E>z=gF6A24>)43lt*L+PuL#lSGXxCX;M|D; zO&GhvWd#8&VAeWJo^;h?>je%N86QI@!K>EcXE3QIo>wVo2I^Ro8{%?6v3+EYfM|GW&12%Yak;BQLSdo4;_$;fHhjPoC&8OMs& zl^qq^RL4eH-8U3|K0?Uqm$hs5y>Ooi9gP6woX~ik)uer5tvRKbGCX9G=&G5^uWTXZura5SG?;f4 zW5Cn35x`4eNXq6ku!@mq!rH%d5xZ78-`V4vM=U z7Lj3t7u*mDyFdQKU-6rQAw7t|4_P&i=BnimBT$slPbWyI%kM$sRsnXMNwqa!zQjyk zdC$=vOo7?onlCc^pX5%btnA-tSY2|=jk3mC8Kx%*HJ`vl`~*m&Z5!DSWYImCF8^&} z7ZG7r=|A{WKbed{GartnnnIpwO}BKtTvM%5+`{1084{$jQ94_>;XJcUwTZ8B!8}2> z*{P;c$A^bzyzCAu?CYAg4Uyq8h$0k$N68}mXL()e5OMB=K#K2~R$y1XbbRhj-5*8) zeBhioU!`%(-IZhOt)SjLKsRP4@FJ0GQP2nhLTp1$08H5lNa0Z-5E?6YBdH^?O&akH zEG^t4k*k}WG(3DIk6T#UVt$uYs@`?+qvX&gi}#^l^r~duQ*RQoRESccW|{J8U_r?j zl~IkE=T}Gr^BJ3vtr6KOq$HGedgF}Bb!Z@E(Oc_|bpQ%N*RIN?XS}?M>{(2ymJm!b zk~W|Ob=xw);Zg`XtU`(VZZ?MS*izIe$;f~A~j*?ss!juKTm*c-mHT9qUqz&GYH{& z>k%Z;p&>LznMyNybzkbOXhE=e7kzymodCLAv ze5r&&;h3z_JX~K>5ok1hgdJDZv?9V-uQNN|w}T6D+=&Vfh%~M`$}+=P*wfKqQ6g*@zPEH}$qxN{;T#SC94I)2V^_MhPAQMg zzjE9P5AG|$BcV{Ejv$TOWtFI5frR+6g^RFAlC)bh-8t1JOLMG&a zhdMrru4QIiDY?R8ple@bIA0xo?!UNDgfXbWBKV;5O!@ekLuF;sAA%$<{9Gq!w!Q+X zT{kVNpKbnRq;>xshlqqipLo6pVo7wSF%jdZr2Hgv5#lynm~u*Y#s}2V`V7d9QnV4M zBvYT$9wqbW3*leI2>tj|pp4WKM}=ELAV|4-T|;7kj91BM!BC4i0V%h0PBa?wP0z9x zg-Ed|FuLC806iCwKD)oRQ4g!-(V@1=pYM$~DD-Vcb~z!!+2l37RS#0+FLpuGq8l=+ zugHF2UtmC19;ra)++?uV9}60m7qN8+1XLO}!Sw_) zZD2{s>od^w@b8!6R2{62*?t9frad4iM%AC!F`)+rsZs^T4&kW%GXd2VDyj?2J;@T0 zv=n-Gw%Aa!$mUmL!xibdF+qkR9YZ>CPc(rmnwD`k{0mjP4N-s7JmOZnG`S*Sv4~-$ z6XS`9BpX&Rf7{x-tM_24&c;4R+hbvCg{2`=F&XKflxH1V?a$*b>XQkpjRy2=F)mfU z321&aTF-Mn42NZI%&O7}*)M=cCrvW!W{PNJ7obT0u}&oLhWw_a>ro}J(q$#mzyIJv z)d|A$C7&gverK{HBSl4toRSDGT|6Vfb8gK34gOlr%Q|4de6Bqb*a|G8j`PJcmTi3( z5o3dHeNdZ_>4IM7H0bqcPgnbHVNB+`cs9Z_hnkOzu#T3$hClv<#HnRe+>k~-RC{0I zz*56WFG8E2hwCqaAe&c(5>h`W%!K(_mdiI)CSjsvt6%cKA~#^zAPPIZVH=)j8o!f~ zlfDi$z;Rb&IlOeNMm*~9jaZ`AXVi+)Q9IB#uf7w%U?sEzmX4*%Ol$&`pB5^I;r174 zFG<&x@op+}jz38v)45}w?5Y2q^O{LDOAPgO>_f{r9ep-s2q~)E1X(#bMtmOK4PBxa zi`a(&QAp=YVHPGcg9!rlPsQU|NN8>Ba=nFk8A@27>dS~P%^^EQ{!$=9b<(=gDaduk=O=|TX3zHVuIM#o9 z0rH2HNWR0}5yTfu@qYY;fr=I!Z#u=Q2WY2rbe)qA& zE$x#?Misr!F!#Z+PF-tKY>EFdRelX&j{WL_7r|zp%>~1zGo{)o>Y=M#{ZR?Kx2#{(4j4f?JESOuIBp4!E{GdjzKv%MX z&}{L@LSQi586%t}LFgFD^|ZlY0+CYl9oP7y7N^2OZ9p0jt4L-Lu$c%&{rq-?Aqcrp z?f|K7+uth*tPGLo86p3242}XbXtZYKGhmsAg+XIpxldY4W(;oV#e9yKz7vMdu_>Vi z;(eExwOn`9Xi%H@n}E1$gX)21iM)$7*=2%UJ8%g}(DH82t7o8~886(=Dx3)| z5Fc5V3JueVYvpt-cWuOhIf(wFVYmxX&tqO1r^i)wIt#58^~jI8Gkiys!Z(;lKTm*y z?#(6(p3pQ%@DvWtkUnTxsB$&Lk*gPvio%2>qf1Qq&305HF~KpYI{wmPo(;8s{aRV9 z$&i0Ll(&LyD$!t9xg%&R1;=6?a8fnK`RlBFt*cU)?(P@gKg6>^Kw$Z~N-bn1^?+Jm zKq`z^kW@y@2oH`VvM;K^!7sv@)G(=;XifRZ2b@a+ro2Nhszp>lnZr-a@a~)6(B0a$ zKK$TH{j_{KPW8U9DvP30J*KCT`H?r7aUv2>BqdrtyocdBL)ef{Nl)s$dw-|#^;c9U z?6;!78$7`!MTt1j9>GJmK{d*)=C@5My!Mz>#dNyOiBZ%ycv_~`vMnz0_nf8jv(V!% zbn4rJEj^uZ#Co!bAs%XmJe(K6^Hb+SO;d+~B~3%X@Y@!`61hn>bc_jS!Ul9ha?Rn| zrO}Y6Z^UV|LMkr@7~(tj(m3`*6Zf=60~xp(eho`K53i_0y?A zPGI2sNB{JTN2K@{;a^fArwZU;g@xoEy=MB$?#%HJIm>Br{U`az)+dqv-^?2So|z^r zQu*t+jM*LjMPh}=nku~rm)g=nzF-@dpSHt6u!xZqkehlh1vS=o@oy&ashC>hu&RB4 zVJA~^Z)j^tELilnA^6sBGS>uftkCI_!(AqZ)j~NtY`-_?5=Cg-q5_v7}mqc?R02Lg*M;` zSDLN3(OiH_tyR)o(ru;y>GFiYPCqKqjbm}@t~vWIp>W*;$z(dXv8$P32wJ^C6$M@! zYocRklG3b$U9O^%Bh_928PlOJ5d8J zk`lP~2t|Wt=xfM`H-eN5lF)D@7Wb)yCbMiFd;Nihcc}r5qbp8uRLdt!mC%ADdCD)7 zlyptAViqcCzB7Kl~h)C1JsRq zu~}hf0;v(z1jXRlM{yCIOJVFA87o77H!sG=H-Xk7o`AOsx6hp` zyfM#qBt{~W_D*n9Gt7;j5DXt~KeA>w4zBnCH&sm}`0KjIV@eYIG#1aj?k}l7$3sW# z@MQ$3Gei0bNAbQE!MbMUJy@^%42&4p>vF1zE>S~}LG#5P_+VTQ!>OC*@LDh5Mc!O| zD!*(Azv(#Vxg`nNmM^J!1*s)Tomm8QzD_L*Q2nq#Z~<+z9p zUqXWGW0WbT3^i{B(Id3DbRCwhS0x!DVsMLuAcMs0OsQ}4y`Fzo*lQ`4(Stx6a4t)Y zd4NdVsS{#njDZ)|_te8S#S|E!&*v2lzsH#G5vWIH%^%srAdZPQ20wp?adv4%(}R!*BVy$C5MP$&$< z{sV{l92=xjE1SodgO;!orWbk6l+`YPe07&GbsNp{D7%=;e#EiBxHMUQE}>?1VgK~o z5LU6?GLua8So~#b0XoFKl(^{6+e#qt4w0?cI-b}fgYt?FHM$J%2{MV`AuIVcG_c?o z&km<~s*+aEvVs#7TRxVPD8%~YaJaJSIoD=0Y^Er|=ZW@@VHZs2za;UHq&XT|f8KfK zcVDq^WXPs*SLkwft?2BV(vrUQaYzHr{Z=XPo(6e`@CUgFkA(TCr7%MVXk4yy%VMXL zE@;iOgZ7CG^!vq9Jz&2@b0!&Lh+a_b`UDr(?cYVr>~aqMA0xkwP*OF>0aD=Sz1*)2cProDzSo-+uje;@%P6#;}as}r|3VRv2! z!_bOv=dYvedMCotzc7rKL6qs1Cj1pn>R(mImY!?wnIOz?ghWe+G0NIp&)hS@E_O8% z5`+uoLR0bxSp^2gqYow&^ZTyCBjg|;fXX*TvNLpZuV#o+eU^=YPp?PHO-yhU1G!^f!HRreiqmfL*p<2nTEf-P|MaOkQUT2TVO*%gi%h~ z#7s$Yy~!iPr`i>UQ$f8@shl-(JHx|AL~5+Ke;DU^5_}F`2$a z&I7UxYK)vLv(npQk)(~7i zGuvMlBW^!y-QcX>W5`z^(TpdDr57`dMX2CkPDp*!@~>Y}IpoSk;Mp|8gp<>rP_QeA z*dIDy!uN5V4Df$ijrO5IT&);7;u}*RKbTJtiacSPAfZIwm211Yo>D+q`7@X9h zEb=3S(+?=1`uF;D3_*YTFdV7d+Hn%7+!8~dPFeqdK-Gk8G`5W*D+XRuWIhRb+_Atg;>-l0IojnRIW^^hl+?MfxL)enPc&G^?ZS~z<)f{VSz zKqQJrN?OwtosbiwI)PN4RcCkqZLbwM&SLX4h?JZ52?;&+A4q@kFts5UY!*Pp`7``` z9~UmQZO>AuyTlPHb0Pv_*Q3Z#jnW8wVj|cMGrFG|2G_A;8NnNS_Lgv{3s?SPKtns~ z0hn2_offZBp&y^~!P3w$cYuISgzfwB`fFbNZY)l6Z z9!)2U_;39!57{_+fBFTZeo^oSpeoQ*9oPG~yVeTu?hMQXp z^Nqp;b))*DUrbt^;QQ=RE}3YbSwlFqTFp!;71yN?v;(z0r@>V;ug^8sch#Czp1;@9 zm1L#g7EocSP_BO)Tdb*OTwjR%7N%*vBlr24G?#@2Q6VKUloHnL`OP4cbG2O(pRAg& zLP&!?4Bk;Xf`XGrhE_+Z(fny4e6?zTmD6abfbrLnc$29VKb-}0gfvVdEjP;7@FzZm zQIl2^MkhO93_X@K97?>7YcUjYje-)N&8?|7Xu?8kK-H)#fDL;%%oi(wuim5{TprQ6 zFCOWFl2~+EC`%E~8yOd8L{gBw_P`dd%VhspJ8<7ndQJwBER&**4uSILVzEdvyP1JQ zKTVevDsk}Q4y@IK2WnVHUC*Ky7;@oxBBaXb5={ZN&l^hhn*0*2Pl8f&*<=FwmuF0V zO}T3GYs@=Dz9L+#m7GSyFxF7iy(%;nf%n6kDxr^{%3JY|La;5(DjOIn!w#mDm+Y=F zjz0LmT@RUgqj|30T})9(G{lCNXkaPymAwuWB{Fc(*19dM1Dz6cC_Kv%F)W$m}{-Ykh^{SC^tND=WCCyw4V*?7E&#Iq}8Dh=vMqz>E3lROGp8N|! z{M77(2oVU+n)Ic@2!#ovu~7AvqVj#u`740L3V9oeITa;) z3EQ=DGO1>*n-h7qm-B+zmmceASw)Ek?@WI_VX#3FmwvFh_x`HlRFWRtf6&josftXUa$e# zfA^!;uW$|S;SoUHs&KIESRStG1e9A(6YW`!>WU8|A3lGFM74=i#J_C=1U}tvQgg#O+w(LavCqBtd(P ztK%t93=gz;vecD~;w+>t-u zQ0}s0fujvq-mJ?>B4k4ZTvr$+Q#hgn6$GX()lTp?i;k05X;}T68!Y49nWNo}Thf|Q zym^=j|J8j77c<%cTyGE>L-AGFrcOM3HJ?>WWg)k?L{XhlpT2a@b)%B@kJ{Z2k25{$ zaC=c9r|%s{^3nmZ3AF;>(=!3cQoPBa6haKuXZ_% zAMm=CpYxGQiG7pyobDO4qHJzLm$p2YiBAfy$)={Cp>`I@uWp$sW5iw_J0Qor8Cw@9 z6Ocy8ZPfmN+k+2roW5R6sl>w-5oeFV_rxfGh=Z7?Qu=^^83@Tr=?pol0^K4OVzTy4 zj_mCRvEdat*Ei;9J@7+!enVQs>y5B`+}$DQYfIn;DaECB06?N5|K z>~L=>BfKo3zy~$61FNW)%G4a14< z_ApqQWe}pjyzaY&BNGpF>ToRvvUb+p>E8!xTxQ*L^2Kqz8XMO7A&2^9Cr+FhO1`sl zL^nhHOOCTCDek(zu*=xWdnJJ(TtIg~7(RDTcuaqRY(6nOFl_->EPw_P|JoT`;e8id zT+&C7g1wZVwtJAj%S%EkhlM%6}_F#?M+1aFdaWJR6`}acGfXS>F-@yZd2`v1i&EB zqTys6SNqOEpB9sQ9x&sLSqPqny;~SzOZ?chOhB}v2*>*XX6vlB2ZoVwmJ7FtnpV3O zoPX{d`gbq+J6qbdrgX!k;&>h<@^7p?;}AJ-=J`WzW@5Mum^|CtR&XU>o-Gw)zdB7- zakc4Ou*l84CI6}5t5NotUNdD{6iYfB0NvrN$Tlfqd)yIYts#c0&y_)Jn>4v`?~Ul* zN!1V-@dW@Z<0Dg^EYZ1u?Yi32xHu;#4anAtn@9rBMbHJ89H(gaGLr69G z$7~PM3vMi=>Xq|@x-@uJLHJl+eh+mG;I(5nBaY7Y+l~VjHL{Y5tvD<8-az&vi}M*I zWl4Zs>Iwpod#4r)_imQLFj5UpFhoHwAV}obyFlE($`yRJ4K~K`Atw;`*|Ckf&J0{R z1K0e?M84GARp4#*>7LUb#V?=QO4nEcA<-O$c>lggu_c5lVWx_==O5BP(--$}Iu2w@ z1USk^ULF%?2?0SYdbWw(hK7ulw0BHm5@*lgz}AtKZh!lnh3V^{{{lxqxW7h>Kq#3Q zue4y|@;3sF)(p2OS;DHB9?$f*CijNYFcFhjvxMr7VBqo!9a-#eXmuc`!xVfTW|1cA zB)b)!2Mix0k3?O&~0K6R5>O!Ok#z=|N?3Wx_+D58=9W}R}~Cw)4Y@$)4>$euo1w2suRCRN1h0Y7e2em0aU?NMhyv2$E$&(T`{#| z*rD$+R-C~cy2vKM=TFKUl4c(=!e4DciKR!3M*%b>I8IuEM>`m>>qcVBUQrO0HPw1@?|}{7Brf)A15LF|`QMvS%9`w5QUF zkZwx1hc$fy2XJ=qL%@-4LTwZ<`%PO!29L>e|Dg|qgmnB=r68-xjn6?)?@QR+1e?=H z=p26>kRasJ_DYIiroa!yD#j)TY_XCf-11}q?pVJz$mbe^lBxwv zvWSN}PEFA@KkTiDoe*np3D~1QT0W79n$jPcDCM}+TnZoIo!15)#(`}jgHXkNFS$8U zX)oi^;bUC*aMdw`qZEwypimt#=&~CYXuIHd95_aV!$NeU&tnlP#8jCrH1OHRUKfo1 zmr5&$Lbb^abO$Oq!$eqTT3!c1Y93$5kdT>rM$$pXoU;B z?Lk_SU=nsGe#jFZA>LDWVO$eOGL85G5px=JjfXE|BxE@UKwqUu9`=Gmc0C&H2T_X< z(N=QyP8?m49Qv@{2FCm-5%Pz0nmIA&aquL2EZC6<`kZE)1ykJHW$Go$+0vdVM{t{% zX~64v0;jfpwQi#=llbFb#zBipY<4`!#iW-mZZcXDiVnT)Mi@tdeFBe|y*exVWI${r z1m=CZ)XM}$ndetlIQVt)CKZClD1HWzNT?u|-;(wLE?NxYCv9?Ip-+6;nUNC&4caI}<$dYz6t!h*z(y&PEl!-Ry2Bbjb08lJfWmE*;14Im-> zc}Q7osG$iw{2V+^qCr3+Qg__qfZecooL~|-K6H@UZK8K* zbPRyXQB`3Ct&VkqILvD2sJp;hZpLv4CERZLA|aasRvm9b*^y-fkO_~Vyx;;n#>?s^aWXE z1WLX~hEKqr7sNB}OOpk?MIavvjTh`7#<>YdB5@kH|CB@spShkHq;`^+tiim3ak5i! zc(unY>NXFAv^hbb)__#7Q#<>ny&r(6LNeBs1nW5Uv2Dj-L9;AjEJTy`Dw1dGkmKZ_ zraz*0PXd=9Xt;HQb2xS8ha``j=Q(~X1tHY|>8m!OhQb$5C$_x_#H+2wR3iYI!-y!w@fKb1k>#AnSmc;na#5nfFo3o4z=uxzsXaKPBc2})fz$~e zgo-V)tqXAw*Le|p(L~O~-sc3x-0|0b)`=oxf^!RVh5WKSRx3rPSaZCFUVSLh?nluR z?`o2E#KdB}$^D6m6a1EyFkp@U=6s?wYYUsihzJEfNjd}-VBrLx!K^(Z6rEWetlX5$ z;7#7&DcK|u6a~Wc@yefiL?8>N^PJUlfT+%Z;1)hT8-e6(M}+qxRH-e6|*1&o7n3Etw9jDM3x5=ZTZx0^hAfG2qq5q0G!B` zjjmaw>qrIGk=!_j#0|RW!y8WGR*uoad4dL}()8PFcQTx`7?Q$kfKc#6)votbq||ITrgmR z{)jz&M?A0>nVg}ZNq?ExQknEzAf^!h`bCWa<6{k6MQ6%EH&T(?BOvz&i1llen>|9q zBu|Y`>=m8$61#?|5i&p$O(>*z@!uYSvu)Z7h}UdE<1aFaWnzHeoXkiNIMFm$z=~ae z#4JTj3OM}8;ZJX(uup6}g^Mkk!sSSajfiLdiYX0d=dfXvS^Hzu?bG5i5MvY zH?Z&EVjB|>`w4~n(#bO|8>L;fM-c2mJUD-0cP()xE{TYNp%XFd7@0*{Ttp|rW)mjB z3xgLeRHZ~u>c*#P8|hNVM*GB)wuI{V=1LO6@sF{}d}G5Ta1=9uwOEn3Yb>m|8pL>P z$Mwjf>ZSN#9oYulkm{Q()3`B28?e~uyX>)>@hFJTLzpA|M3fC?8E{YLgO>+%-5z@y;_5r_SvF=ir;-9 z1fzm^!lOiVBo?s~-}Hwr(@T2e17`UGSq7Rh9jNTk7>36TDCaz{7IOJwMxW;x001BW zNkl3CcOGxC7!L`20w73 zG>zc>f#SZ>CK$0d8?8}v!l4UU-b)G{f{yC5Jv0HJtS*_hNfj@h-x4ac#W;LIa0WjL z_%;w211k1HLBV$Ss^GwgIrj3g6m76eVRzdM$W(h){Xm@UH{(o=7Aow?gdcrs_Hl$h z2e$ZY9~zHP{Zr^AY_N3>W$XdR1k9x3xJn^|7dlSxOzWocHmeGVX=B7-8Nj$;=vH;< z$J(f#i%Fh00{2TAo|9CyS_!oOB1A`9)v%reHvWPFeHe65!h=8IZ4(SG;BvGPzl=lr z!5-SKSP871JXukO)2ltux-*&s%}(thqu_uhncOH1fT*WWT^D-9jl?nLRdK-DT!qp4Ki8$p-()C z8(*HFM-Lw0yCdy60+?FDA+g8NSH>{+xZ?gB{~g)jB~ksvaR-^d<=1|P52*Qtb5S6D zl_RDYWE-KCK)%iUSW_K&krCEF0P+tu#WfM)AIA%Z<7&p-h$dg$n9t>_bqOcs)^Bek zv)RiYGZP`nt16pZ?;X4NXw_`0b*{=F?#;3zjS<0NwiiBTVY3a{H(9OEplSM%RV_7F zzY)-Im{FlWCkAS^-t~L~gYcs@CFUp5>%__fo|vI)V}glVbe!Z7$++Ugoe@O5!-p*7 zH~TnCq8mORnrb_S5eB&mvPFkJxCL}SDDg@WKNA=8CfwD3i!hJqMT!hy6#5itt$;%| z3V|~h5)T$+#*5D_B5HD~chTIw zEs2L1MCUk*fWMH)7PVO@a1jkjX0S~UtTDuTVbljZ;o^f$?5!JQfTrWXft{(h@er<> zR#<{#ZaJ_bJi6)YCH$5%0VJ_w+=n7{H9?`@TlGgG2zm7CoU-*brBA%)x>DiAAM3`QSFdT=HyZX7fMs`oe`og5-lvRN5o zf~DhF6!QQf!6VRe7THb&=?Nc=+M|~vMb6`>6C}|=?8yb$k_0&?6I%S4PkT?+g1019 zLQvR;DHoDoDizCo#jcYHLpd0LZq;h7{s~Y)Er3 zZ~~z}aGSx>bcTh#kXDWk073#`99f+NAsHT|A(bt!4)MUZA`Kc?!HO_H&E(;iZy=S& z)`$=ZY&}+yE8kFU^$~&^A=t=fZYVjRpc_&TR(*4^Pqth@CIRfQBcX%XCuNurj%e4@ z2j~u%9%yx}th51Xj0Bm9NwKm&u%f5=WukDPL(QnA0!=Ly*P7XzvX=*pItvO%n%4Iu}eQAPZI4jW15yY(1Q%IzJZ9ntld-_0aA9k57gD^&K`<7?y_LF3np8JCDff>+GZt-8;H?7`??Bq3r~ zI~jl6#j3!c!GK&ykrTfw(lQ(`#i_@sM%dutm?6=%{CbQdH~kW+ZD`mYc&(V2#1@ob zm%#_br8Ti3{1RT}H)~BJS;WNW90_AjsN22BRb}8bU{YsRZ{2s!Ly!$x4%+TIKjDeb zCsmMXps*YvRp1A1l70iHN_E6>86zT}0p=bg2&wrTX+h#^qDa*DYeCJgUy`qkJtiS1 zkFwXvBLh=B&T|wHw3}aBi`O1g{A(=&@MJX;0AJFG7f)aLTyK*(>h&iCVeo>Ce5S7% zLoL>}U$U54fgun49La|}0)-MtY0j_*2Ah*a0s(;vp~qN+c-YCgeOO>2Qc|7gDg*;D zRFb6ze}0oVfm56CadPC+-ah?SG_MaU#Sn5)I$q zARf^0TMt4AgypJEL~*Lh@Wdj#{nIuRn*6D98RC#K$+W?QRThFARsBva3B?Zr4J}pB zZ24x^-pj{?X4`@^o`LPe+;R=_ro{@m3=m`@RIZ9LKDETtR`wkvLTE+E2~j3lr~AdR z5KbinUv>+|A1rw05QzsEy0T>-1Nz_n?Bx*`v#ou?dwjI!J0*q-ZKV z$sG5|C1F}1Li*ze706*_9XK`WiZy!l7uwoSZrL-{L|=6gl=M3y;Lv!qeMnaE*p1F$ z0R_i_MPX~-+)RX!o0WATH6b{*J^;f@n;1F-RZhPX2~;r=%34(6TVpJaPB4TVzd0^A zQMuR$)xgQ^Xr;-x7C>B+4?5MJJL!(#ZfJq3)flrKJqtG5eqRg83DwXV~OJyIkpu?R2a_yEl(g{@-uzY!Eum&N%YK?)f5HY z4>VNY!MIIlsKcWn^vJ+e;+Q#_hF}1V&N% z9v(5&Co-7FVdofN4|T>Ba3BZ*DJgZ_Baf$F`IV1mt4Z0IR6O*r_^^QwTORThn`)69 zXvF&xACx*F^13(o2VC+AD`HtV9UGBi7URfYcEsUvT6Id0Sm4A)h=J6)8jL-GFw@0j zLlBzS!pf&_CGj^BJ5>prHR+>vWh4)@0ArlUW(o2*qgCVP%j3pHsU=~D?905sJPae= zqf;MjM+RW+eJ*e<{oRmWOc0M3R>bB$gk3VUQ{uyyG-|=wB*}InrhFuQE1c?Za2%kM zx{o#F@L~Q%KmXXL!q(9G?Twj@NMgFLGN|skQEhx`i6UH%7K&7-|L9xWj(*X4VGF%# zyFVB|wDq@iPA`Fi23mMoI6y#61X;o1UU$CqGIIFEBtKBBy7MkbrMq3a_PCNfp9?jP zh!M769ynh8gQa3pqxG4Zc9bYEt~7r3>JqVCG93pCT$EI}6%k@bh=4ab2R}>p^3t8n z*E26sO9^aT^5EMmFMfpI24aw{-Mzm|MDK z7*BLs7iSm!TZI8S;VN!sRNE(C@CXvvbe62V1n3MdXk?`b^DRUJNqE~WG6XnL zfWdI>G|sRc3n@%$ly=h0iA8kc?P-K32J{1Na)LKjb<8K$6t;X4K78hqpB@^hcy>O( zNRVnWWO{AVLy}ti12-w?8!z}~eWJrB?BLHyHZ?iNkdnD-L87hb;Gs=CI7r+u3<|6Y z1#xJXA-QDPU#F1_Y-B>|OKo5yA66GcHdc_0*6N>vO$<$B6NL3mtl>h2sp%lspj8KH z$Hc=Q|Dy@1(pR|c5FQ!+SQsq$R4oA}8h<1cXv4FzHrrNiP-UGF0-gH?+a!xoiwwhi zza)tk>+`NI`&NHi5q9rwf=8Pq-V)c8Oj+403bt7F|ul1u3?|6>zvCQ%n# zXMRoz4DpfM*p;?jVXzpBJ5{U2QMc(8Pu~T=bcKvewrY@Iw<38+bWsJnlb|v#1>jSC zhR!0X*hp0va^QiPfVyO=o_MK}e{;7Jqe;ym+G-!tkq=qTfvLF6c%#nBD+OGV|IEksO>n;S`&+5-_KT*d!NX5HEL&q|pMUDUUZ0d4$qzo{Z#`UBn6DOe+qDkmBwv!d}UzBD`>lxCY^)aFRh0;l9^Nj<(% zE3ux7rO9#diL&aKvP2$UV0CW?LKDZ49Q#7os2d;nW`XtsGr}VV8qv!Tl8C-k@m32^ z;<3*`sYJxuRtfGeKfqlNv*&tIc$`;8LhJD-AoCz?y%x~(60v9e>?J1YBq*#cTUB9D z@sx@LwF#LMZO+d^BZGcxg?_>ok2!&vqrFNXhnkIK7}QhJ&!|Q&vEp5uqk`>?R%4mC z;e!YQU7)?f^WJBI$m?pDxAto9tDt&#m-9F2jC)-6`(2R;lc_HHLO>G7LoHqfRU*ed ziq6m{-VG>oy224W%Fyn{Lk@Dcjxn9`hh}L#q@?6=5ZJ4 z;N6YLBtP0WqgUSuVZGY3Xn`&M2+y&%ge!k^mw4EDo~x6d7ZaQSXy(|9!^}4N=e>wz zsr01&iHJglDvAKC7*->nH1Lr+>%@i6n?$5x6T-%oOdNY^y4)^qW{fdnhzAVqrW)wmIpM=&k?~ zQ&30%gl$bfv8NcXklJQ-OnRV>AlZimXy60KY6Q@w3b9|))087j#q^?y9+X(Sajruz zJ2Dobu3w_e8|@I|OG<3;NW%~~Boe6}6CrZDRz)!?)W7lRDf6?2|hPjuW*VH|eSB zon-O_k~SYkGI8V7T}2?qAAEMdshyQ8PH5m1dw3Y@mOoI7pyPhLlni`uX@|F7=Z5Jn{e$VhnDAiAyM5H7|_$?Av2VC4xK*k zP=UpSY>yr78HLO%wo}6WyTTl=%oS?WzfCT>f%$yt`2d}Uf&o1=E-knC;}bfP@J-`=Bv*AF+iWIZST>AdxkE!yfR zF@9(z!p?=k*m^d3w1EO>_}KHSh{=6Nbb2;HUOuk>&;RlF**J{!xWK;W8BcZ_(&*Ob zooCAjn?D;xM*eCe$XrnIEYh{*{Ps8fEy}hjHoaRZo)he6riM^nk) z0OrL0DkZ(i-fP#X>9_1=#cBeWAP=J*_tU$qE*$;d009l|&~d5yOQ^L*@OF#fl4~gt zrA?m|VMa39QNY*!br|2YOO(fqS3GY)JR$RdKh#~Yo??Ohl&X>KZNA6&Y275PUkXT0 zNxXQx2{J9-qVjPU8Xw+{@JxsLv&YlfB3H~@y?;D#ERm^%;$b>J4(I%&@7a-ncc!^~ z*k57|Q!3&y3|RS~=+eG2gUcsv*os0gu^3@S<#V28YtIG6F=0`}5~ej(IT)&Un9*k@npM3#T~N{e3J_ zHLOwS{y;|)@8iUwTPwc#@oU_UXUR3)hwgkMcNbqQRx+`%?>MyHw%DCL7=FHIa^KB_ z!HeC)_^-}R-}vdHxl!+m{`&XP1qpnKisk1P7sYvbdkEtE4NEe+P$MCf1?tJuJ!e4EExR8i^$ZpSW?s>1)3?}Fz0c&=nwm&0Ao z&J6Lr?j%rSYse|uU7-MI-B^~(vw6PBgI1j7@7X@ix4L%IyV093u+IGaBV$FN^Dc@J zV&D#I)vcaa%%9&z@ag%+hzK0U=;rl}F&&5LR-QRQd-CXxREZl8zc!kJ2Iup9ZB+Sa zh8iOq;my_Y!J$IKVc$4wy&b~MuXMy{TEIQep3(V2#hd%|EfaRkM+?Og^vnNv0axm} zIafW}eAkt(?=I&e6Q4e+6ym?4JId%kZ?M+5SYE%_9F>85{V+t|$(mn}_LGM~lA>?z zaL4O=9B2ko1;0Cc3O9XE-@(jUl^-)0_{DsqLrir~lzc1s?#^P+9S|_6y>*$MlbkOC!jo$RW z_2hK0(ks2Y1DOdtpx&pja5s~IPmgbuDM=Pl`!{xt^>9=-w)RRL7P*VGPjsu5Uw`a@ z$U0Zzax$}F1J*8^G?Qgv-`M760 z&@>P=AoTy9Sf57ppZ_%*&RzWvYyJXLZf#+rXm&cP_8JlcI%vJ63+@bF%wt+CvhVir zP8PB4$|p~mfwJ!Uyd-IrI=+wX&_8d%nXkziW2Wlfr?2V*>kK$OYYpQ9TJJySP?eaS zb4;6{u5B?IUk>c*XM<253$Taxv&ZJ_ra?~;Sik2n6m3e!L%aYD_o#PL!FS`Q_GF%b zoS;He`RraR-tk$cHUjyQtsMGJ-d#og$3NXgPeAtml*#?H>i-o()aAlE?&fcL7Uw3L zaO_ScnHnJeY`qwYehbL?XKH;Oz{v~ozOQjOR8Ht<-y%^J+g>}VeRwxNZ~0toFzE|~ z92#yXVc4F6os^>}ThQ>Y@9Zt-ye(f&oxD-T+Ph0Vu?C61m$z#Tvs@=~SJl5p_%@Bv zw}E`Z#L~29F#~N~*-)xT{&A|ZbX9#AA=~^4%V9h|o4{`@iRzmQijQ$Ve2XtB z{&=|{c2dLU(S+MfoX!4xVZNOe{?X^yPmYW0)O%B36hu zq(*Cc2LakDHG#d0;4`_Tc#uE#eU%lPJ`bHtM(G2-*XC4hs`XqpJ8H!fKDbofVAy=D z?jt-Z_#b^(fKBF5dJnCR1?dF?}1y zXmUdVb$Jr^_7cOM^&RyalUB=uF8*6j-uwj4Io{m*D?Z)@Z0B3@_&V&dHMZcdO>Vir z{g+e-DQsPI+JJ7|{SDwA*+^d|CeL>o%{d9^cJe<(iC!};lAbQ`;PHc;C&Ol0jnv}*v!CGUG4cQ=aO+7iY99Aq@) zo^z=G@9gzjiMF6?q;FTjFIjIp^yss7_AU+fl+jPjw;7+p!<&l3oxPuvQvFNfq*$kN zCFg%O4Cu7h>8=WXu1lVk+T^r;>G#eFs#Jj zd9tvr6%PrXY_g`vdUw>gZD`g$UuC@Uunjc^8`Bv4$Rp*!9c=?P{rr#Lm5iKc@+RVXW+#-NJ>yCsJx(h8AEw%~y|?{uksqsCYtwIs#R93H+;BwMPNTjPqiU2MerI+k1YZS3@Y z>e6^?35|z&7lo5QKk<4N`*MCD_V$@%H5AoxC?N12(*}seG+M7SWF)V1m#gZ!X)Uh|+xcBmwypt;qnE1V%D^}ac zEDncGthQmKO6=Zx{f)#(R@A^Sy@PPpJR2XO7=|8flH z^>6m3K7N=fsmQD^X4=1xH#ob{anwn+%5JC(#E1U$K>K0G?p&}x;yY7OvYUeWGqk@< zZ|LY26$~2)hHpN6ct3pCruh8h`{m2ULJ!}*aii0Qed81MlRQ)WpT3S?&TZ*R$FGt^ z{RBTO`tY9(c^r#3fvd=mT33+tci7`s9m&FdRGht*pAHwNbtfgReq2QDE^jvIIt$fW zFUgLtCaBKO%M;z@lGy2Y9+98jQk(|NTVd?c!f&s3alXsc>1r_F)-|y-q;q+|j-L_o z2af#EC)UUrU1b~J0y`VNld%YVV9#nL?AGiT!u1!F3x3MG^Yd*m48ZyY zc)`EFw>mGS)Sv(3Zze?Y{&(YF`cEbTEV&JnkELxC?_S{WZ=x8F?@G-H+f~yx31)Dj z?y%ioBN@~POI87>PhAjt`PX7h?S#W4a{R5Nsk>zFd%#BNjm*wcdec?im2(OfOLv<7 z(keuQhoW!N8K8KSB~K?4i-`e=Or1g2hcMT(^Z%!u1hHTI#)WT}?-KLc^r7t}hrPOR zt+GC&Rm+Aw93Cv+B${s*uf;4OKlhB<6vR&?n-s!={6RV+W8t!YkEigzk)PZ2ioy_- zog%hC(21v)08dp7a;W&k>n%m&yN$lSX^XEcOUXYHo*ucjnD8H7zs7w^U+j+8UA-+% z>rZF%;AeC6CJ$%LhF?xYto%;9CPd4H&sJnPV(y%8CV06KeJ6|fFTWyGhA~-F=9j&z zFAdq`(@1CJivlX^`{RShGmciAi~orkg_EppT)w;{U(>w$`n44-x2V;hJb3yh`nWGA zPxoOVWND!!nWu~IuIpYpVXpBr_p~eP|Ecl)vghjqXA9_f z<<2GS#ppKM%NwJ#I6C@;_KsEnT^3rp)GsPAEDxh)=kyeV)~{O=j1Pp#!m-*~X}pV~ ze?&iy>%7(G?A`-8KSB|K_|@LWMbYS$bJPFUI~Q)VNq?2&4IB9eWcitlai+G)8{fl8 zc7CUI5sT;UsJ;=1;h@lQF2X?`=by4j7pA^2Sy2j%-YHK!3G& zhRSjA^vS@l{Z8?shrbT{Nv^isaqfV5e@w!F-(JQ*Sq^4vu`x#g>S!$3x;3`>RS@ai z&5r`Uy+o{Aw*V;4w5opRjQiy6bnPSSI^-A!$=_ZD9%z6%r+@9e8uf>5NUXX?5M>nKn9dGSz68kN(Q~>f2>QEa=;bCsf%V>vV)=SF!3k7Q}*(y*=Q6 zl-}vMNr8*od){CkN`?r4Rqk%b9ufUXh7At<@zJf|p4h8x!<7!P6Dbq_i#z-)Y`prc zJza}?42ui7DL8xN#DC?9zmXV#SzDqUi}#rbraxNJ_~Jc4R3Y~>AAalCHD30fFF1F5 zy*4s?VFcdY>C;)?Y*x$5URo!E@|EL>H-l&M#_V8~8yp0-<89ii z7L=U;&{MFJyN{93A8r1R?=aHs>JNCC%`$dw{ll=^ko2f@OCta_)y8d`h z8t_Imqo`XG+Q?L(U=kEgRf25+{Lh>r}ERpdoNFb{rWbC$MV>3cut5a z8y=W0Pq7$8T=(kE6S@TyV!4V=S$AWS{W(#=6?aw^+pg z*FJA^;z!@QL4gdEcIi9alVX~M#Kc8Q38KgIMfVp#juQ2^2aNJVtM)x(JwKi@paeY$ zd^~nPRLWIj2g5}I#%&`<`Qpyz3m*WOe-&-(LqFT-IQ!`j!sH+B$&u1`u4t{e!9upg z$qp}IIwPM?^otK+o_^zYs_q@nukz78N~?B4i{E$hm#39`7jv!8Zq?1lkCeU~^YhtW zlw-GUY_nT^aivB%2ua*Ju~5Y0=yjHkZ&P|gl%VyNq2Kjczr?_pQOy@+=c5?q*mu5K zK1Gx7@OpiD^IsCa_NRwQ8ZUx!?_&KZ*LCCh*@UehF<4xJ0?n-nuuR=}Huq{TM!?hG z+Hg4!7uj&;^X5?bzd6^5Y&XJ8GRj9kLwuG07ln{aC)KS1JuBs?xNu(n*S`EbI=O#_ zWshRegG`+}A5!cxJ|I;G3QtUX*2AQuefE1x0fYWha-}wBIc1SnH=fu9$VbQ&{JSsi z-bc(e_ffR|xY(HkRow9ugzaxrvX2R8ynmw20^Mv8Qq-3r53ob}EjRa2XatE}ai?>O z?lpIkVQJW4NSCrdis`tdVJ0$jCaK%_6NqppwgxSlKaMeOnXa3^rD^zW@I-hgs=9o~ zk*mA!<7Ov53A2yW0xFssq+d49tI|=8@pK7mvW{m|@Cj=!+3YlA^PX_HL-;*$= zEG>qQACymi3pz|w{;*`gqP#rI?s*=a9voA#WCZ_bA-qUP@ZOrOk-%^{3@yewH z%EEai&aig;if|v#jlCXR-yLe60YKzzz;k-G0nffQWBAsC&%Bu7yP%o9+S$6C$OxTJ zw;J8_>G-iVFp#V0>%FQN+hpy^JLa)H-o&knDub3-y;gQ8)@zzn^n^AJ;{%u=m#!Seon5BE;R{IbbTN+L|{8Y=! ziF7>9XRuy#joMwtR-te5;^ajz8oK8_|B(;kX|rlARR33B5pr3FF@iapMl|BPh?wrE z=5lO@$nj8KzJ@)W`vFQ{cP*`{f;p9_hwt}=j0ZnlRj|4rjCF8E0^Zle}iS%@T!aGd4Hd79sW5<5fF-c<<_zq8*3QF+z8Ph^T!`u z^;>iGQ!#Wm*oW`g!23*KT`_jPTa}_+Xpv?s&}=R>8?Q4NlIZ-b^K@0eIG^s>eq(Mx zzH~nQG21-++21c*{HX#}E^M8AJ`WsQ#$>%`2i?Dj6(jS0pIFA{)2)jwXwV*F{BM=5 zk}kX%BtNWl^rB0FN0CSBUia*|IzhzVX0!TdM?3vz`PuHUv1=Yk`RfZ`nm*k|RXlGT zuJYG(?GxD~&KFpU!t~M8&lWvcXoBtO{7c^x1N$@J=pUQ><*#8s_4Qq48^Ot?I1tLn_ZomHl#<@!lH*F}k-q%8hS1SIh2Wtcj9qi7ht$uVT?fF-I zcdO!C8anAz^nQ1mV8crlyasbg?aYmsq+R+F+hhZ*@@-CMk4>OjKuxmyr3*NC&MsY~ zqb{!W92&WT02KpA~j*{d`RJPYEC|4cnaZERI0Wfj+u?;RA z!Cf9yXW{e7-6oXrCB(LJ>U&SuZPIS@Eq6RV=DV_f}{y`UCvwqWjn$GibqojyL%^tr+Q zcmg^L^XFmTW1hXInyz3V^v4fKdJGRNQ*lEa5-C4 zm%q!;`1)Pn4%mhQYLXP4E?v9eJB_ouv5(FkJgsB)X=)?I`QPItNUE1%wEo+ z*CwsmnVsH;Gh2&+VHZ~;pwpY*?T)l7qhqW}e+1v|z_G0bd#23Bt8-1pt^aN>!?Cd% z_Uhr#mKS_oPS6>jXnGv$GUED&?;n@%!))bC*X&H_V#___Hf(fMKifxR&R6pZBr%>i zZf{Xo=28A0%q-DmeI`@cdeD3^H`qFfh3)xofTpm%JgBZIL|eff3$J`f^EVZaaQ(v? zGQ6RP$OmN?fU5W!pUth5x`|le@WRVo&8t4TTha6zF{v>TWg>Er~~BaQ95I0 z4qC$&W8DQr-MLEn^Iy&PY==Q+?R*3%La+U(SNq~mcGCUf&DivzJlo?o(WY_gC-3_o zMTf2|W8XP?7vU*d4#i^3R{!7s&;PDEX2<`9@u}HEz=YFD^=_ZiPkS#xxR7KlI{uHg zzo?9Df80Wrjrtd40sXiC8|}ncAi==1Ghvl<(R&Hjz%{^3UJ(%e-a@s(zX2LwWM5vX zA8rY^I|>p55iOrOY#<%A_K{5W4NSqbkS(VC$FaK=aa&Z=xi9-3UK(a?32~D?^pPQ5 zd@>9_JGX&4k#Bw2iEui?UmP357Sh_fZUK6V^smbL$D?i`P9ji+b_%5xT}K3q>Zk zs4ORRq-zt21ir(kj>7Ja#e^H*9QrLQ9#@A~yC@BDl+(M;y%pb0kfTVCoQbR4>-AD= zoo|E=lW~c6xkGg?sjb2%sXrfe;B&0pV@C`I4gBD>z+#?~7hYbIHI&3XLdhkm%k zy|!0B;;!8Ls*8_J!Ez~*=iSI|i~x(^<^2O>}MmTX;;(&8sX?4^enen9HVnK)E9kJi1+~HL*{FI9w zub$-3+J(~VYm?xwe2wm8ymQ2F`e9&najou1M7bk7q)+#9A)#%g>n6+GT=UJ7DLex= zX6K1dYc8K-Rvw6p!LPhp*YjMGe>Vj_%uq4P?w1p+4J}iZt+9*XwY^&l#CGTU>5rHo z8hIO%>2c1ee1194FV5?w`*`t8u6Oa8X?sLqj>%V(#peU9 zr+fZc)D^J~)Gq!4ZA=Y`N8z4{V;e8sr+vugEGH!+Ly>(|LXD0_R)B@eNuhjxoZPb$2PcK7^GtR zklh)qv9HAWVmv?M%^e<{o8cCl2ph@#^0-KDvJ_<&(8R=*}H~@l|&VlGJs5=4?OQECuoZXc!Bt8mE z?`Sp80Y z2UU1n5ty(jcVbT?dxR=~+xuH|;Q40l^qySSfAs$Rw*YcniABFETnIs3GB|#V)NIym zQ2BvA_9U*DlouU|Ll13^-S1k>ggOh?!|G!4@CvkRSIj$$#RjeeN&0^wDnFNR_ zGGg_o&cx!rk$A$N4)M1@eBXOGhpDEpzO>EWKN=9vPSCeNtY0I)Y!_Vi})_$ zB9P}lByz=k+g%X~2z1;>yLhcvNC6Q87@auDS}V~M6!F0866w!&IPyuQWPU z^X)`FlRtj{@Wnvy#cCoy_}CR2nmz?LuWz&V>DY0B;BVzj|KMH>PRsD*Ql)v8%;wS) zA!}apapgOCUMnm&u2lVcesJ5Wi4dKcj;qG^G_Acfj&OR4n9dk4_KaLC+*M!fzrcs} z^78b)tsH?BFsT}kyJ4vGMnqRGGDhj?n4d$rydJiKz5h{>zO8aE7WCXyuOpGor3>PG z_@$Gj%N-m0SsNJn^73a$E?4BG0Wl+2rke^HkDsk=O0{R6)>CTz<`8xgi$B`dV3fDY)JAxxX`k_4pWl<)qVe=!oPunm(Jui&wbDrMZ zkEq-Gb7S*$cZ;8>r0Y6Ka7RQ~ezHM4EVqKS(;oMO5?QEg*1 zCE0M}&AKcP_w$ltO9Mr*{=R{it62Wkoyk3>Ls#X6;?^8K{*V8$?+8!9?weqlpjm6- z{&RzPSE4NV*oM;r)~V7NqR&+R)^P0K;N;By2qZ0nx6*NU6$e=22>dx>V|sVG0E@=@ z8+>i*V_hFzU+{fAdp=(xjt7GYH?s+baS7=-ZmJqQ*yYg58^BXG`3qC;@%7vAC0&#! z_XZ#PEdnq;ThZuepIwQum<&+5E=v&-&rJbQfb^ymlvKHlOL$G^`bhF?^m1JXY{J-YE4B%v5yZtaGkJb7wCz#X0%a z%}GjPH@B8d;w`gI-`3(E&GF1uN+_z~oOBzHnmc*KkbV%JYPI+ut?G?Z80b)njd+@~ z+ek%{VS244@|J(UPys>kbbT>nzJI#0@!kd=h+aF$8~iESr%Bm}K>?)}TIokT)|1`oN`8>1zqhcSzQm@Bcw@)W>Alf}6@59> zkv$Y;c#FUDp>g`zRI-%*yX;|S&E>^bteN;<$3yG;B+JUq#`Y$7D`K*Z6TgPBetJ`W zH()+H;Vx16?L4x4F)(sv{HNA;5R>q4V1d4I6<~m|>@R z*m{9HsPS(cFYfAp{I~!1UjkTX zZ}>PhLNpaYBX9{Wbv`52OIq{=G(Lk)*Z9>q)oVH!80{rU^xZ`goO*sv?!JUKR*u7C zK;3EDPB?-{I`j^sL$n} zydi@EXK%B9_50tehLbs1m3toW>N=Lk`Ec6#IBk#Ni#^pbQxbzX^O?2xt+Fq#h+Y2_ zFg$GDmAp4UsCw~x@O7>H{CmN8V0$LBe;IpGk39@@UT&Q1T5~wL;-&xZvvs5L<;z`I z#y#bS&%Lu_zSARS`*5^;TO9datks7`AQExj zpk-&FY}mm6{wc`#jZ6A`(f*1ABOGjfxs^46D& zhxsVxd`n~!_|U#J8Rot^r{(Zn{(j#AZv%BF_$1y2kipNWkF5V}u>!oG8G0ah<*6=- z9?%&4Ub`UtYad}>OzH4!p_4UdoiuDn{^jy1-KV9hktuLK(q_-g3MI&N{A})6i5E@q z@5EZ=0Mz?R;z^Isi9t~24-nC}+Ygh060!~Qb#0&wG<<^lOuk2F4fi?BhyAyoW=`JGLlm|Edk@*BM4E?o409iHvzjc8`CXW z<4+H6A3Zpk0_E$QRQHHPT#)@PzloI&IeQzZn@n`;+a#@jelY@Td87QX1r+h@i>qj5 z7RhfVO=POIwcKPhW~ts?CUkr9U1i4SV}O;Ee0PT3zu(m$dv#AHZ8n?ontnwDmxtBi z#pw9De_~C<-7Ool=^DQ5)i8gyDbpu#bVUd27Nm>bm$wv%s=|L4V{%T;Y_S2c)(?nr zzPG6-`|PMO=GcKHv2@%Ui@f$zRN)e;p|>p8pj_c-sLwvCzN5NH6g{nX~xe*@+-|WpRN0yXXwv z`okeOmo7&B6{;4aJC53TSnhY{tbX%-o5Ji5Q>Mu8uTQJ9?9&fNFY!xdSdTE;Ais{0 zZ#=3_Mi8-VUT6PwLA*K3m7gMy z2ahhlR7W^YvDY@=ulPob%imik7Z1#MTr8)+^l{ElRY#35gc~M)eDOB|UT?70vU*!j zzZ(m;wvq#`oLM3r7F8Eo@&kl4_mjRkWQ5Q5=417=iAAeZ9O8_R*(7Qyipkp@u0z07#iNW5YH}Ek5S3if8?|a_pVW5!_i(b9^ z-Ny9noCuz5ckrC!20k7Gk5;F*AN^+sDnVq|<`P3MdQ>T%?bojJPUP#3E&B-#>mqY} zjll-{0u)be?um}UW23pX0sip$NbbXk-!ml2J$o17!(puF50VMGVZXaDvE9{L$M_A> zCueTXHgRr+mw(^jTBPPrA|}TYqJF%JtH!IIoWD++eY%=N|88+N8i`lDf81KN|Ku;o zM4;D_G-~Z#g?OIAIMKsk>?RxIu9jSZ-G5=J>&RPpSXv^o5Z#8;q?PRdHHU>-6t`g) zPj+G)2Et0bzPd(i_G}y2ls%mxQrq}(i9SOO4<3GwJDH8;T2UKdU+}#=pS<{T>GUO7 zUr3@YH_{i!^t})~Ub^s#|HpIBcIJE%jeqmZodsoctm8|wX+*aa0dW2ta^o%LHm=6J z>U`5XzJPJl;%h^39|xY_9uI-@mrQm3o(u8jO&??Y$sPCj?rUBS$&COD&z_;FVVvk# z*?cHgVRRuQ2H=w2CjxJ+VQzV~{(2kZ`K2S?flQFBGQ7yo&Umluc&p1xqr~RMf>SCc zkvz4yZvtyu+9lkywRd(_j;pVr2kRS3d zC#%UPwkiQN5A=;^a4*je&Ads*xI!l{Ys@PLH2?q%l1W5CRPm|fcOkz{?Z>me*E+i> zmn&u%*H(K=Z&7MsT&^`4`= z7>u`svQ)y$Pg{+R&0Ic3=E(2Gm|aNdslH@RN5swJU+|VkGw@<~qr+Ah%PsRsu}1C- zU)*&I^Edy$y$uMz=iuFFAs2@DnVhewdo5aPoQ19NQSSRvKaLyc$)?9G3T5qAhf3S1 zh_U11z4L4}etfU_B0pV1%VoJ+XYtw%h^k#)SV{{CA3GKZRu>-K_569Ucxxx!Ll%ns zwDu0)8x;P37f-y}x1l(`v0t3Uhk>Qmc}FL>=2I#vcU0XbKDzzL_wl-s7Ojm(ay!dy ztnLDm58p=qujRvH6m2FMnzZ~ozt$hQ(_K7}pIRthqIh9Q1&$G^X-?^ts;dNv+BiSUO9B{_6H06P$?wrQDu!eRYc-0^T4-|!y#4Q2|@ZiA7L`^)4Q!}h0r2U0fSq!X7Vc$YL{~o^S^(K)e{rPJ&rYrhJhT|p8Zl$AXXZtpc z3&(7JK8x93i`s|B{MATmv9z#mp`b^hF8>t1v3j@QzXj|0o!&#_J-&RmI2$461F@tZ&O<{+mVHIPaijFR1NnWN zzlj__bi2z-S~0e)V51CUaF;J0~TU#<`uz4j{GNV%ZNyKO{{7oU^UHq){4*LYA* zbYqIA`j79`ua}wZ^inJ|JNX2{;^MBqu>NQ5ow(xLN#AiAlPwN=-Za|Tpy(j)>zt;z zSS$`WuKcrj+{qkEIv$5K09fk)0+x=ez98Fyb41Oiyc*Dx9DS-?z3xDh~4uy z=9%tRs(8kca{9kb*m5v}xaoIX#^UKm3@nOFSl3WFBQ?H12E%v$%|c(rTnq$cRZ904mc^K=M9><$EDN#?kd1C$zd;A-ddTpd&6jLL0 z<;PIbc&~#(meV{Wm&$cjG~w zOgbuc&oIsNTku{nXEx_+?+b!s(7qKW%%tgMpR@h$)Di!3-Z&#ae@|_;(UP0JuTk6Z z9rTM`munL-9}=Nx?adRqM>1DWAEwcwi?|$|;PB-kgT?Ecvs;T}S&3)yd`%WF6hPe= z`$`w_ABwOnHVjtel04c5y~I^dq}tTVDGdkx@TlNdUk=FObnD{jXdNNlSLLjp z%{munddB^D5!2{>8d5|b7GF|6ATTo8>$8n}Yr?~`SjH}2-rMZFkw1OLJmBI4Cm;0> zLSoiyxIOF0zi*uFNrc>Z-``fudh(MVrqA!so^jsheXRKt@3;PKgRK2tSiW;nX0lW4 z>T|Aop&D@mry2#MZ?vn#qe&M9#+8jopg9ax+s%P)g@RoEmu-DmOFWNG-z9|RYSBzCSd(hzy%Jg1J#*TjXY_WBzUhzeQ-f^t#+0BV#Ke4$~i30H0V9PwzCTjDvwl5IMMcR6ryB#A|ou7&XVC(>d> zuSNPwaVy?4=hdCL|{FzL4H%9Kx)$yZ;`d(DY zCDU*}VwjAN**$x>kM?UD+5I~ve&jQ=n_Jo`;wPH2dbAsN-T&?`ZF(D%OeNQ)TNU{+ z89=H_rCWHWrp6s-tNwW~bDzB${VpfQnhwmfo&Rqfc9BtxKZL^_+o35ZxI8=;Ln^kI zqdmP-`_qaTfj+*`gKv3^`o(9m@lmeuF@8w6EOqqE-aB3kbA38~D>gz~Kod&U#iM@d zqs@%iADW?CPV|R`%lloN#LxDX5}J)Ck7f+ul}pRw^kGpPZ=B(OWBGb7dmrZXosDUj z&iN|Yl&5gxS>uQI*cq7upG4qUnRC_XrhtwF^;^eH%`dmfuRh*7a4}7<@|1p?$@!nR z);T!Oad%6^7NHpI(A&c{(BXxBQzo5Gf45#UVvTM^gN1>uLytci-9C2RNwGRiOpw$3 z@S`}Nj3sajQAW*6dlPh+jMvub23jA48^#=h>mY|MZ8~y6*rPr3ev_+fZ31CHXNRDBx_Xf z`y>%374c29DHZEF%K4cdjsHk8*j^)^;S*aFsyeE$QDwH`8DDSqC;Ju?ygK>m$~Q?q z@2w5AF>cFPld|Gjdpy;bTbhiFe$^MgXCIQ|an7-km)~M39`-|2yNhlAC69e6_%1Fx z4iSabtvf$9Yb$%yXSM1dw#BL_JF*^K-yg=MU-5RH{ZkKvyZJZ66w5sv>|H1co&$nR znY(OlIR|=4Mec!FUlH=wE2nZ55@_aD|J(%&}oPk-tHRucIy0;As~VgiRh zu7Ht^ZvxhgzmSe6CmbnS;ZNL=T?LWz?ju0;e^OFEQ#4H{Dr{_F2%=;jx*x1j;^WIxv&pt~jW#k?&-IIU z@k#-d1h&Y0?sojwE?uEG5HM{~fi2llC3S47Y-v)A*Hs$<{&vHAdZSdk*%+kY;VP(BJWBycaqKoO}8m_d=5BzM~d=De~ zekL~GK#^OERDu`h7?srjY?y2A#1oD^>#_i+Em_tVml|_z`R`=Ge!6#ge^G#A$gk^% z2Ort_l&C?pS=ZjaQRH zj>~Th_nXe>)}d&pHo@+^uutc3={PMN|G(vA`vVtim+t8@BAHcx^BCc%r{g12Jvx9D zvHU%6rzpA;!gFya139Pfi-!x=&o7f__LG5cdg<=9HOCo6n+j)+3h~8h$x^-a6(5ni zJUcx%Ue6Px;z!TcrkY9ZWuES&QCsTR53js?5L-V^?jJ1-8^5t1 zmTk1sH5Yfhu_Ql#();J`TzoMY-@&UtZITmqgE1Tfx^sXTw{gIKmSN~na=2MEsQ{Og=^@;Cv zeZteA5mVf7Oe#KmErpD@;;~f0w}PjajfXt=M@O>z)T7|CX>H#Iw2Eh~r{7MvU!OX_ zcscZ^|M*W}1k+N4|ue z$<23hxIXsnORQ1L8BD$QHxJKGI`Y}_GI&-_+*xq|0000M5^s>6uZ4gAX=$+_9bi2CMYxKU0)%)sJ zUEbX9{XFMa{`k&!&YUx6=FH4>&1bIb6REAKMEr#22^JO>v5K<7M=UIy1S~AtFBTMlmx9r?sV4=8qz7svc^9wbf9yq3Bj?nxbOZqX<;U`8=z znvuCfa2ekJt8&H9^R}V7`q(o6KgwuRv|Lp!{(n8vj8@p4 zgPVGLdviIal%!7MQQeI9t}VAqN6R)gHgK(s2dz&FoOS=7QU3J-)Zhi>6AgN|?VB1Z zIE~;?pzf$7>$0zF7@^zkyC{@fL?y*#IbuPHaayx2ZE2Y_r~4F*UhZ8YgI-=-y;?BR#1+`{lG9#d%i)};!saSE~(Rmz{C>L||;VjOpb?eNwL7e_< z7S!3m>vXx?0-4!ljGFht0W2-w|FuNajhf~uuYe(e8<{SSmy2QImG^gZt4=i3cDV%CEYjJ#la##y z_8`q?@4ed`xGn!sMt|CP1c`f?0z0Hi*j{=Wp<4ETp4vF=1ucg?@cP~gB6P+adbSo? zqDQjB+MBb_c0%Dt;G<-eBh|g8G2*$ot>)C^{~Rc8*Te(m`&k!^p7_Jv8N$pTMIn`H z;;wSrMb-Y+(7?}S!|ryC3O1kfZx7#5)}utnd`EveHpC%scGls7nk_*B4;We`@Dka( zwtBe3Z+265zI02AZKMw`0GoMDHlEHUTC`!bD z^WrQN*y1JbDt}@&QevgkHd%G}CP#K41bLIK1=Y{JxCSi}flDeDd}Y7QmrJh1!w~SM zD&PZ#Xv_iTF_x?rT8==rp(rXOkCRc(w>;3QmR~W-1NBD1nJulWqbO`rCsdOQMxo`7 zS{mtGAfXR#Yr8ZNWV8b`-;u(zUA9%-+c9I8tx$+kYaabKK3+rIY`t5}*oV6W{>V?# zf9~ri!D{xuD*EAG!|n6$KPfrYbpeEr>%IO<6b9r(f3D?DmyUwG{m1;5r_f0711-zS z&BZ<0_)_YSx3u8usSFtgg*Mem_0sRFxyR)yRzgy9jqPN-_Cwr0s& ziwFpwvh<0sixaH9etwImgU{X`dY+!^a^~RyoWY0pWSki{xB~;?vf-(eYB#KQs?J`% zA+3mX)>ciwl~DMJR=}EO>I0>3Xs5Q~Pv7%2`M8+6P8l|9V<-Qm^>3ifWY`SlmiMEe zC2!dD{gH5c^%9y;%l=X%0LaI|;CcQUfs3Gp6OCb<#8;02M$m<1fL)a>6R0bb5so&U zZ;?bp^r8KAo@u#C1YwMX^w5xZhjK4xq!-rPQ8{rw3XUaa{0huHp(5po^v6M|FW#3b+DIawBt2qKuK!S*&`ECA44m={OgE; zo5Sp8GS!{XuRCL?W;=)g+OoLGi{kZ`s9rj$?naO|_mZ^VcA`4OmuujM?)}j1zh4jm z3&o&5l{k9>I#-!?_O`t{&~*>5KIAgnjVyIb%V8VfspEgk*^lM%v1R9XFPd~4_Ty$9 zQ9cSQ4ZOJ%WVg52;--v zEwEjc*!>mgPNm8rxBaCdgRVQB#~n3lD%FMLFM62I!4~#Xv^o0UMAXGMxi=Qu4`BSe zZ)q#ac&Kb`Gc)C>0OyyeTKY4fg+)7RzP$~|4ye&3pKnJYP$3?g_5_RfXcT(j`u7>% zvP$d-a(UV;+POh`=M_B8e%q0ZB)#$Wa`kt=)40O)J)jsNZNqafQoU9?W;SAsv5a2eBBDG`B z_L6NGf}&C(b+PQA<)8_LzG2~Pc@xRvY~jd>Ug)mV7gY2mWQ5#rxD_0*L*I0mjspZ4chquzOvGYeo&8^MF)(K`?X9E3@hLV)d zNCuA~QB*C(``Z&tlX%VB-fXx(4ekT{i}?uh^>qT}uJI5EImCIgvu_DewD!TC# z)Ph)w?H$y(l2QY8^zn|&Rv<+9ks|T-_h4`%z5fw=gIbyI)l?1weSN+U7atAq6L%SI zLLvA(PmLWOZilxaap2Z}*T$(Y{n0G2e^M^an6~r z`x^9G-Ppg7;$#{G=c#f!y6P5&He}tOgRU#2rtj|}BRZUFtjYNMc~>*w9*3jz#Srh- z>j#I+yIwVfDEOQJWfAdb-mTA@R|t91#SXoM79h&1e0^^LV01ocYwDp|a)a@q)jIJo zgRegE=z0e|DzD`PzRm&I1A=GI7z2+$9S^O4Bo8`7DM|dkN4_u$@7fcHaZC!&4jSiu zSiXeYfb7-%4;sJ-L`+6SDm3vZCJVKS`r`pEL7k0UHXtQ74?3QVL~?kE)0H=-;Lv1=dS$CL!(A8g2~4 z_anXSyyq_yKsO#z^Fa?M5fI#_th>mw98%t0`Gcf~gXae(fspKe92DilW)m=Q2L&Yq z#^p9sQHJ%}wlC-S<_02Cd3`(_wZtLWKG%#;;h>!})SJWXJL%jutnVcd|D_-(s%$LZ@;~;ltUNfH; zOk3OO2LsUyCG-KuohWES8xZt*_G36>5yXk+ax?|eP*2)cOc{^rfjPPOUPQrZmwlbZ zKONWI7n?R0nWyH?`38u4Po=`=VE4129kY+_*PnLZFa+KXPD~-rPl<3u+dvVNjDUX7q;brwuM&-1<;KM-pw4=Tb^g+cxwcZtnX%7ryj? zH<8xJ>340(o9~8)ce5EC_jb?U zU?4sPWyH?BEZB4cRjRjT0JJwEVPJRvAzg-v!-2MZM>Z}AXWy&PCZ{EM`U8p_7YZ-9YL`I2pm+({H!ImHR@R0zh>qQR3ew63ffr% zT}*nzOGMelPxKjQf@ZRZi1-{H>Mhk;t^(fVqQ^nE>5Ma+Vkv2;dT-O|<>UO42HUpt z>7H?0*d2;?r==X>KW6iMLDMPo{!S4kEA`0d6l@RnUCTpF)|nxtJT^xxGkbnCo1PA# zS6-92DABno1d>YUWBR!GxGSpWs^HNsvfi-gRGiZ7ZTtBykg43T5i`sz2-cxyAEMJ%5CkYKOCQ}FrK@jipjD^ert(;(#bkF zh?OaL+v!43Rk!=G{e%-W@o-fQJ(@vUJ&d2n0nbQ4*t0F#TxqH9*Ec(%Xa}N{HmV9? zx{Jw6C_83x*#y7U@fKp4PK89msY@^*aXWht1oVOa3H7|!0;@rBgOFAFfVY$Xg&M;g zdbza?)#ME*JZxd|0j}quG8uzB{Cfepb5bXj8VjgM4jLz7WfV6G$X*o+jEO5a~q+P#=R}X^^T%>9+i+wyX8c1jm%sAqRbi&%gA`beR zOXCnMz5x{<^q3~~R=5wFckhke_q0g{|S->$!^U0{)Ws}#0v8;t~tv>{?k_`~%y%pEXgQ^xEB8sT^R|xE-e=U+?Q$`Y ze*g;9ge^Dh2e!2%?jUFjAG({62xutC3Anj5dx$yiw}_oWP{Hn(K*-GA_oj35fN|b{ z{wX$o(f*i<+=n`(J?+?-pk`WfnM@P4q_6GkT+bFjrqQS%}|E(Uzwv!O-@D| z{kY_xjhyz`Gk^3$3y%Cn5~teWZLvWwLJxs(p1BKu(Ppdt?X*Hek?5q0M`3}!l$Nwn znp0O1aZ%#$1qa6};6_s!l#1m0O$UG6l)!w(HCy2+qz zR@-_d)6;g)mUz>v$Y;EZZGYaCs_*dXnkFuyD~#b4hbJrOp@)-R^w}5~M{Bjc6?ii= z<#VCscE6g!V`5ZshpyC>ln~@?f}lf$Tbt;EM;ytJ6X@nC*?91daV_7z^pIn|%=f3_ zD5P`0xW?t)x75bZvhj63dCoz!n}>hBqP#u|9W)#1c?l7l>KaVt_JXbow9MNsLeLFV z0l$|{B*6=COXN5SV%YzawUFm;gS4!byXAd2SmabM>yMWOx?LB6)pd6-jWCm$Nza8( zGx$W2Ry*I1pnpazhG$>RHH-z_B%*%n1K_cNOYuOy0K*KgyCsRm)f^?+RaO+N^*-0& zG2__O))^2M3g-RvuwTZvzOgfohUT`6{PsB%)9@@Lb$4-=n9RQDUM8rV^OgcAHE$r0 zo6v8)@10!@*MecHYQl-Y=4=-T7?I2M&R>u>W06(D`~mI&jXT|(uZqRM__Dg7=J zcAdG%?%U9^!~=A^U*CjjdTtK}nXYSyCNsEx5?}2>I^Ald36>qd3dg;|GBi28pc~fO zj3vyh7#!RXHYdof*>MeH=8-P9=*gYF=nRw-_d&V(12g2Z>t{GnqHC;m+$ z3-Hf~KCW*dS>czCw{?w;^eqQVhk-pz6>S#@=xj!3*{X!)$SO(*7iCCb0^lJ0zD%BB zK31%e(#97sLZ;=@Von(2{F<#{W)m`%kFY1Co{z}_D5iNy?9!s1FsAKRwjY#3l-laF z^oNJ6dlvl6ohSz1aXL35LNhX>Y_m7K24{1?hk=L#t2|-PH|bmNXS?j|kuA`I*k=8e z{fH$=?@6jl7t(-}8q{CGpeo7aaNDdiI5bD__c}v|=8bNy!Uz*8>-NDDrsaiq<7{); ze%3a>zZo7u8jueu$r?YD0T|3bIHRbiF@r^mMF9h6h%xta%ii%(F^!v%LdM|wH;4bW z*nhc_UU9ztKR`F$v9-Lu>V>UKVSBdk1JO=VXAO6*Iv;) zTh6-u zsQ{-vFuThe48R5EI-mtXWBBT&0}~S`+)z|UHCpFS_9N*742t=9TiU=|tL*gv(akCZ z_^OB#-t@$UV0f(3P9a{W~W{HyFs#g`IG1RLqbT zrg8IJ=5eFIVbI=9fG~I%^w;CvUwf2q70k^INNq5&C!a-*=`$ zLTwFQ|EAt~Hzh|cOj^;AF4|h|ZOuKH+!koH*R`E)^h0%XZw4Z@<3!KeP>q!yXW&}( zb1JJQ3E6qn)1!kkKXj3c%fsrf3o;VU*m^67I+VV=0^K{_>-(Q`g5jEXF&=hcck%r{ zy70POOa`c2@(oC}*Lnfnye|(QjukJ=2HglETHum!@%!y-;ZAU?p&fzrIU(v9RRRq2 z9dz8cnMhf|m_2Vfbr_Z9BXO|V%Da~W9?$XZi$qn8?l?$o{RJJk^-%T3NC!$w4BViw z;~XyMNsSJ0kQd7^tocl*_!!48~sb!aE?=chJ zTlecfa%aRiFI4Gqdn?_YH4LPw+AbUWFRq>QmQE~6R*{m&M|B9iD!vPb;|=}#x%+-4 zD6s@4=xiQ;$x4Fyb;?Nliy~3tcG)0PU;&NVBGB}*DG^cQ!rdHkhjxTNoE^Mw3=EAG z*Z3a~!r90?eC{GT-d|XSrvTu)nT*nK@zbSDxOV|g4dE4%u3hah!g!g(zpAIy^d}Tt|zxquri`6kVf$!ytl9#<1*F_w5|orsyHoMIPg<(x4Z`BY$f`TY^)H zpxL)it@&FlwCuX;*GH<;5RJ~pEqerKt-rS@7ms>2V5kfb#XpfhRL$Lq$0RdLewj8f z8e@YJJSR{|EHoY&p1_Jlda4j0Ys`|^nVrGYJZt@L+e1luT z`NTk4sIi@39c>LcDZmS(pXyS7ARW@^^f04AbmdLkm)l*n55-htuuL!KkMtJRl<3)C z$)_?V1L?=)fKJA~mYsKWey)SAifMmrJl2QzXB#9hSN>SL5s+q{)85Nf>?@q>MH@C% z0_Q_puo_HeDm$$`{12AU%QF1@%7Ri$S@+&{>;IoX*dwO>fzq|1YHg z*0C;MTn4!<1(|BtdWb(<2IbsZ0G8VC*tP74sFCB|$QU3(?;Isj9PvNSl{fd>?$Onk zQr;1xnvJ|Pe49BXq(R4_zKwmUF@V5}nWwXeTA%ZT20k8drfbRN&F#SChV9KUVQ-q$ zQCa^d=>C9eGrX_vCK;AH=_2C$Ykg4Q8<}-< zAQ*V)k?VN>0Q6>yTa|`g$+>&8mF(ghn9scL`n^W{s#^S&w4rm$>s_Ojd&DG&hZ{W> z(KqdV^TkyB>Nz_E3BMQgHF_vsu)N=1KPzMT4{Th|49vIkqj;MK(VUBs=YUs!_y^8~ zN6|ttsmYnhH7?8IC}j=)|3w&$k$G{2?Vafu@U3xUQeYzSxg*C(()52!o4et<4vpH^ zqJOKC!5TZ>UhXG5-v5tt=`2R%ga{z^X40DaufyXyWoW*o8L^m2KGW>IRcxu71g7IH zAIkyLE#~$AasxKb%}MPX?7dv1_?Afj$UCe$su{aIyJ9nr7n zVUuPfw(qV~=VxmFgq!ZpPoQ646V{9jN}KO#^7y>o4jh z7!kv2QT+qR#{%McCYfnCrpAb8F6?pz?sH0vHs^W0`&ywps`EMxWC*klj7%@>UlFu@ z2a)|#TrW5~TI5(zLq3gVq~bG8*jO9H-eYD!*isHUsUqHLhJ$1sS=0B)9a)=wgdYX5 zVuE2bF06m|puvK%Toy`8=Z$rETZbSdU@H$NN1`udIm&dYy7~3PX^I$FW>1)&2S=mZ@d@7q$!xZdl z30LNT$VQJ$RN|wbEG9~&Rkf#AkNXpP)s9n&AYv-UA`BvmX(834?2>ajf7N2fHC`GQ zwRse;8*7~gv2Gss%QIxGmbNn+Hy9-x=gQ#7yrD^^;$-ok=>F+4{#4?^_vfQPeW_RZ ze{Y0Z3%*jk#s$8p_%d^S9Md)GnICnrLHbsiMoe*-%6AN3uMKkIPA1ouwAgPHzWllF z&N1|7mp3_UQgoTbqerEU6u8+^6vL^p!n}2yg2B+YdfoeR#R*Mcx?|rs1&!a7;T@hPU{&t93^;$L!XQ|MBYK?v9jcy+|ZaOI3*dX4os7s9~p0; zM1`X)ZbkOsoB?wGoq|fhD}O#H*9kX`2^ppu(R&I$zVR1Aw$a^xxz^cCh`M!hT8iO!fW+hXe~?b^R8etb#sV`qP2yFZ}hsi4TIQX6e$ePls#F|P^5M}`_k z`1H%|FQZ=*1CDw48aSKWc`OtxmC`@Xo#;l4*sN0JhS>~i&w6lw;(ew*{?a{-N?_Kl zHNL(2J=U1Zwc!S@;|wK}m&Uu^&YBHH`FNEfaf8gvRi^D8X#pbRbEj2YO}Shn8)zgC z#F4c>YwZ)1cL+WeS9BWtskf}A_UcOSXCT_qLqbwAKnO?C9&^MzK-o$EOzyiM^|efl z67_XS9@TLIZct;_kSx-7CTv#QAW!L;PBzPrW!H};Z^!hD0_&phiV~$d-e@jvb~0~~ zi-u1b2#|k>sNCR;_StvBRc9MI^xyv?^7`q^lT+fS0lK7~-*w9*lY*Vvj{D7jqZ2;d zDrAdjwFw2_A2YEZyX%sOyKfYINShDwy8xs!)7SgBkM!a%e5s%M&F&yS+QTV3$4a}<#!lpgTt**c|oxpzg=Skw-o2&2S zvcQSD;`69y#f!V#=-^+J<2UozJj&c#L(OnGA%eRNCYsm;+4nQ#{dkdX0#S?BEnmLS z9{$J{ii;{v_snpJ@LV%~P#!jNlfVyQyxuC&e^M?rw5ordq?lPY%#1G>-nZs=j(1xT zRNW2lSD&n$FQ%bU)c%lN<@)%A-J`!Mjw1LveZkyS!MN+^j*n?|6|2`TKlWuXE60F} zS5}w;TuUvwXx2n3W5SjtF81=kNya+v2{`SbXDSHgQcY0ser#0E%E^{5J}7s@NELl3w9m+TDH6v(sqlGF_8RJ#FiRSir5E2)>f8IeBR3Bbgf< zYAv#`X?y!3j;8qhA7NiPzo~4h@Us~(bZR;(GZlGWeVcx4r}|kR!zbn`Vrg2X2+K)T zv=W#~Jl{B+s6tJifNey19ckIGRSLZNNc+_vZ6wJW>ds+9j;eEinHJ&Ty!FtDfQ;E+ z7P=)_KVK|^Usb)tF4@L=2Kt3BizEgVewlw0Mj@@JrHy(;Y&jWQ6Pu<-ux^rRH_U>) zPB+6@i7v@Hel;M5!*lAHv88mdo9Lcf|K|SSh{>L!qmqz2$~HlMi$*|nwIYK_&b)UV zQM}M&&14&!7d!F6;(D**M~r5l>Ulk%e(R&B*jtGc4Dqi5#Tthu@Suz>oS!h>=Z?3} ztW-iG3j`ux`dVDBMSEd^@OoD5W(;aMOI#mXa{F=puVDCJVb02Ga zYg3c*qszf6bV0W7lPBwz+y~j=6Z?YEY#dIJpEFqIE6PMPj|IeHJ=ywRAp@U;eViXe z>a(k=Mf}B+;T;YQiPe%0tt7KgQ~#KUQ%E5j{Nsn}-{~lui1P$3!B#qx5&i5Y_rtF- z2h*{I4A7V=6MbH@<`=khjf1tNx+&gx)q07e%=Ae3JdY?UpF}Rji7w&yhoxSoywJwB zq~EU=SRzS2YR^dQ6@4DoE#e(ykLsKLWTM=ox&Jk-M;1DHt}jg$C?*Uf*{S0w4tn>U zhLqPXx$ISMbdkJ5NWz!S1Z+8sL*s9o{POOY(QIZRNE`XLJg4kX1$_Qfa^FGYe*dis zZUgJ$tVRe4cAuOuGiB*Y%|x0+{TbO0b$l+YX!+ak8yZ9POo2ob)$){Q=RTNnIyCXR z9a&w6S{_KEA8TuaL(y1bV{Vb97dma*Kqo4YHt{tMvL@%-@ z?APNfGGC0@IOE5=ThZ=1`MvM8-Cy-OG6`;A|4}0W5I?3rQRnrWnR2I;`NUO%Zm8@d z^f|6^a>Ap?qE;HA;AE%jgpgroWPLs!WSU&hgneP-%Y;xoXq(lLNR(!N9i6SiJ<0HdUsil3`&Rcs3~`mGo!>h^Wlb5TtWJvQFYG<+Q9 zqMQ`?XUCa2s$zPWv?qFX^f^qF`iIDTd0GNxboH7#8J^_tgBz2bO`Do7o)z>*-98yB z#y}b!w&s0yM2x)UJHJgOPUVi~t5G`+;(Ohcz}^j&9%TpH!A3VKN0$9_%!j9F$1YPU_SfI1LrQ%6+g|N)niBk4rUc-aFfh<4hP2-9-=xoUC3wNz!Yq{9&;BeIeq$J-)M>SK(NpNP3 zBr`z=9p=@_*989JZ;s2()K1wi_x>U;Vkutw)kNOU@R%^cqg2kivjeZML;jL-)^t>9 z8()mBobFVpVX-G4t-FF9fEzw3QeQvrrH3_gj1J#D%nmRu-YiHWa3otE6B_lIYS{{L zm_m%$KXhrT8Kw~ZSUA}(&0J+|S33xwc2@9`v$w&)DFD@by64TU9CfJSdD3}$xf9dj z;{a>{ZEGEG(@_HjS7MK`^I5)LiIbfgu{b=<;(oSCeReLUikDxh9ug%YQkyc>*CijA zy#XxmG!)auFF1I6I`CAU9ZBvGWW7N0?%*wg zz}DKGi8|CxyB|)RpkLKr1xiw&?w?>fW7K@>z8YP}OvM4?Q>h;7^Zd7DG$ndI^&{=uMCi}MVu3cy zExNT?ow%aC2u|4^slBhY=2>e*RQN;L^mta=Rp+Sh@R6|M>dEA$N{{EfRopCPwi@~< zcf245!a47%Sf8m_qcL~XAC*^65@V#r1 zxF@A#@$+ICRkpoU*R0o~mAmgBN0O;RUq#o*zw9TU5?QF8Zvn>u>NNrw22NKCd2t3r zVxAQ)1;@O&9ps~|iLE+V3$@}PRp8evRC)IaM?P%lO2qQDSFBfhlx+C`?NwGaQ-5^S zliw*>W9xK;22+XnD^=CW`jIh0zh}Ce7G85~3C>QYZ)_ILMI$?@)B>J#F&igaD@pK+ z^bXH@dre#Zb%9teY<^^OU32s)ZMKYMv^S3Z$H21YOm0B(MFK)Pk-IWiKp9kKkNgpZhpe}5D`%96vpv7>CC6q&-mQy4~e_<{S2VVL)q&pzxpcrgN! z9SH>1>dYCs7{PSMg4)E-{C^bfIj^*t$K{yLy>dwJtuyRFmSx@a7J|h*44mbqwpu;}`ZRhe8?yWw7SYisnxB2E-Nu zofI=W%*51=-1YMEk7qq{ESdRXvOhe2xKSFm$rvTJhqi9T-rqq?8JoP_pM371^Ua}C8J*gYm=mdZvS zH7pOfKC6)~weNL4TzwI)ih2{xsBlmgKJ;9FSGYb}O=BqUK(XzUH+GTAoedVjhovck zJmBxf$se1X-~!UDI_>!4Z|g2_tTZp{g-vpwaXkaf21yi+mF<%OZoPLBkS{;fpiWqw z1L=CdgLR9=CdvZrd7td#-f$r+pN z(=*BT^dF-OGwGH;+i-&ls6H<|t!d97e;ZuFObLnk5u!g-TWIZeH+aRBz)7t9b(~r_ zl=&wnS!{)^olJ2-w7K+mjXqN4G5wVlH`S|XE4Y1dsfH8(?)ACaT1lmt-CG*f=bevO>ot)p4pt8YbZ2}YSRroz6Zc#MA$;&7xA zk}H9Z=-)-=25EVi@^jquR#le%t=Kk=BZ{tO#l=@}B(Mt`>#)!HK=JN^so+V+*3(cB zAOGXTI(#EX{omndB6tG$4s=RpVn24l3beL9OXrH20EE}91ik^oGk?KO^GrQm1?zJ` z{7LmaA*H+6HQA^1j<(c;f<*iI1%5<$`SiXkvI*c$4s+kIs5cC< zr=QnQSd|nHepP`;ut=5d|4dPOLjPn)Ncd{};1MdsSzQy?>{F-2n}+vu=;Dd3bO3LG zjNnHsOI3fXF3rbP0VIoIR1PO|R;T)G<|g9`RCdwyG@CKG{+ZbWbz*#HKA})_l?s*F z+i{rtOMCjf3QrRJ7Jwm^@Cu^Ly40TnXH3Q&vj zHP%~ifpPy7hS#25=`>^5eqtH$O7mL1O=(foN`^B1+YB0Qv5{=1 z^%{WDBvu1mvrMzt(R@wXba~?^nHyTY^X7Sb{wU`o%Cro%&zr?GV{#obld z;&sls)7d<%Re1|lVdu2I-ZVpe6>i3wgRCD<>PPNhX^Hx*ZStSJ7Z4-)T>ui;5K4S* zDM45&I)v{Ut{LMvPx45(=;`ZWKR`(>QiMoqRiLc5M~6}6|&a()DaQxdq3kj`1;vZm{MDHl4NmY06T@~ zZz!CYLYFe?aXCuCq(MRdK&T!EZmEVi)lXlcB)_(n`Q_YKS2f_I{pR!P3l`huy!{oy zfXaiJ&5MEtu9vj4e#OoSFl$zRm3Ou}oH$h5uY`ezQjb&UQ*lPAnVA(oAikY(?I`ck zNv0go)x4G_*#dFv;)7$G=p3G&>bNALb)pqw@HzQa0SJ77Yeilns}p>=$nk5!{<$#u zQnhMvX4l7UI0bAzUuLjJ;)3*?+lu~9kY-OO?{OQTvLHWyHycm{@U4r!wW|5*eACgX z?AMeV`99H80=ve75+;hp!#~`Yvmx27*GGmhkUOm;wMtC2sr{EK@Fo;9$O3hHb;SK+ zUHBE5Zhc5gZ0D2r)Z#Mu1(a2hvRHKIce6bMI=4v!%70^B%!e9026D(t=EqM$$TeJt zgT}EzDX|&ZsuaI~@$Mk9mxc0CI!O+Y$4Dc(%ThhFq|9=XgkTxWw0>rpZ6ChM**hj` zSY)9yeHc6MMsSuWf3cG)Y=Z+#D4L@z9HluF3SEyR&kLYGD`Yq6jX~$D_m012a>5S% z`^DvUOk_deg9F)f#S}!xV*6b`g@>JW?Bb>Bsd0NyYii@3nOy&BHH&PflUXEEPVg95B=fS$? z6U?-HMTilK_0v=&SvYyw^;VDVEq})l+0*7v3I}QY;8?CzrLYH)i?`mO_>{#w=}V(j49h$o(O^_Q|3SDpDb4YG}|H1-P<4f8w2~n z^Yz=x7}!udFyC`hlUK^p_Lsw91>#xqa&3cfg%V|@lZXC~Z0^OYc;3J2gybv@gD)bO z;&|jx&LFycoz@pQS+G-aXQ9#j~js1$QKAvl_Bfd6et{EhjI|90=2mJ;?zyMx;_{Ru6W+b zR*fFB;JX}m<+e&`-!G&Gtxl{wo~rfY{0gWKeXpe8MFueC-ihS?vQmLhqVtZ0iTH<4 zItwBEwONIZQtnSrTk+7C@FI(FS=Fc(5m(!ZG|#$`Bz>gzQ4i;|vL#M+mRk23w(xoR zJVdK3_sQ=^{A8Hk+EF8j4iVlqRkb=tz0=b-IsY<NU44%}QFArDph8S1KcV_Gk-E^ml*QWreE@)aZ5R7sU;)&Q4{GdYH^j!;WjtEQ-pDE}LgjOdKTjB%$x4tOdTc(a`AoaGmdf`{ zt!tr9wi6;)`r?vBM=w`p&vJX`_1^1B-4u+xfk9rvO`dpgMsI$~YY=@&tv0!`Mjlm* zu~4w?4^YldvHhoCLKC=~O1{<+OL{nq?XOY-`R~_lHT{$NOLe8pLkM2gzR);n35@jL z{u&=Go>+eYv&xc=fd12`Z{qI#KMuc?^0c3!h zHcDz5YiWQ(aBo_=okT==+BW*5L*R-*2m_p-i9QEA>MraV?BNF8T(CgfdJI%2aQ@d2V)3_-OpluE$NB!_k?Y{kZ@85i)FZ z<<)PY81F=kymX7rvwrQdEV;b%$wM!>0*1+x{ebcp>*av1?waCZ^M-n}Kl27@46Q@l z$RBPH8n-7J_T4fDF6>MSwT%@08-qVQY(OWRTy+>ViK^m9B-err6J}A4Mc$o}5*;rI ztm^FLA^4?k?5dC1WrJ1U2d-po5$1|nPm%tZ^-3Q2obfmGaiR3$XS7IzTQ`G=+3585 z8j8~I+mB(3?2%Yv>edp&WM=ctRc;H)wHaB@%hEY<%CP~???1Z+TM6MV8y4qGh#X<* z5e~qADk;;1*-{aEq3>)VFfj2mEIE&<8P!G=WjQ%}R=U;=FdDkqaV$_V!B^iGP}VK- zf0R;u{}V83HV3l)XCqCJuFH4c5NczYzFN}9mfAsj`V;LqfL(^DoCrXA>6TYZM zATN%cs}}vt{e+q4-F&Ba%8vu1Na7sK52+@ZK^$<~d$Ai^hKe+eICHHN1&v*nF{ z*hzO^7=HhSg9!h4%jAIbDW}LyRfq1j6>yTbI9d{_Tc!1jQk0mY>2~|Z?KS`;n{!Uo|Pp(eOwsrMF)XJ!S`(I zxzt{bbJ>1t>yW)k7>;DqhO4jC%9*@^Bs3xB6PFEfKwmuY4mri-8ZyMu%hkz(?bHN7YGYsmsb9Nt?)A>Y(5n~qOqgZi0n6(_{&XKXYI=eFT|>>= zz8rd)1>#=*Be@A#R#GpV?TVMY^OTg723#*i131+B?+++qOg>|eaMij7lU6+W@Ed7$ zmUs~r!pYdx{Ihn9D^B3Whr#%Z=kIRx7{9gGh?U~FNGAfWI9^Z}^tu7(C&;^5NIWWM zT))3Ny!ZD~eE1IQj3yo*ED!Xoj_tueC8*!BU@`c@W8)q@&ag25%k(89^DL>6+LvAc zuDv9qa>e~h>&n^t8@{MZQL#Kc-KK_?u@Ra7%>{shA;V9Bia*69=~-VCCQ)czhY9q? z?Gko;bTM9H!#{g!^66E;6>AjJugn>Vy1=B5FJLs}&6SgaFR!N)8TWeQV>@}pJCi#I z<(uA_lh}W4xcGFN2s&f&7J4B-pYBdcL`VOW>+QMo`2PV#K)S!=%s?wIKG2P9V zbnt{#cCbvAN+v@QNYam-%0W5X@ia*@^sUvomW9n?umcN$-GfIQXcyBMK%ES|RGQ9| zdg&Kc`A0`-0yl6%pwlRNWh>80jHY>6AdmR5hq{pB18G{eN?&NpB<)F+MUL9T+a#Q@ zUF=w=TpXa&VK|Dj;7HD&H(Z$J(<}6C+0zA~1cI9^d})h!sg$R(Nmm^$yBjAp&+-zF zn_rz}l*$`qZE4-h5E z4tQZ2R}QSy!y61fHHa*_y8-^#K@2=h^+kNnak3MJL6wIFev%{W6a!Y|s*@W*lrub) z#|k*+NpS2iM@LgeZv(aUh+U5s$*AuHg>WKw>ae2z=f74mWlaTp`^22xgv+LD_n%Z_x3N1L2~h6oS0%` z4bl~ouKvl+2-Vrq1};109K#(wa7*~OQ|<)m2(1F;S#Il)rtlgZDYAP>s}q|#S*yND z${c)4&=40{6}KcVuu)3P7rR22o-&EXS6WJ9|8`O9$<2E7*EKc&;0N-3rU*Z2ifi~r z7TUAJXNk7#js=IG2nGk;2H)AfjVP`>99&$e4LVlkUPp|b-C|nSVG!6wV6CAya!FIk z5o9d9Tv+HsZ}rF^1Kr@kL;4AaZl~%4)MNA57$<3mip%0BjJ};s-!78UE0Y7u!+J49 zxKbaxK!spaKRvk4_fH1_%s5C2@jPRZmdH;Mk z)gkffn6rSa8I-8kdXAU&DgBQ}z@NwdXrN9IP;33+Vp-Y3i$P7lQNqu}L60;0LS1Fn zGhKFr+jja*e4f~avOEEDUhparJ)P+R#k_=Ps|zzNu-*YplPb44U?i?_QY|3|j|B$y z7#L;(cnMNN^+(+^yx1NCfdmh?`*Z&sCHc`D9P1TId{Y)S$fa7cb-WcH&5(yW&X!bC z!)wS){y3x1D{wTPIEn^naP6p-GZ0G#VH{{;1qcYCt_HgK@PkQRhNl5O2H_6f195pL zpcRB9ce6PQ>U2{NQR&9a5RU$Z61@##?d$7H(g~!4ytUH-f_~CwcJ%PoE)wutL8`9W zzy-VmS6ZTzbSMK*XGh2ktPq8b)NwG#uyM%EhK&3@fsv>PJ?T?ri_NIl|B5G=NtF17 zNY_5OL}~*7i3hTg+@GGZuuncMbdv0`V1N9~YXO`Gy|2+A7Tf(MeN$QzE82!nTG2hi zA)|{RCuIi)qbb|*41{*FG0E7e)5&RfAXFCqiWXQt#Vud`oaoZ-;zSm}Eb?{0RwDKE zVX`%M2P8GvEvZmB#JKNF=V zL47hy&{MC0vKs*nNYUTr9V5{QGm$l@b|s(tG2KIFw;(l`pE?WB==$Y5r9yn9&v^r8LPw2qaNeL4$1>!}-U z+AAl{YKnb>A#FYs;XfCfXFgIWarw&6i8+fYZN!*te%$Bpl@#anGPrp5c?3l!?>~1T zS7iy!;S23-+%2aP-x$!wL?n&bJ#~eugc!MP33If*QhA2L%NPo6Q<^g9@pR{uA{5{s`xm@ z3g@yuy#%=IsAt@Psq#yV&XvU-S#4n-$QTNii(2&@{!Bk0cB~i2#?lEOcwMidG2wR5Gjc-=c>s0as?%1EOF z?NqP<*A8fN21JjP9kkLFgz1XvCoWj=gOKIb@Jk1Sj~|0HgM5Nc<7pO^s=zZ4P0DE> zHjTkJA_A|&43x;EP6|pXJ9(5%xm-eCGsWTHc+W!yXk@@NF|z79&?%b+*YQ2_qeFEQ zdC&$u;nhK{mAMN+S)R%yCEyLX$YQ`zCvV$b2t)@Vp<;(JB1poQCMgze0>FSU$<~2K zb;T%nd3BMCOv;(d?Zm*PJ9+7q_VhValt%njq;n5rw%n!N>$Lw^%y@?7;4fp_U5sRq zRt~%je6X2#fYAZB;ldIHekqH?&hfYbwWT+K-%SL z8ap{MOc!!8kglr90(+U4gY|O^80jN4C01y|61Afkqch0KM*P?wJ`D2elZBP_@Q4k> z1=huO{<#45(eCjlbw2!KjEk$!I3SLF)Yy`V`D%A^P9MSQaf^5aino>kDJJ;!Nk3p! z>-gX|@vW}3%dh0-KwAcB?KM|}nX)C8m52IF^1;Mr@_?Ck2ese~Tvmy>`axW4mnDcQ z393?We9|6UD`oLlUzPSyyUXC%80Dz5qez3w;3V2S;jm769BBzYxxnUFvS~N#j34#S z~y^1}|4kMCgKL zkpgaR^C$(Pm%__VVpeC>YfPqA;UU4d#%jvx|G>hd{A}L=&y5#8o*ZR<6i0G=>cj|K z$;pJHCla(O)C4qe=d1j}64WOi=dhu2Hu>{ShctGdN-sd=4-5DtjU=L1t6!oYw=oREEB>>ue%@H!rei zk8;$2W1ojG5aqiOo64$G@{qM!S6zAo_A{o%RBoxu$9M#oWBxq!HerO)hIxIP1bcEf zG6>MqUP;u+FM3)v>Y*{#N$RNdleR9CQk6vbNt)d+)=A1Ml-hHHofG4yhl3kA-1d#L z08DVJ3124@|Av#Plz&#$>YVM@tPWIE3=%>oNEB0C>u4D59;K&}PK5JVe;9Qr?&34X z1J0dIpL=yAF$h4$n1is&T2GlIPB}Nl$e}@vO(CT3S&EZ)V!&Z81A$UiyD$c}m-~~4 zSI7cV9lHWr9UUYzpo?ua%mMl2C$7QW-IcO7&Y~7*k51R)UXbty*OiyyG%h6>dM0f) zI<8gLE@ta0&7nW^a~CrC_(oppQ1&L1oe+aEFo zwMyoW#mr7w2HHB2$=FVu!`-!px|(vr2x&`p{PZDYP(E#}z0+oeFyQ8&OX4E(altEVQSAp)4NDU-lECodL*OHE0(xe&{3 z3;12#ltB`SMfFCZ^jXI>TViTyOG|rTCwb*wMC~CnDfD0H*F-()3fBc<;yE%1+uJ!v zS>I&=L2ksnw+CK90)aBC2+SV5|tZQhRwB7;Qt(xKf9~%ifwkI;jcC+I0iR zq@R+YC0WagA$tkOX9A>MS&Si$0s^Ovt#PajzJSPK-mI6VO~$AeUY;0>m;KwzwYpY2 z)GJ_0XLZTR0xXc{_iF{IPV9?_V@Cx6R>FYp_G`!VHv=^a zmw$YR;lj*3@|C`lbofx_@{jT07(@lS$t|=vS}^v;JTMl%!K2y!q9`$<{2`uKBS|Xs zJOTs#(#u$|g`H98+AF1%ZpSeabbW>L$Ss zh+iGRH%G`P_ZWJl5Pt(giEs6#VS~2FzB2gny=1fxP(>RZ<7;?&7b=F76EmYEZ)s0HGHNQ2Yqzpo>>!~CU@!;}p6KKNn=y_7zb1mt zgL)?xY3Ru8A_4E&$7Dc4mhdv5OX}FfQWnR(HagJm89@au`L3(@@eBTNe$N?uXhljW zAP=@29K5WSL6;vVc%7|`(pcc~p>S%TOB6;O5{Mez7%(`@l8g&=Nf~=Ye+M?|{Z>28 z#)`0fjd0^5V|Pfp7fsy6p3vtpKu-`HBx*q;mNpA^Zqk=l2G+FHV3@N{=s5K_$vEH| zab;idbsjV%lNh62_Oo2Oq)QQxzC@uE%kD35h!Nk0U+1qJ#-UouVb`P+k(`!YwpdA{psY0Wh}eEHPe0{|vH&oHEPH zrzwTY%V?2P8#?e&$>I|~o5xP_%gIH8+i^f`D=E8?Gj>SyTjYbTQ1Ky0QIsJ&?UaXa zI$rMY!eohQ?PCk*}CQ9 z3y1E-c~95{6&PH21{e6ywmx4DP_}Oh@Ax}*^Q};Rb+%s>q-FR~XO;5#jZdk_!)CPS zp4`*}2$AxqdVmi?HY#uOR30Z}%}oXF)lZBy1G3RzIOL1D7qlNxEN zkM!M@Gj2Stut84{z*krR2JvIjt!w@Ht|I5b5e39Br|tlYEl1B5Xp zP5Gf2a9-b3meSE%=}fj`A_c9KdU$Amdl*J)kX2g`Le@ET+pSG9!mt$R_9kDg$8 z&-b2M3%c6Hc|wY@6>V-P+iw(89?F)D7;HV~PC5-r9clWiHjVy7=V9`vcbzl z14GZI5CK^jZyA7B09L!O2ky!dj=562Kfy)~QwPcmb=d@RtU^ z$!B2;U7f6qwi!<;D|4RWU=x~Ury(zuLoWo%*}XLS z#Nhn4gL?Ifg7`WT2dz4(J1SYAY8&ipyK$i`4lr5BP;f!(f=bz3Y$}I&a_QKzAV-~a zj7Rb+YFUD$oY5yXX_FpKs*?rz#GpB$(FS&87yh9iUQ(B*M{piQ>!6@;U`9(rX< z3D@K!apT0nE0dmznxL)R#-UWzSy5`3E%_JIuWf;@Xy6)6xlIQ9ItEy0+-2gIZb>CD z_gU%hI>XNM3E35LxQDm0(&o$9yP1jyt@VLKT-V0rK*EYcs0J9)6Zy%;WTIR7NbT+U~ShP_A$F-#?B76@+2YAfnl=wfLPfT(~i{1$8fWkC1YeT^7YERLNCjM#G0+cYA^4dv`$;ap42(fW)71#@@ow|7U|kOT~}KtP#Zp$@k7C7OC~$K4z?grO7eQO_abPllQC!HO#g3 zEOR3~$Y~|V+B|SfNmiNL$k=L9c1bU|CIHrjyfK;0Ae0!d@2TCwT8+fUF@W}0-)=t% zVmD9PJSi}&Vg%?qwontk&KF^d9^$(OE1xMt;RpiZ;Mx|hpTEX|;C4nLW*(_q546vI@Cj(4<9S<@{lgTa=9Z{#sUrLjOTjwF8I1N0_ zq3NVA{{&5HDfCL)>)t^jgonT1fOJ<=u1i?#=+NRTOSD5=1)R1FIqc%unjX?0Qt@0g z0|_uA_{oO8M^oY?42urGks&rW{Jp5X^$m_JEWQ*&Sdo$yq9Go?+!S z28<*8c65=Z6NjUUi!&FW*=0ltJ3i<5$ZqV&tOPCp!HAT}o0v;0xQXd%qT?5igRg^D z{+$>!rK1gvmw-^CzDXR+8N&_`#=P>l?9tGINlTg|84F5K3$%byk{`9ia(o0M2dU&~ zm526IhR~)){{{~`l+IcHgpKhduJP$)Add9CA`qF>U6~|GTiHldVfgI<;2;GFDu(=J zS#c_r3@ayiqo;JVYpO#p!^-%PwDKy4u_vj>Mkew`tLj71)xiS5JP-tUO$=nfjcjBs z)Bu@6er$_PLANcc+S2p#_5?(jsG}?^k~;XOtd&L|E^tpm`fy}GH1m8;Jnl5qA+-8q|}N)vV?G7a7IKNox6RVqfbz4JYpS zM!H0nDeoHPILl%|*>`mM6B{9nwlt>28kUJ76eO*W<&m8+`-3upml*34jVCbaE5MfD zq-mV=1pVj+{7RDwph)u7e*9FtVlT%y@u>-tFqV}~9Q&v#eVDAp&{0zMCC=X|ZSHVN1`U0;S1j=bnJCR5D_#e5&8}=6>;}wCDlYMA=GG13! zjH#kiYdL8ZE?^>NSCQOg%+WicO}jD+=B-NEOd+oqw6O_}{!~9JQDbNE=lnN1*-jRk z##$hn6^+#?KsJp*K_mDXfg_QjWE{YZqmdE}P!Mm?JyEFvYrl##W+*4mZx(73k%du- z(W-hShl)7>*+H%KYjU8>1Sw6dRvcry;u}lKufAiUF!TrwQI%6_5Hs&9YjO$3!6vf` zwLLFyWy~T2oak*w?L6$PI&{cu*@0}N*aNxp)GGQIIG%whW8@;2&LIqWd5tzj$!-WW zWz(*!ATl*j4Yt^sHpA#?4)AtjYzsm&(ms64KSkDd7w!C!v|I7A1a#o5KBeea zy*0Iz3$9+Z;DiPZlvTaN8Vn~7f&l75L*I+%lgjIJR?p%R-wvC4%%lihY^Kk7R7W=!v`7C7xJ}`!ZDFjWqMFaHE%uaa^a=ZA8HQMy zaFh*&vLhmcU~wSTA{ja$#J<4BrhzNkWi9fZya#P56+0l`|()iUn zrLJQO8v0GWHh>vNHXaO?su;CRoqt)2T+^=4a7vl5D2_vxCoRBs+CG$iA zYLN#VQ#=e%;vhBhpQ=bFm$+yPy!d0FuBH^tzUZNJ?F;-|*bd1WTR3ND42tE}h6EIVehB z8*3MBq(`%zkI>@j2nV@hTX9y{C8+nUX$xfvyg8auMP7fDFNN3cw&`KZ@({Otr#yNm z6HNEnh7c{hyz?`7N@}O%7K7MGTJnXHjOqDCf5mplr@c7Uu?q0VnEuW{6_HhCG+&{cj3D@)>lqr}W7w!rbYGFh(41t8?oewmM?HSwB=1FQQO z*%Y;2Ok90g5EppLXbeT2E>rLRR%x2pE|;z2uUe2A2iw;X-P9S0u_XvAMW&W|V@CQl zfhw7FP0CM4)RF5`a>vW z;T8VD6Al=|NxjAE^s47cG}Vclm}c-(D%whaX$Q2zC9+7f>YS7_aRVP;aGdBbBJG~= z+Lm!bd8|kHIh^A{Zv2NSZRwom*k|5*!Y~r1FL{E;PmLWmmQUhuO5!+vmSh}ABL-_x zRq<4d9qmVd^sNMetUl~(_A0jVg9a{X0~=pqU>^u|btji^MaNyyE)RNo#rF)k8_^wMRguYW==@?7im#ma4lVZ!hbT@_z-tl26_lm<; z?*havut{1i3B1tLs1hM%{3L7N_JPhy^0!e;7Ru^xjyfem)1qTe0Zg=+8!fSgd~7n{ zXen!rzE*Qq>hVkITsxJ{5%TNU!R04OxI7evfH_%Aj8&UV!$Zwk-%jvNrK*KNct_h#-3fSzmj%QWOa|XA-%;&dc0X_)^d$h(BS>?`}>K z2K2p?8o^zF$ReH{5a${QUfhYi9GsNw#CASAySIXj30FogsNV3x9c&2 z6j>c$vdN=&p{F186BzQR z9Y;qD`V-ihLx>ykGTxQXr)q^0zY)xhKX@yP_!9`m06<34r84)Ak*CgLMk7w7RQ8Gk z&OMPTYi&#_HKAP;`SEkYuJgUPu_Cl7-AGScxlsOL>;+N=P z7wHfF=CSj->#vus=O85Q^s|VR?FT!{_teIxfY()XnHvyoqYc!<`9T}z?JQQ=hK}() z=kq{0cEz``eOJPH+ z;l)!4H}@5K*pm` z!gKV?qqa#75+gQqE?OA?@X(3Pnm_^2gI(tmQ)Xr*+I0$Ixfg`-w54hQ5`hB?(G0+J z;J^vy1Oo=0lSSU@z~K-CqB@UHcTQ?dGC#OZ}W*_1|yYWTt19&^1{96LT8uANkc)FuPhi32-IKboo% z?fnAPUa-VTzyiGqJv7b7A(7Ld*I)O}rp;7vl zTPc9GS(!2}QO3cP0Y!tpIP&tzt~t1nkB-XMz7-0%25n^9wjfWm<)48kF}P*Ivkmwb zk3wMDRw=+lI@&+2k#*o&qwF3VH#Uwjw289&(SLwYIqBfrnb336)eLSYQ#NZFJ|aJ5HmQAp zsR%0dS-h#uP?&fK8(*Lt{^5y~5KG_(CC7Vqu_qd%UVh(}B80U7XS_q#lKlLdB?0AZ zoA#S*7yVkZ(MPF@?ezPW%kiCrpe}w>DOK7dx>E_&1smHcFC>cfVwmPd19onWIv9W^ z@`s^lp(z1UWYNNnYm;-$2ojg{4SwCK-czRtXTPhL;>)hd>YG!2D(G_*%~0b@K8^vK z^a-+xFo{AY{Qc@WzlFEl{z!Rh3VCG+%=9JRB%`yew6&7`C!iAOgDvAkD5y3HBef=B zA3J0unQxFH3v%by2->Gn5WKL)A%P0GYlnfG^vZvQaSg zW#%*Ta)?ae<`^ZVB8GerdP?!1S(%QSbQId1!-+!ZNGq?RA)^n5_K=%`R5h&ubnwu% zS28*CN1GqMBT4YdX#sB`N-gxJ?xCzbktDj3M;}jjdv1{qNgYG8$DUPL12~2sGW0x7 z8Ca7);b$8B4OZNE7;8W`XaRwcee`=Hz%tq>FnKCDDx)^(gxRw^ldlDM_WIz+H{2(Jh5W*PYrO^bIAPEeFAMNwS8-2Od)~p z52cTPt6z}8)(+}rR%-SQc5@AJ&TK&J3xdI~yOgfk{@FTG=uQP~+nDX+%591+vo`rn zV!$W;F_ne0pH1mU3>JG*$!oh%6$N5M@_n-=fRiIcuc-suIW$^K4NMaaVV#olj1dJp zN;-pg)ug`tA%Wrlf70WE~o@t3s1p z^h~iKyu%+fC#U#K0*gTK)i@*QR8-etwb$}yVTjY+I&Nzy^?7P}|h zbVJIzv;nkdvf{^C0z7E6ZjlAPkyqw^T1x@Nm~7kqP9?DV6=y31C_@+JO`xpOl7ON1 z>cOF%nwAr!KXv5}w2PngGpLiH&dG#)U%Il-mD~gc<>8Y_5yR){O!4+h5g)2EKZ`KR zz8}z(l~#P2Ms-pSEv|F4WYz=hBt;$I8Bg(Ls#8WhRknk2zYpk6)s%WrLNooMrs{&+ zPUPM%<>VoaAzG^njFq7eF0BUb?zBj`q3K=fQl1o(R`+ttN+ERYAJ4uUwkM|8ks9R? z%^gm6BQpNNVp1wBsGu0uk^#Xtd2b)Bew+k*IuR$h@8mPg$m$>v);4)W?;2_(DEjq&IAq+ z)gFD}xDO%3FgVBy-h~0shhy{{i7E!)R&s3nwg5EmmtqIXSrD8nCb>bHCB(`?qMjy1 zO%epD^W8P$8!B{;`Uv4%5E?jfFS253FLk34U2P9tj=`c3U$+#MTC*0=Ypc%$O!&$vJn;fF>nUm+GExl`D0$dxLXK&zg&s`+I>$>5bYWZKtZ73}>FT%2 z>TO5iN}hiL(8>FtD>(`E+|ZJ>$_qXknp6f006$!QGVA=*Bck!rPKPT} z2cg2i7rO4)O4~O3I5LHb6%j8-<%mkO<-OG$7dD!d%Ydl)xjqVPED*PmLTsnp`_+YV z^KhP14&@S5J>=H`u5?F2j?qX}>)sW^UtJqpSSj#{?oA`}qNJ`TN3K+WmaBQT@0X-9 zSn5t*igx{)MrQZ5@b(h|3EOj`vbL|-Y-&2fbvbmv^8vo)ULv)ADWg*2$$QE|E?dnU zn;@<}U7pHg{M1VjIvvpAm>Od+iBJ@L%}+W~!0DrdH!QTN7AI$TW)R*9TpW`F-am^@ z#tw*(0ZNH`2T39VV<_Nz0&5!Sm=IFo4n8AKsQSteh=Sv`wDX>mv4g|FK%Vl}K}>Mr zMb^DoX8>uNv@CofX-`B5*?-+8oWaf9Ia&HAzWUp) zUxem_3nHz^Pp38yG?o`6w1YZ@R%n8pf^AS8Fh0>S>Y*s_u>O?b(Z$j-uk=!?rqFU3z&WNn2&Qg^rv>HIbl4XubNj!xxjD<*Wz2E_89#sC$m#~0{hsg7n1B+2Hh_|(DPfe$xi(>YXN4e_r$ni zm95jvEU|m{yM1B`8zCpXIZS-e#lEGF>&g;%O9TM7Kwkm_Q@-@)4%KX&EA^eqvO8;WB zz|a!j(j>_iFuAI8Xi_jpb!JX`S_QU&9?~hSCn`IK;Cc^1hyW*o^+`zX1Q;f? zEeHZF8A~6!*|me7+pl(_NeX#-m809WrtIW#%mM*KFU&oKDpY>Q6MT26yh&8G#2Eu! zr*?3rNjl07qlrS~lXkQZwm|TKwy{`W?(irj^n!&)I|!E1!%1w8LMAdDKXP(j86BVn z`3wdK+NnyJ2~>qC?94eHG}&&`UYSslGf&<@%QL`S`UHPD??O^`16DguzZ?T+kCTLD zaaD;%?P-6tC^XW+dEXg;I-XBeWudUo$h*TTaPEze9JpY?a~e$G^kN-W=~LxV7L(s& zQdd_ySKjKDAR9gg&P~sNLR_q=oYunAonCFAJ@tb4pfI-6S7JL*-92g9)VE=~Y`;Lr zWZzO5`4A-9OieZf9vSqRBjL2lqO9zsIMrmjAMH+Dpy?1XLM7n$X6Uc2Zt$m(J# zj_o>7{1o0~sFjIUp*P41=`1O_`noG=zD?D1mq9S}s2AQzB{tWK@RbDS&|2y}p74{e zAIDpCoABi@gN06(3}7^!_v2dj3Hj(9PH>}MwOsTR)ypUJLg?n_=M)40{ZO8OG#r9Z zmDERRd;ix2!!cu&O6}l6VS6Sij-4125;ZAg_ydMa_Fe87r8<0r?-+Dc4w&}Q;A@DH zfX#fxxG~`M^07KfDS`R~JJy1341h*X0B;{fSr8ky=_iP#PvQgaKvJze5i7eK0S+?w zw$WeFC*+g|vaFo)_5{VD-m@zi>Cq7JP)}syD z_p>5vipVpE5#t7;g4?>8RbToL=JM-t%@JVT(|O9Il=-C_VX1OUyY?7at zZmMXDm;zm-sTEpl>f3QpiZ&%2b1p3C!80UU1qbc>p;K4-M!i}Rg{I&!wonP5vRm*%5KnmPu zYuO3N5JT|XKdO%Q&Hh21=m#W-hA#~Urs8NH;aqpMG1gbm8JaX_4?p5#@dcO)A#cI4 zXy~Rq!)%*mm4}A?h==9Ae*0$No0F?;3-eCLTX6#ECHT}Loz&Z;Ju_z_l+TO=zZ=x1 zxqucs#oy&2M|JIBGK8qJuRP&Ub=&>+&!k?EZ+%lUeNh*G171H`-ob{0hzdSF1QX$* zsz3=a9Q4xVlL;6^CwLtw+uhVVNrYjrS{!eR?MnvYLRcCRpj}DFlyr{%&S%)57S66# zvq=iVnIuIfB~Ssx$s=7PJZS_5c^Y_FoO%^z4o-)UT}k0Hks^~0qU?(2q!n`uzn=}9 z@U^C6sV{9xfhu_COvWG_+}y9=ga9!oCT*x*b~^vi)~0;ZKJIl;ZU?R@yFDE%aL zv8DB~D*mFEO!(?AYTpAZ4a;iMNfx;*4SWLh8BcA2Zt;*jS>_aiNGqAfRWp67j%LVcfLc z?N?dl_JDXuO4KF92S?w5DmbAbJB5M1?=YkQ5Bke2N5xl;q-orZB1ox@KZrjyD1&u^ zYm*W5%4Q__N6=A0o6S=6(~B)f;1Ym)w4ue%5YQI#jQF%^i;?2rqa#7;^Ru2<<=}%k zPSE~oF^@_RUx1KvLSI%|V9+qM!8CRUr%$G*Imm}sGMRu2e)NHC78DTjfFXkT4%IBl zVcGqqa#`*ov;7NRCIsh7h>;>I4Yu`e5p}{-wk=ti^6`*lo==AL1dyQ5Mr9Om0MB?+ z0a+h_nW|*e^r;g(yI~+_!3%iHm~A)e6a2|Te zSk7;sgPXYxLwJ!xxwMe#dCVA__!>s|67#+++9yle$KQ@t2Q3x5imMpFkCW@H9n=ex zAh8_~h=zj|V)sdXemnqr@^>!OR~#qf#9Rrb9XrV@!fG>V$s1q^Pco1r>!0?dl17_N zu1AKi8Ly&gqWlg?)XPr72)H;KY!3_hgrQj-frU zG$WHDnv^#ubB&ok2}9Zi(aOrF&HLe1u_l4Wfwss4Lik4aZrq=zm=bMzDdo$P*TgL9 zw@`h6yxyw8pp&RUq%PRWLDND~mN6jeNmkQk($S$*(xhqf{wVG{6i%8FkCTGjj1&=U z%K+)5?Lc6&5`?2Xn&fos&ZIHCVys92J{@I>;*kLzltWnfYb1w?gR*y5!EquGmNq5O zF}fW=G(rHQ#w+|L6Au+^w~FXczzZ)@Pj9ewD|BJSnbm~Th;>FvfEp3 z0{mRoZ5t0ew@vITZ~&BVCM?BMPbEe$`6S6lp4wb^bVxu9fP93@Al9~#0kLQp`Pf7P z$_APrl@;d?R zSeX!!Q7{*_{&ftYl(6*)DK@gDi{+g8E9zh_+{kLHr9&`LG8q$cd6xve@x?2 ze~^#7rI7weXNqcN|GZ;7koxQ%;;-&P_9YM~u#M=rjd1!DGKo59_pfRw33f#hM`c6< z#o$5}TyzW~)33k`+h?*tIIY(GYaT0DXC=4`^_JUrwbyu>S}+q`M`g0#_NB^B@b-xh zPR;^qOs0MjM_$^KgbyXtg`hIZS! z`Dg=xL9!%1M6Px}UwzGrbMGb{1Y=4n)=~ z`2=X9OP*wtG)$2jypgoogd)MT=azzro*WggQMz5eY^Gt1rYu=0Gaxt{PUjEueH1xD zdL-jbmQJi$ylo_ij1izW>mxASaqQhg9a0JtJ&5Q?vp-0*F`x^*$fi7DDvlWkftKhA z$w4s&MtIwCH0Nnybm$Yj>PPPjXbrj)P7t%);rY0T zw?uGMW=6KVY2JgGOAIm&33Y4UE)Ln5q9RTPfxK>qdM~A9@X;2%V4BG<@+wHkW%6(m z(0yVSd1O?2gg*EVgI;&gh`JeWGA*?-X>%!!vsM9}q{2V2;9?KS3)@aN_0t5`bd=40 z177Z-O%5aXq;0#&c4>9JdFA!bNN)kQ;k3K<>4Z+$I73Rs0rA0{WVL^srs*n1Kb>!8 zq6LxI2|fmsd7&zRnbd5Ppnc6j8$j8bOe_v8`&@ZKBQP|Qgt1cEewB}iwniHZf`_j%yLe{&*`gkilKc#)ZL4!;7Y^FtFK%ec ztEoHZ^bS*B0G~i$zY2@Boa0oOq!?oLLYw6+<#9!*of29A%3m{Ri^dL#=$XsQlQRZR zflLEQxmw7s=R3AXJ)hJ}0!1Hal;+SwQKm{cakYqra<%|s_nGodINF2e$Vpv%AyQiB zobAcTwzI0wWKBJ_rdBC~6S57~e(ZQ62@n>J7) zwXDeT(HC?JyGahxn~e`)H92Qo_CDF3JA?rzuh8Mxy@VGED~i0N2_e@ePn4|-GY-D; zu7jbLr_JtoqfA@YNAv!ngS%NW%vHOIHqA-HY*WP*>3zzqR}mxgKT7qg*lW|ovoJ1Z%6zd+4?#%f;|2tm0&Nk;t*9-lqd)fQ#7bF_=7HWpA z(-J#$Uo7xfN9uyoc;Z;MycL4+)CgPH^PN>+#@xjrQPlw^3lW?$z`2f_dkb(BnY9xu zMl1ICq3RIc6?9||}eZ}ucqA&Mj_=UlA5*KtQYX?efNK;SV z(1C=zUM(4VDK-No_g`WQZDwaEp(LG`r76C)(PmB{>h1#+CiU2Bcvqf0)hpCEY9n3KvV63mI|K$?PMPA5J`WVA zs%DVXSd)IyrfngQ=m9|d4ID&izUrI5NC5wy=-mQP2b#J)GJ>xioP6LTzpt@`D83d3kyv< zddiEknq;!IEDm+HYd3#%Aju+)?mDJ#V?Z&TC#WNp098daO9#9k&JOdfWH>G^s$y8Hiwl1{ESyCSB z%-gL0pjPVq?is6nAV4Zi8so`QSV>1x;QElHNxSP4S!k$_O&TP8yH6Wd+=w@`r97s( zfQ&-(Q-?4Vh{0zuzOV0EfLMYj`I5J-uK*!Y=uP+@NY2#*F2^X-ZT724E(t=Q=^WAg z5LkY|yB5gP`*hnJ&;*s)FR;>e9f?na<2>Y;a>w1dBC5b8S89OOA63RNXrii*)b-*` zUm66$#5UEMKw|s&pKUmx@3>V9=kk1qhBTe4SWjRguJYz5D?_g-_Ia-N`%Mg}RwVn@ z`AvDfeM$oA_3*VW%)`9WwsF>oj8^OXSIk!v2h{^dkZesn7_KmkC3=CxC{@ z&JkdC<`61*h|07n04qWM?Ys^Ihb+Kl;ZO;%r3n@FPD*0j;n`!q44YgqrB8$2+kR*+;bdUOjg>Y@}w(;f(u zw8APQ^ka)jPCM0kKa(VRr4JtYA)hpr5jALsGe_+!9bZV=Ol@h%^cC&tb9y5k4h8!n zW&%%IPEei@G+ig1PCBb+^wx)_FfxULvKcr?)0#LmIbSKG&H4ciWRt$+nAqd|5EEVv z)!Ebmqd9S>I^gYN7lb%8^)4Q^Ms~r4<-g@tjxLWT?UqZDbXuSM*g}#Pg0)`Kl?GCw zPh_Kg7Q_h7pEh$)bTAJme9}Kdl*kE=xQ>h5|7)RFFEX9lm(fSw-MOJ4s4@*d2N7Nd zCjdoOaDWs~H0|rB*b&JAsT# zhNEAi7n-Yv`N#kcpd`3p-|=9aJAjB3UsFDJXQC5eK5d$ZR;Uju*!H2H+)m>7wh*e) zH{W9^d?(#9Cy1lo)a50=3>te!x=z3uKfr{7gF|_-g()@%7wqmo6$_>Z`Vzj@^rF{C zn-fS_4+Dcq7mb{tNQe6FX6vC0jfmeQ;X}qe0@=UK-TrTT1{NaPPP?j`<4QduKDg>? za&Adprn3Y^@hCgT#V*Qa1%lbHCh5ANZMSriKEW2{$feDKKic;{w$wQRiQK|EUqMj6 zP&dkL4e`3~9CLj;#3X;_TVTCc0g3hM?80i7iT9*x(@ekh9I!C=Q-)kvtBbZmChBJ5YPEk5R$b zC>qK$xWI#}jKR!l1_p=L;F}#Fh^U_=4H|Nr8qt!S;HCA@(d(yCAx_TjAUtl+4F}40 zgnIOu0G$nvvh=(H4Pdep3If&9$DS}}SzMFsOk;ONyC~Q=JJBKA!I($_tE}<|j&gW{ zuQPUVf-F;6VT8-1Com{;qM=mVgLe8OzbpezzlUqWr<@5FV;NAm`1HV}0b2H>eGxfQ zqfW?X9{KVoOZj8tu4~-!O;hR(@(hf+9{%8)l&y0qk_SN?+dv-psSHB%543$4`VAjC z(Ct7|ID+lL2RPX~S+p-SZShT62sp|wB4Cpd97s2`IhxCEzN`amjkZc7x=ygr$&Oc$ z!O5W47wC8L+FfSa%vp188vOiZ6^lfctjOxRA)D}$CjH%$oc07C_^{9;boxoA1*fxL z@gblRNKZOsp=@Lm@1{ z0e}fju6~lJlZCy!}9+3bV8(gH))CT z?+J~p#Bs+^3TfdRi`rdGMl6%80KjX{nSw3PTiBgFp96-9a$?) zJB#S)7zgFl6CD^gO&4yB6$1j&_g`ucXxN*`HV<(p2q)L0<;-z3~gUr=g0NrhaCVYj>Uw|DV%_|cJdF4miIIELx3aN(y z&XT9+v3>Nk&jo<)xF<7CsXPuyk3J<1NP;#ABbWC^X%ZJ-`TJ8|S?IA%Pgo5f%LCU^ z+7tGTc9OIfQ1+Oxt3D#BG#|5_U#x=t)B;l=y z9A?4M{$1M@jvo0ZP%yeC@6U=Y$kKRy%Sh+dA+OW2KOml;EtNP)mdcEjDd%A-1yDZg z9XzCd9hkUX9sz{r&t)ofvB~i!V!)bAd~YJ(ZP;D;rEdx>_<86^8U_{32AH;0!)Y6d z_Oc|R3x`9(FImUt9KB>2`k@1!hB`oq=00Sg5uU)&asA)|C$;Si+41k&PP#7~B^l>( zhk)If9a3pb1oOHsIby$@JPQMTr4mHBhi+0GzMbQc6P%nTtHVrC`yd4PT1eSlL*96T zcF};5-=nXG0Z-wk>Y06ki7(oac8;W+7$_HcE$7_q&v|zDDnsAW-`W7sXv|-pG*IAQ zz1#mS_c#c>oSjH@AwiLXbUk*Gw&neKI1dfmwFTRMgCQHBXLI7z842Q$Wq%v@Wo8h7 z31^Nmu{z?!$xot~x4$iRu%bQDGe(3X4gI!=_OXU+SbfT!UyZ15sh&15FrDpw)9eYp zn>X_COKH64(B!+b)VC1D^Qy2wtElXY5S&&{zGb zLEH3k<+k8*>2Bp@t+Y$mIi@!e$dHs&+D;$ZPVqB7S!0|#)Mg>k4!CP>=5v_E&%%^C z8%Z9un<;)lIbx+g)%_LL`eFB@+=S1)@=At5JJ=*Y(QfOiE69BGU6tpaSzhqTD?ixrHHUSdG`Z%< zcfpqcT7XrTsoeM}s@|kmmj{eTBhlC zWS~OU;7DoI&8aavQvjwjFrEX;Hd*R;1{3)WWJd&bn!P93rh&x4(diOAa@3K=uBMJS z9B9ZD1@?}+067amra2}nS@_$nO6m@nyQgtVXee`jXI4GnAuZ*}fm~#x!FuIL&`92+ z@OEGvEU-9cXbu+yM&}E+_K}8nh(Q_UAQ4{5p2fGDlJ^C}T2mwDhuq`H1`1*^35qX4 zZGj`tVc4@(f&ch7(vaCtb1 zyt*mj&|euHSjrqx;VDEGN^Pzm5a9S@@hhZFUwI#pX(Gf<8a` zoB%Pwn8UVZvrlt6(kchqo8Yvoe-18Thf|Lek_P!mC_*EK0pE)Py;MVwr%iPs$7_z=JZZ&ppUydB<_%j z5-re?ggEdOMof9_^+&O_JA-~XCupIMz(Ze8bcD}I6!>Eil%CS{NdoQQM0J^%Vk7fN z7E^h-E{yRN>TTkalLe~1|S7ph6jeF(pvu=-BTQ>r`TI^E zoT7_;C*Q`6Wz%3C_ZvdolaZmXVsNI3G~P%=y5KiZ=)43j`r45-}631 z{m+2OJ``P<$Y>`;rfeElBjk(srA|9dnI3x->>F(%EkD9&S^FbFnG64 zHW}Ef%Q&%=x5Eh%^o0%P8#cVaQK#nD{yeml3mDUwH>dRKthJ@(I8pZywI+2UL@JJU zrEu!zIMD8W3yWhj81aGbLqVICzvXSKdWq*6pK&r+XbT{sw+cW@S(ef#U{IutKWh(Z zQdITG66sYqHQ_Sp-*fb^g#tt_WwM}X{xSY~TxdrsBeO7$1>g`(JWKmLa|^HTU*Yq> zXJm>sCi#SKiTW63KzAR+km@|q))wmSFS9xKMMrq|IOzWAc}MY}fjlu2c50oPOV2mT z-uz=H^-)i%lC>0l$fOAiXO7kyu=gwClJTF9UF-ec3-qkF44_>Hpy~{|2_$n_aGHcd z83;Q*3j$o6EZhOgB+$HIq(m)OGJHiWs$F<(Nibo(f|o6nGO)FXWiT;va2OJ#j{f@i?%5X-orY~a!I zv@Rm24{6J8i|u?SYiCTTSMF7W5>RP3rwsXtJM5o;hO%}x?_}y?n;j~3(Pj|#DPMJ( zC7RUIVA-RSQnX0DI#R7qY9sP{5wdUCo@{5wNLy5n`hRG2houezAryRUAO67W$u5Yw zH3KsG>SPsiCT*`Y=mc%;reAAcRGw%zWaO0QkUR2rp=m&cM*Cs#P-bo7(G*oU3OD+t zc~15P(}gbMkAT@e@u8~@Hqiz&Lo@N2JUD4J7p_fDb4ioKeoeYUq^lBUI?gSNc-Lrqd*>Hu_rZ%h*X60xeJKyu#6c z_7QX@J5iY=eS^dHaT@t|j3=;ZW5gCgj7{N-BB7&={L?;74P0$!aOJ$>$zHdKlLdW( zb-~EJk>JJOIT~1-w7qiWPkRT4lBP=Au^W^a(*KJcpqn|L{>>&E%isRDZNDy4~--IzXQwaGMZjV750*V}KfA z?bp^-n$n{^ZL~&LaY@k62dEA&m#vlfI~X62)Hj4dIc=Vpuk85@W0 zP!RCgJeoC8zUA9SZkcV6zH?EM`AKif!#{hMexW?{9PI;HTG}gd4N~cw>DfV3HK+^^ zfM6aB#uJ4?CYMrmY;K$QTEPgy859zhA^$QL-kDJxvoiM`l};Q@ z6`Gk4x>GcAzu3HKXk6?4WG7$Y^_G>?GY{v;1+ zDZ4(@o+)_yL;nONwJwg>z#Sv~*^x2WxXAYeBb;IxfUuZIL)NZF0DB^(M*$oimBod^ zK6NTpZ~H)7DM`x(IB=@132irkwRP<1r!Q;-$Upy+kY%=mCx-gT8(8J?q$DpUrat~| zhqlCNvqV#>+HT@bbyPbMh817d`{l0K(Ax>llN7n-1WgeElf~#STT=!%G`I!A2{C>Z z=HdjaZIcpwtb&)9+glsy^clT`;zQ`FvpIh(K>%gTNq}z8bFg=Q$o+t{lb?({`-LxU z&cA9v8+&Evzk4U}G=!(PBw?F|nNCp?LU~w@!X9LFOk`YTf(0(}>7SjqS5-xeYaeA7 z$iHz(TtChlAt1WDGc1%zaOW_Cb|v$KHG!Q)q(J{xXeG>G07%1{3L0MQUw5rpTA$E>cXqB9HCTpr5tB zx6ntT<=%mCBC zw8m!4ZYXVj#s}dDk(#UAz=_ns{kB%paq+)pk13;-+Evd?BJ!T*b~rY z$k2lw5rFN0u9Y2~FE4e>Sufn=A16h^_&oF-NU|13{*gruT+F#gs@tT)f*O5eVcL|} z0V7^=_(Ye7&C&Ck;NaC3+&e z{_H{_o;SJM9$tyE%n=^gU$kgb7bsKsf;@3;9{MJ20iP`BLTC`yx_bFp8?{e{CM>GNxQG`tmG4l^J=2hmZD0iXJ=E!HcT!9TQOkY~;%;fWQi6 z=p}%1yn(df{0^og*wMJLi+~+J}uKko3bws zmX3WA`t*r6w%Z+!V|81Gpb5Mq1Ab{QL{BlKs=UzFk2oe6qptZ*u$BW(IRnJq6fZLB z|LPLy(w`kTZS4x$kEdP(lH=UC6kL>GvAT7p=TZX0AOwFPyGqGEL)V~ffFS1 z!rRAE(_cc@`M%}N9OJ!R;lvloTTf_JrSwrR=uTl>pJ0JD?;+L)kL*B{>IX>}kVpo*J|T^m^i-1(72#=+-ehkpSGW9*;h-PTJ{0olOM* z03ZNKL_t)n(6@gke(`~(lJV0MN5hA)KH?Z_`0j8Uq}P3-eiSBstV=7h*tgO~KJf@;d{&kA)qC)G>{zzA)yl%I=}u|{qZ88DKQMp*FGfu6zw#sm?gXQHs<8_~9Op7l%u z{&Ix;_Hsjw6NO0_4M%Ot*{MrTSq2OSbn%W(ni1J2#}d@Vx>?xijT4IKDedS^J@Zz@C!i z>=`BGzncYAF5+ke#b{BS5}NToI?*ZG%XBUz>F);T+W^OJbc1FBDKVwP-*SSF0}T2pEycKO-%SW+1=hS zIq`|5fnP#^K=&exr%^DX-JXcgiJDH3<`_fTCfI3GX1NGv-`L;!R9>aK-k`uz5?neXn0A z4?l30Cg`Kb)Q6ArtF)ahJoDC7JpOOz|p{5*)#9kPQ8Ae z;0L-=?Z56LJ;Zhn&{7!thzhgFl%SvbKx1zc7~Pi*265`9#o0cv!ox&1w5(mQr?jV5 z|Lri3~fs{3sqvJYu)GtyNUH}Q3v9HiLd5R;cA9I$z z^3py69{tJw*XFT7 z;FK5wLm%?4Mb3$2Y(!7KI+I*fYG+PXxihwuQCA@;t4UE^Q{oSjQ(j7$M<*cV%i2L% zmL_FVM)g1`#wrxFX?S_XD+4sJC?%85heq3B;LFn=d&x;djE#0K2=J9t;+FUUeJ*EU z>>K9+Q*6&M_LWw&(kh+zVc4vP%8Go#&F~^m)1|R-(+s|SZVfQ77ga8)>yrfQ^hu%G zyG$q$F=u6r#ilrvYYu*fn|Rk7Qp-Or9nW;!mW57kO-< z!S>iR@Bku<%aeAnU5J6JT<)70QfQArN?aHY)_W4vGG$mvLKVY@LwGIth&9rj1{i?$ zTl_+1;3``+H0pwQasY698>CgaEeX6_=1N3xO1YSwKn|#Odff z(s}(g6Lxs@*YFif>Ym^#^4VwN`7zm};v)la!-wRXhk9turBC1wzyiQAE3Rp%k}+Ww z87FnKnFkxJJ3|Chi=QCY1uPN?H`RB)He%0FG{^wEYj6Wc{UGx$M}trqCV+sTpjLGv z!&nlwf7z*P!xLOAz;~BsLR!Qi=}WUyhxG|_8mE4rue~>SB7>I;l^mP&jU)G z`Dx7SQy6Tz^4_#2K;cI6%2zyO_@q66|NC&y@co9DIatW&uC6ic*x~gzocE_WX!lWG zAWp*X?;8ieMJ9Pbc!E8!z1dqA=_j3MFv>(hnGa}+t*M(T?VVW)Ps`eq7WJ@1UYj(> za7$@M#1b%>le^{HzC&*?4S3U0zFC{?VHgQQBUO`Jz25f7aEt`6p_DkN7KenTc%SQYrlFle`&(qIWzDB7X26~ZE5+lL9t?G ztzNadmakq{OIGCFDbz05eDq_ag^V_W=wKb0l&twW4qJo3O|1hs^8vV#jj`8qqa%GN zXv=EPz-Q*q!#?&6yD=!S9d)A@H%|`nxWs~anS;c7Fe{vq6HQ$ zuxNor3*2HXuzA~DU3k@&`r74NYSVSs)VlSD*2)zt5;qxG73t^=$J3aevu3?Ln`^~CYz(ufYY+UHTR&4O~gvUvk2+tGBhIf z`2->$`49xMi6s3AC-+nQSJa$2Kz!leL77R^U{mF@U#3`|Q0F+uF65Exme)h>dYd}> zkX5yN_s+Wd>g^ycrbP=ZT42!vixyb4z@h~fEpW@SfC1V`N35#D4_a9t|LVrtv1>bl z*8 zCUXe|dbwXAil_`9=p_P%7fkg`} zTHqFG0Ryx>T&-QlgE}W1zP8T4=(<|B_Fy6@Vl{&}VzA|03O7!pkoYT68!?wLSOCODK5@mb#D5Sj##&>9c_*%6|ROZp?2bbC~}`j#Cs{+@Qf zteFB$-iws`eYinAH}yirU{PvDyMY2`m+Vl?;4$~ai4E}qkb8f`<&wGg-!y6ml=FLb z*X@p6%T8oJaL`DL--{Mlw7{YT7A>%7fkg`}THuyz0RzfguU}sOJnv(A`^8n{^G;}Q zKsHVzSmZx}qCug-UdLUQh~*5r5`fayp_|c~hU?zNO5 zDI1JH<@G-@RcN1zlje9|nLHH5V|F3^5?`3y3_0bg@Y1*sF%V^T)G8z3j49{6vC_zu zI;-6(jU0TfC6t}f0{!|)9HaAHyLQ#KD>v4~AN*il`-Lym{Py0#T()*i9df5T)$tE~ zXstWyh+4I3RV`o6+wiVuoc-zUPjlzSt7^wz|4r@w{O4-v&ZfC{^_p67=eyLJAAESN zKH|uKH_eULT~#0Y>Idpyul$$VwRd~$v1<9+y5o^|st2F+usUM>zm4YpGB4&AEwE^T zMGGuiV9^4L7P#eDVC7OC3*ssJ{163!qjySr1=tYKxXBGXcPaA_FmY8L)T2GG(VJlV z)jzBFz8)o*VBkQi;G*@L8BIW~3!9xOt)2Fi|R0=JMFXelg@Fa&>MgdrIT3h&V; zvU8am@s|^5TJJAqnDVL^Hz_A!jCS+bKS2KV6PO7_XMtl(AOeA_um*`YmE5iLviGIu zeZ0Q$Cui6CHEZhFqmQgZ?{E@~m)7Qszg1U$@*nHdfA{xw!cRV_j=Asm)EZz`aC7R7 zr9pH1$N!;rpZ(@qd-!2>^a;n+>f>$~<88m>!n*Fu|5V#P_JNvv($i|~_uT*AO!Jf9 zIIrIJ**~szha6N#9e-5a_RteJd)QM~Tyt4{;_{Ey2fy*5ddhb_rS5aWeQ%cLjmf`Q zzi5H~-&$bLb@i{G|9Y)E@y>M|XWRef)BKkD*Ux>e*4_Rtb=<+@+0Fm*QZEX)Xn{ow z{9j-JH~9P{p5i!1IAuJRH#`t<`5M+K{BjR3a8cVp%?1tzn&Y0y5gg7;V<>l^PIf6Z(oH3IA}{&a$Lr!ZyroWDy{hi8;jp^h z_dm1_ef*E+R~@eTv%jcs{>3@e;ttDr_s&0Mk zx;pNz2iGC@zhAAn?_-Efm(|XXy|*@hp{5RIR z6M4||2kMOH{aiilPDjjy{o04$Qa||u z<|5MfJ^3Z|f@eLb{@pz?zo|a();HGM-uT}7s)SDZzWTLae0KfFJ&(2YZE4d5@2xZ6 zv8jIMMNh5U{<}Nl+sG|eEL!0IdJ7PpC8{$PTkh3eBSzw^{#JeJ_;<@MJCs%#k6B5^ zSj&4S3CHrvH1Dr8>hcv@((E*4fb~*%0z~jnPkk^L9RstNrSm2m76Um zdi_2zf1w7ZzP^X|_ej9l*fb(rX^Y}6Wi~=7^p2&18)WuFHwKn-z;eeW2qLNA%?FZO zHg2p--}bILg_o_}ZR4Igdc~GH_$wFI(odc51^YVU%ipNgS6)|#T(zfGty@}O19roy zcVKt%xX5`!Upu&*z4W~Vk+@=tt-jUHTCwMnT5|d4k&9mw*?mcEIC@8|u5Gn) z<@UM^*d3>w2F>Gdh+OJ6UVBx&`!i?P+GAJOh7(uS+Jl$Y)%z~5FKzm4?cB4YHZR>+ z>o=^dT`N}CwWu%b$w!@9$F06`nm5X9vEi0sfqkp%m^<8#_hPO}0CdGCUtRC|7{b$L&bK^Jat>?VA z?(pcR)DPTxQ@#52Z>{&f>#g-8FMe13>H}{$z=`SRH`ho1@^5R+eSWy^d366aS1nuJ z@~-~RllMQP+vYF%+xl^W$kTu0&Gp;&TU+mc`RR4WPw%V~{_Z7pdsN%_$v+~9d{do% z<~!@DxBX`Q!0FGcOINVZdJ?__?<7wXKPd0CxX52>g5t5DOr^Y3H02)$^5Tb2d-#q-?aN8FGX!?SVMzw}4U zH3ftHkTmfW@s{^L=HX_;EP~E{AE;4U?q$qIp|RW^cQ=7a6lw+e-hk`4%$-Q%yX;8r zXYvjxy_}wX%K-6X3Q{tQr+{hzp6tH+g&I9xAnF1koC3vU|-Dq&02Eh zN5Ns^c<WH-)>Yz1qb@|oTl{k+*;>T~4=3>Jw+yYCEs7F5I7dYGN zbIS{l{+D{!=KI$(pYhoI1gXPq$F-Yk+wPklLpNu|`MJ4TwsH!b0Cr3su%o}~4)wM# zf4O#TzM?km_>b{o(AO4t?BnG0=EnZb$$ew(`&RZ=*At)qi}l!(*3{2G^|AGn-~E*O zm#<$|bN4?w_jBJM?%qvx*2`a2Km4{w*WJdvM8KoGdzY?=yx(S<8zl7qedR^}`e}qX zy?*NPcdLVU?5(H%%1i6_KKP!R$5-Z;ZK=P1$D3&W!+OEPPpa*^PpiNG!!OnYpY)iDpcZyjNrxblv5_UGPLtM+WE9lNW3 z>Nn4V&(rJtFJD^sKk}IU46(i5|9j6Qi0rfsKNu$74`?yFXn{owT+af=QS*tfd?!w? z1>PNL?6!;zKA)_%+2FB*NC+5jCAMpL<2Kv+S9xqVZtFCjC&@!P;Cbmh@WyEg79|Bf z`9y%^jSF3W5+TAj58GIjm^-GvU_3t)BxB0{9G5e1?@;F9@(`8Rctg84gZk8W-Z!Ll z5DH^(eJA@aB1hXgo?()#0_`kb`U4LA*VFHF7Nf78L26_523R z$_=$_&5ByegEzN1?dtm0H6MrO)5G(IzAm1-u#P_M@H*n?gLxzUidwOJ8IS1isjW+{ zNbtOxpK4e(zn*(S=WEwGe$4Q+6?O4d7v7|tH^gVL?v`YMopsGsJ7)@S=H>m2$hzXH zt1=d54O_lmZ$0zr^_KIjIkA4_)i0|@-R1B!U-Y5Z*6)4k_Vv^|Us=C!2DgVi^L2IB zV^-B`f95rH^wS?#o8J9`I-AY?y6?}|J74g?I{yzIS7-ijQNO=la`rFR_a4L6;m*(2 z^B?|_I_U+ksfV9PdG70t|IE{C_xqk#ub1!LpH#2=&8OCBoGjJ+74>Ilo>{N?TM3iy zOWQBgb~tT}cc+MPStRY>;ji)V-nPw`)>T)sWxL|Ax+^^1aN$Muo)^Em{`th8s~4Sq zk13#>uYZ2$3+rtgzq{^m{wwP%z`f#(XVfdcddGVGU;j#-vSM#}L3_(LK3;Es&GWxq zSK2mz`6KnZXP!}iOWyRj=e@dq`r&s;uz$0*YLxHz_d)CJ|F_mhKmNpez$pjUg`ekY zOC41Q@l!H;w_Q=^5obQ-r;n?vui29RSa#e!Y5$}8>LuIi)Z4GAxywFR&;Ori)ZM@J z%6j>e_qWxH>N&6fXuaU%bLyeTbMnDLw)2`xYwI+PZ)dOkAyC>i*L_Sj>7oz)LA~^x zs#pH$o9ZJ^ctg$M2RAeI57fouq6Kcz7H~(>Xvbi3nZev_;806Y=Z;}^>x}M52_30- zNcP)zgfm_fj9^lD(u$NjFC8yuHwk0E2J&ET++%5}W5wQlOz;ZO^6_AeLEgC6QN(2i zgBoh{2BPkU(uPZ~ zs-rR9>VsJj*zvnyEl*a@3<(BuUT}ngOli#(9Ns(YUs_o&a?>V0SUPI{0PZb@!wQBwLjkRfizSkqWZPm6q`q)G2@PiNL z<)O=JCA3x|_X_VV;BRj4N`Cj5n`QazTE#C)AG@rsx^~-5%6UB&i^W^G1ssAIg!s_H zsJWufdi2xl?Tqw4I_f3{w5&LiqW zPcZ~rS{Hr&>vf_}5M~T4ZT#n*Uk`fDtH7VDH~z$P>#PrdU%l*yPa=xgRm<=2xcakS zeMN29`uX~;$NplS_1|cFG;LmvFflpa)EI4`|99VBEitWJwkkZX{>-`c2Fee4$OGy= zTh6QBf9c!n3y-*4-Ps!gH(yYHKqUO&*FCo$@&gCg`Oi3`?)nqYtH+$Wp$_A!!d|W? zF+0@jp8xzC)OGHn57m!8^SA2W&v<$L)syaBJHPP1>#;xk3-z_Fe^{@0%*kNhjIDt9 z&qzn!@4wgc@AK9=0WhtC#=PC+oOnTkD$3zg(Y!=_A&z?41ts`_>+r z0PstfY^)zV;n-SvyGPY|@8lfd^yk#Cu6RYg^2hF8pL^E}>p5>Yub%%luB4o}m)+@1 z=taypOJ97?8|p)p&b<54v;LjG;$!uc7r(C__40G-fw#G)-X(PUe}=Z0S+u~S1#ZLw ziLDY$8j~40b-0&UE71iJ1_j^uy^qK-SBZ_mJa6Z3IT4prqSumUfg*V+`IiK~Kh^4$ zBJYvpkXSB3;*<4Dtvq=BC?6k-CvJIMo>H3JFqlFA^mA41uv*d4~4 z`3J~Q2_Y3m0m%2`=&l<|(R3j(@gT@F&*$bYZ>?Cpx>g;6hDQ<9@!OO8uDi_WcF3RO zK6d|#ppaL<*Q(vSYQ^fl40Qm|{)N>C)lx5MAIjU}dAhpRa4Br%a&{#7nE3tgnj^3z zh+NJY_tH&Vvsu0KCS_f}YAyGDt*!N|*VZb6$Ti5cmdjzQmr8SAZRg4DZP4AtZtNU8 zmAlvh-LdNCFYQO3#jiyRU;)RL1IIBlM!=45{8PQ1>SsLY=eY#8}RFr7YDayyU(08;>}qwy^#5z(-x8cRi)*qbsL2P9B@o4f+0jUHO$S)};_1LovWN__c@K{nd55yPR5X zg6^NjTP0rn!spi6U;9jb!^THFzn=Hd!|I^TPpx|Cv+A!ubzzS<$N#>MO5=2_})zAG)-Sr-5I6a8CeaCkC zYr1f~XItj*E3ezp{q{SaP5r`a&aG$QR}VgPzBcWoujj9U$L3Mj%%q21PvJ7;cg=T`Gg>I zx~~FC9Sh7XI0UxiyooOW#*GH7<3wGVTADm;qdZdXN;fy%Dd-XaOwzezEe2_Ypm)6x}lbA5f9=%~-U3%4K9Q?WjJI5V)P+i7uKKti&$b$jc zy1G`#D-mp;6vkgC{Cl0Sx-PkrN5^@N++b+~0;c`C&(&*R^0GSX@4iuW;)&dAQuW}` zc70mlEu>xb%}<r?Ky&YT4yi#|E`?Cs!Pfg8Owf=M@*MsjY9+Rth>x-x>Wm3$72J`LufT zgMYKmef1OSkZZnNe|*Mc>UVzqskQ14-cvttkbA70JFjA=V$46pg<9>@+Z{fu+r|5E z=MTGIJ?rsRzw*CmeEhTOyKl9%uDq5L?E}(|`snL_rC$7=<#pzto?8#Q&EC3VbKjD& zOwQibYQwL%mw#jxTF%zxI2C&g*1H0E~z$l^%O5cbI1FgCM03ZNKL_t*PPv=lZ zU-F9(PQgeDtlWwlQ(B>v6@7EhWxP(cN+My=v38PuH!zDc{{lc2ame zyLTnkVhIyvtcp1aDmQQ4SK{0h&676VvHtn0f2h^_R`AnCs|X@j)_Pt-zIqw7N!}DY zLiN4unr^#tSKaf_d*39@8?wh@-7VJwJ$N053xVX^j*bq0{av4|C*>&#?ZXZ@?=8K0 z8?V`NsBPX|+ji~kd-iOeg1Pzaye$7v7II-oTXycAE_HHJm?7FHhs)p>kTG5dK09`M zHy8-e>c4XWFrR$O&k#iZM7{6RY@KyfTut-m6Fk8sNYLN}cb5bYF2UX1ZE$yYcXxMp zcNpAZa35Tj=Y8J&o!veA_nmWY_uT64s`^y*_o574>AbA#t>mKA*}hG5a0+Z1aPy$1 zfc#Xzd=jIPnW7RNh|9qxdR8)#q^-J|40|U-65YPq(Qf%SUWBTgju5?;4BHoWj0&sA?pXa@dBQ9((~I)qWzsliJ^c z0OLVv9kr*cb{bGT26Oa+tJp)ykt&Ga1G!!i!o%CN_$DC%h$v^|gpE!~OgVB}L<|M{ zFs_~2V!4Hp8mi_?oGcgVXhMQhkC>Y{&U^i;y`?0GrH?NAyeV)Xq0}_toCf4ntE#Ug z)qyQ75~~t#tGs6CS#-tLwA#J!;a|vs6_wn0j=a&B`E`EP(7^OfSZPF z{FTp9JlMIi>ij`b(|)^=>t)`t!EEHG6Or;k9!YucWSOeU!Las2gt^&zDXZh?59bnI zo|w*LOxjE98UbJK7tu|E%;w{M*!j5==i>r?Cy|QB*SV8=^$&e-Wexc@7u2^iq-_F@ z*GE?l6gX~rIfHart7_YplvR52cFvVV))+}6NIpeazS+~Lt^*YHCZMo(dp2Fm;hO6P zzdX+Ywd2`60biTsdH=(izC&ka=k3l;?y(o;eo}pQ_3O%*`CO~1v8{-0XClEn?^5}3 zc~sU^9Aj0n!kwo2Dr<-`u^7nXN&C2sz~K-6-pD@K($TxJ?c~sn4yy;f~vU z`3q*J_fYG&Lq{%ca(^&%yq?W)+(hoGxZ^#fIR28x229I(F6KhJ8P~jvaXn9?*BJvR znSnt3vR+*Qj|dG&+*cof@fVGG8BzuJ`jOIARC}Po>%G$JIqWgrrW=S$kK4&w6c%k~sAAP^D9WKmn zOX+RtgOlA*_!7}?-JOQco)$n9;YNbL*)c;YmF=TxRs8)e(f2FNdt$gr#_=Gl?TN+h zl=;;#Q3?vvPJOwKN0q_XFXVq~oxixylL2<*4vep?`J&e#UbdqE4w|>X3wk!zW2H5U z5o=7e2dQ7$=l9FX$~J!Q0V)m)$h@VMM%UULWs%dc?v`Z7nyzg_i~CsbhP3>r*<8d) z%6bQ1q@|MP2gjTs>M9yH?rItw@1-s>&v!fPo(aH*Pq%-)e#p>dP8|kXGQl2Dj()w0P#=FzqnRWSVMg!lW8xU7GDp5~!5T#@0;R^Y_xf z^zQB*m&+Gf%g0Gt_my0k0q#fFB>3^LTiBYvsRWB51wBg@B?)#$Dz}!RGw#Nh^c&#)k2gol?B}#)OLe@2T%{ zpw0s)aINzTfsCd0loD{xaiE4@3~d}m)2 zo@M5_`L0R+rn!$n=~=fHPd@hV9?ML=5<24X-&N0eTkkW1bE7))7TqtXk8>xGEBcT? z1?IaEgIP}?^dr+s6wL|1n^;>@vbr)*)2_h{BFyw38r_Tok1a+n zO|r%OcKwadN*W-M%ZjvgHEBhii01HQ4@8=%nd8vJhQY>znT6Em33MU;^O)S_ zGp}4E>gPlLKt%tz-{h&61<|6AEG6vAjRk!XASu8B9h}?v0OKoCZD*~rul5^&MQDnx zaqx{FjrdI0mhs_sJx0Q#jQsRh@l9j?1wfjJbXXqZ6COj%j?dqHCTW_)aH& z<`chOV#!8N*IL`yG#d-pJUt{VdG~Tyo3{PEm?xwh4K=F+lGq7b6GQH{)S42P(YP&M z`A<8L-Tx}5(7iy#q7{o5z***~MelTnlWJk}V-Jyr)tnHYUASRi~4qW2CS9k z{~8O=p74)Ey9O)yHtLAK$2y7CONmJ27N{{HU7;EMDP)7J5umL%NYxiNK}x>>Y&6`D zh}%s{i`^!ayCSv`&#(=7cxh7s5j5 z4aT!|7v%#}yhpI@n}9eR&PBIjQmro70a@C9&50?j$>N{s3iJa1J#z~;*6L;bE zwX;+&mG-8t@6^^7BvALPPg|%Dop1;TFhdjGw>M@)bb|m(Rzo6iyJtOZ&(>V7*LJxh z!O7_U-Ic?bX7@HZ?J>Kk zO{|}(yE-!Mug#IXhY2DPQO5J$5|^oY$#(ZWK>q|^JZC!+Es7eEl@G<+?l+C=^Lw

5?EsM=IB_Id~x!f=&gsmG_kzWoe;7MqKi*mmk5!Cutpdl;|SNd1eVss%OrW z^7zmzO2E;c@{J>5Ol>x8PE6=J8yH+Be^Rf4fhVnB-vP&WS z#okNI&uQuyg!Dgd?6sTsNv9Hkr*uQDVXA<)SE#g>DS|;4Pep@G=2vt^z>DN~*4?l6 z(V2|A8T;UsZ9@|u0r+s^dnCudA+>Emncz)RF^Q%P;@4x>-sqT~^B;0|Juey5gT^tC z27Dd05LqkD=mT|}Q)eiGDfo|-C&-7RO#T5E5mz+y+Wj?@j~fsHEYNdH=0WChR~hb! zey_0;>RCOD_);$K-$gf`wa#@7;YohO`(r+2sAfMd6KFi0tGW+Wll|3+%9iuM3ivyS zD*Ih)cVD&BE1pg!&n88OxWhlOa@qJ+X_ma&RBDqr7W<8AiEPFM4|Y^)#H)d5tc@zT z^qdNlD$qY#N$t5_|JXKTN#3|sk#CaQRNRLf?1*-D zwL5f$2HccJ=tR)Ae&>oIUw*s@8#ibTvdw}14Ie%Mfo1cvihrLcCMHSdI)jM~=ZErt z=Y#E3PufBjz#njKrOp~y7bWCeOm~j7WRP_S?{=dKBhao(}Dj5<`G9o*JVxlG9 z1M~cg2H}Ih6o-TYA>4XZc4qK40{5XV!+qu9UiMIUB(^|r=@w%WLSO` za}Af7aQ?7!4e8jnM9lVanxO)r8DyW8YjOo=CzSKYrJ02xZH9OJqh;nEioftoJjt3_ zTEbE-bj==wW2CZ~X}O6a9T_1hdgmkazc5Vo{a*w0{}^$!iI3^~nSMK|21%kkzgS;T zzp-)41M3g$XQ8j-s6{w7B)%jBbx?&VkWpAqbuI&_p35c{B*dP1G!y+KGKGk1H3^Y2#rMDM51R%V z1&Aj=`=OEcFge-6@Zpe6mrlI-ivaEp^U}@f5dQhf|BUXx!)?le_I&6g)Ef-PNoF-q z1S`0oH2^K5FR3c&3YE6uIof)mCbP5i8m~6+nyc*0`l`IDd?vXkqWgT99QH^0Or#cm z8~nCwCvPXd-qnIsMsPwk{1P@YB05bWkI#dZm#2%{u!B*N4Q_sAq!B36MBZkCd2dIVc2a(GytWHk^~LOkdr;D8z_$%Sdwpp0Rlz4kq^-_UbLxY)K*_ z^>j-S+jEBIj0zOn_d>!zy=Da8b~%PcE5^g)A_KP_&P6yRifvPf00dH0$O9kxXUgc! zx9GC-tlTQCp3#f$XYCQW3q;?cY3VARZkY|{8E_}esc(NiS{cNIq6GfC;Qo&}}^?nT3aWYe`xz@~l{J|5okSep0x6R{fehnn96Xh>oq0vY%fgDF32KV8T)T?pt+G7x$gF2zy#i$kbbKG0TvR_Z;E&GzR+ zy@Qn*VUgAgPzP`DBt7j(P$VQ7nr8thAE5L(H{LQ{V|lQIn6>x+F=>})^)2hS18anG z;XIA%fJ{-q{fm^=*viUEGb$8>{xzT7we1h8KK+9N#jxr&HZjU3`>^^l;omd9E zee|)b&C3*^QA|31uxcG?q08o~qT9e8pE@*KX zhfII;CITz(uHjj(3U+(y}S z{bRYl*9E`J3bL=<)$b%%@IMEc`Bks|Uv|#~|DgFvlbVjozw1e3{6<{lm|M9vyQe(3Lq$4Dn#0OOw$fX}%pA+8jE}kWhxb6N)Ri5egTj0j<+7;UoMXTyd+>TQS&D z35R#Pvo}_X6~l5W`om;+O7n*g(_u+odWY!V z9W<9EI3AOwr+UT2 zenat7cA=^xPy906H*2R!VduBVb{cneT^2!Fizqlbp4q7VHr2_)uQZeTYFn0uAE#>G3>Y!{i|d>3>|p-r7e%b`zS0VCa%`NR(~9fJIK2yzYq_buJgLFg z{~QwC4DcMV8PzdC6;RmwGq`AL7x1EA#&QO9q^#TRhQS*Pb+~iY5$vhiM1r#@xo5iM^NHt6wav z^EtXQ6v_uUoRXS0w4+^5%lZC8YA1)fqpX^C_V6_|l@oC|13s-0(n)LX7zd!=PZ)`d zI5vl7dMice{bMd)_*cC+=-SFmA$=r%{5j;bx3iaP?Qo3FjF~jD&}jR-n#R5BULr$m zVr}(VX)Va3jXI+Idc@DAmI*nT>2ng@11Xd*zgB6s#w=hN=1^^+1MS~lKUpPE84YGrJ!e;vFU zm948G!l5Y7Tawd_4qX1XtEj7~_m?RX3ky8*Kc6OT5(-dwaK7s2OTit%EIkM?k<%Ao z!(IDFH8U@f(XoCA3AN`ETM*lgqE-z16Ai(0g=X4H6CCkG<%}@>{J|~5R)t^H7l5>h zit&>~+gm72wl+jXOS~vsUL!hGF@;XXbPzpiGk!NPC*%yjg^`c!$NxO$Zw-X z#qs3Acnb|a`_dM(U5A;thviBm1CaILc}JU^>a?%pn3Z6f&7VD-7foAeH6zqdcxyo? z#*CG(WVp1Rdm(lhNtUdP4Tb|MYSjqP%Dil5-m|M-T$R3Fv+GKJIAKY;=C2s>%9Rvi zC8Eo<0hC4xAV$|NyTpW?0KzLU`$2AJ0X{95_Atf82hhhvH*9besYlJd(1^#kA6Bm~ zA;H@?Yk#*x_DDHuD*D%!^Ny^ZyU9Ysw>H#~QtNt{lE>qg`R>xMRp+x^B}3j$ZV5Bv za|g3`qB5M`(^V=(8<8`k(jMYo(y!>%*;!u?df89l$jkPEH=OkE5s;BmU9|qHGL08( z)!+`%yE==b=1)bGbp(IEeGA_6grJ6-E>)r(C}KO;WpXTtP*XFju{%O5w^)?W9Njvc8rf1;q~-uRSF69F)9kQ$VyN9lRj}<#WmOTH?VQwHQj`fzH)r6!Vvhjz z7B~h5DBe?M%<pC%mQQmML_IMRSaQrr_Uu;h(IG)i__U&FS}&PW9mf2%Zps zd}=T_U@mZ3`_b@IkA#`*-Xl8ZQFLNVpU35)v%ES4P z>i$Gm!j0ET+aA}6rvWISaMnTVa<_Sx(wz}y)n{qWmg5nop$5Iw{-$;lC^gju-R4=p ze28w}xszI@lO}e94VI^5{CJNJ47PK!xO3R+Nqv%uIWki_8iMB{HUrE`_x zWN=Rvvw3pd9Jt!m#-r@IUUm4;Fy)Tke92{%?wWfQ%Sw;(S|SJTo1}I>SGP5W#|K^B zc6#k+Zsnc+a=1RAZv`Ocu=a&!6zhAkB3sq*sHJ*_oZl|1INPMj$0%z>WxlpeuWg2H zysa{PKQs=fa$a{it*pRsM}JG}OQ51cN6*!idKga_gA?$1CV1K@2E(pgy6a3!3r9jfU56r?FcJPd=4JU+vV97uOh3uSj1eeanIcdC+t>4!XvJuK3ES z1tgL(d*^=?YZ*u7dXmTC-M&BA~!~Kyu`duw-1Z0MC6Ry3|AAGheJMG3GExurNT3X?Yp~xf6`d@4}D)g zA5;UE$%M%#^_u)!OqB-2F&;sc$_H?j94*my_wp2zQHvWJrACQ|T7o zjNj?~uE+Z4OU$;wDJEPj*S)l70kv z%uj0t46Oin6Zy}PZ-XE2ck0+Z)_k&W$79lRc5#aMLY&Unkac@CA!4dQG z*_QwkaFLaWjrIH&N54-~c*M>zj=aKu*vcGM&R(PNL%08{ipHWph{T*>{RbaB9K+53 zt_6TPN|7|$+}57n9Im#a!gSp)sZtAg*&nOXiFEW3*4pQCKTm}|Ol!pX2Ctm6%Z{qy zCY)=&ppRz1vr z?sr%@_m9SBL*3>6Zmf&-Eq<^Ca>fH_mYLvX)>Ts zMSX#TQr`jG4Zwq$ugPEMf5H|lyDtAJGQyGM$P4+w3E1PvQ8c8KcZ@f{Q$ScMJ<{@^ zFQ^`wbzQ@t6iV-r%2puwGbY62_Z!2~(K*j3$z9;AIY?`$#UjSCO`>uLqTTKkIxmy? z{;S5)JMrCxbD=t&`93IQUDvI?G7oJ^w>)_@qc~ zID-wB8>^@(9&&Fk(3`en7NfFjUR>9PS6hz9zNy^4zHfH}7Y&=OMqS5U3nwjmaJB-D`m(QE7IV0 zJx;a(0o;Tk+IXC1hESSaO zz0qe9PJqLU+N@GzIYUy|KLdF0&n%#kIxhhhtrm=Xm-3Wqa{hM}r_V~&(7ec=DA4Fw--B&S`aeM@ zxZL)zCewn{>){uG6aGO#$twOC4GYayeEh9czI<=+k5^dbQV41HBV(MX757zrJGj(4 z;?VotSL>@)%uypXjWBB+yNq}#9JpS-S$@smo?V@Ee+_=7$x7Z85KE6UTwaW1En+7& zE`a0}-roj&*U>JErn-S*Tk-<2Q~4$169doXk&;bBO<@-xyr5x4O^Q3$m9N==!;TXwU3*P)2x+rX_@<)Hm* z8pEn?hX7*?{uX3+yRr_^lMN5St`rO>|pHaTs5Od4-yQVH-0Qj{zye=DVs-UPQ zYS(2lrcZoquK7#b^7rNR-WjQ{lQ<58_94O$0-O(n_GtDqRB*W=PCOu3tjsv14>7-= zLvh-`RB|z0yq;1&Kcs}nB1kz+>;k`o!x~IuP3GRjB`r5?>Nc8Cv9t4^>5v}bOnL+8 zN~vga6W!7_#d5r(lkK5A;$by@xtF;D`3KZ!5$y@aPn_9-P&~@1J5wmULzJQ*dVKD1 zLJL*;jwwbV)T47@7+|RZyyI^j_k1jL3L~PG)hS(49E~Y?I6Ireldxu%jrtq3T#rMe z&Li|?_sncrXJy#PTO*y(e$SV!yOSEb6Aah-)T0^K*A|iIr)(cwYms0(-f68yV~Rw} z^2f}rp;t4{wW4xsp|erpWHv`TyQu>eX0aX$H{k_IzT(b&B0ia!l5@HIXQ-!#b_40m z(DENo;rSn~zt7J)&&XSW9fa5__l9wnpuMxXb`^~)l$7<}wlXxu)4?9D#!fckI zd0m0mnU6Ng+!vbJIhXi*% zG=QZ&?$g=fL`MX2BVy+0?1hct0y2fDamm$hJ{Ivd@9|z3rr!t^y6H1QkyF_xG%_O5FO~ebvN+#7FzmY9X(Kwv4mdA3* zSB{C{`C;7(7hfb)R-sN(`sFhx1?hT*$&e`gKlbLfqK2U&$&FFM`X~MZzyRFTBees`n!=zvA-w*C5Vf zT+xgTf&Tny1;#e}XQpfH%lQ^5m~*4$n#wpk`_M$G)x|k^lUay;fE}Dv#aNzf4wX(z ztXSXRKG8x$dbaUAX64HR1#*7ulE|5hLxs*BnzX#@PufxZ(=|pa->)@7W;>p3LAT>2 z$=vUF2dRKIT>lQvs$}kaOYPBWZ_o_HHnRgk1WxEbsk^QFNeC!c7^aXKC|2k<4KFce zv>ry-Uq=w6W_Z>cqFhi%dv!)OK}6yO9InVA7f=Myi+dTje(g!dOKLO>r7l-(FO=HN z(p!;C{lw!XL@UhwzA#+SObeTGbBdue_dmZs7z|pq;Q9VxYLg6>A{&7Wmu^lkKtfz~ zU5%%xDn8&rn`Ub&q3U&^M<)1xQNjlun}>kVqWe|RLkDMG)1a&qGh#>j6Wb{c81*n- zohp#0%Sz6)l3E)8HFB1#i6v4%H9P`R3muV}Cbus16$xJTHq(-*WP`?EK(O|nr;}tT zz$qe`7sDUgN}i9b{B@98Ata{UHo8H%gN@Y8&YImWO|0*y5d*9QQu4#ajrd->a|Hy(R*g9|%&`H?Iw`mAE(3 zgV`tO@|s1hr9<9r?8IHI`<^d@6h3d5k%6NL7MLW-=(D1AcrMrIpiwm25B&$$$ZEdF zq+Z2}a>!0BJ6UcBhA8+oJ^lGA89N-eTNN%yCr-3Dm$F)Zl;P0tHQw!DBW|{sh^xR$ zZRx|ZuNR{qZ+XkcrFGciZ-y8l*LcD&pZYS$`pDoeHP$KXIje{&ID&U8t!@)`2-Y+qeNb?x2Ph&}c z99JL5qab&#*Q2)U>-Ra7XF{BCN)W0jc)|AZYR4gvh$?OEC@n3f!?Yi|QN3+-QDYyq z^cH(+_okHFc9nTZF-YnsJnZa112*S~^CPZBSN=cEe65{79t9o5Q`n3AN>$ZEXJ>sX z^6S56EES*OhtqdH+rfK`0xpBqKlD9SKw;+oLxW$lk@GD%w@FM>*E!lhB?(gaZ zy*e9G^nF|u5SR%C0sWO#DIf-BF`n3h>{GFkPOMp{^(*zdIcnsbnGybO!e;bQKJ)vJ zBhpppPb-{rEXK_umSNg;*Kj9aSW*c_gCGVU4->wt_~J|1qb4KS?v-L^ZTwL98jWH& znKwIllMd;ZobZO%#giz%7+oG2E66oK_HpgSifpjy4Q5_;_9qEU5Rvf}ntRFi^4^}c z!|DG}2#xJY+=`{cJ(-cm;Iyw9n_K=ZRiZ<9Lae@;wMMkR0W)SVj|Cy=DVcUk3kz$@sN*)4u|#y~?lMh&X6%t>#O@pykbbya_Y@}z*JI`!g2Gn40Q z*i8sG3>9N&m=JFfatg>8?pghWfQ*toAEC`E(}x-~vbbWTH&}16M_1KT!+tr_eCwAr z^pwjZQ*x6chsjrez(=R9CbSGvkqcZR>(K+fvOL;jL?b#r|Cavz)66Y8YpQlZS@)=& zdwRd-<#Lu8IHd+4S zGj5{oJpRLiP|;3xe#W^OM5Gzd*89x-&29PyB|h=b2BGxJiOYspkO-P0F$XiEe~pEG z5>g%N7Fje3TF(5^2|Sov#>_*+LR%1f2|UlA#xSe^?vFx< zc&th1;?unqhb`ISzgL|MLn61MP5{Vst#|QF^b*OnY`YNKKjN9t2USMaR0dmY8(p(x z6z5T_o4VAqd$s|QeeN@0g7q`+XiT6X$D-Phc5};oSXbkJpah~^C8x%ycE{sm%Z5_$ z@8@snVG&XpA5ft8;dgwc=9u=*&V;b3Bl;e-`ELH|E6V8yg%T%5J3AHn z?M#JghH{9XD6-zzF;6|PvVS(zNdM2B#am+?Qb5ZirNO$oI_TI{(bk>d+dY3*A*c$4 z?3S+ulsZ+Y%}?cc>}xLfwlIZ6nP8Jh!+&ofGOZ!ivT+lgI0$O9>V9@AuRtoeUHWLI z%$d?Uc8iZb(`ke1bZdp!q*u#jS+FmAk>?jJZ@c9>&H!cJ??Vy{j!Rq}js7|1T?v;; zYs`s9e}{CtGOyCr@f@M+cfET&v7oZ;p9Wqf(=w(ibmC5MJ8d=d>NhOiiVeZFr+4gJ zY4zU$btY09nyI4p=(8fb_&<7kyIkx?t^1t2+Hle?%=Rb>U~WXyx!AWl?>qfp#~F%V zFv}k`zI4{MuGL<+cwR;Y=QwH!m7EJI&jot7raf)Icqv1S3TXOf24&aRbexO}Q8`nc z5OK!xrTv;3<>$q`M7+rDpLhDwi9AoXf0J2ja^(-#v4qQR(f#U{-8gC zS1TF;3)(;{1_1Ll+u8rK&~J9cQq%~W)osvB8NIXG2Eu8Ty`4@vB zRVdA1Gx{WBxley9C5BF?3%j#@L}i+p=1!B2lTA0ov?P#+@|d3}ESH)m#ecfW$;K25 z6t~rq+k8~=ojj<94Y)V%F*p0h!5K?W4K*FJ?;hOC{Bl-DG@{W8tN<&>YPDn%Hn_&q zst4~pBkd}Bz^kdPFzQPqMS+ViMqv2uDOVq$w1Gu5m7&y4LU#N<-`rc2D%3({EY}3- z*Ciy)dpfaPy+g2wdT$b+jBP^s7o&LOB6paYBBd==851#S1bhBQ;b4b5v2N`EBwAAM zDXQa`fz~4+JBRswYAMasAMxE%PxE^Fvt%kG7ZwB(3-Sv%u(7nbN!jIk_@PNWdfrL_ zk(nlBpB%ecyZ}^%WI_Ab$@F9&1lAXJk+yQm`|3Nq^iUQ3rffsN;W z)EY2h9B-SFvU|F`ruH`gi+)5@PWt!jt>Q=TzU(dQ9OHhQz(m9U7m(q4sR$!k421`b z$^KQ38qVP=R};PX0K*Q}%n%#AQt9VE!OvvP()pf|<>#{}Dg(~rX1;?1?o)^^*hM>m ztmf4wT)Ji;I*Zv^6;7az)6S_rf$T1>wPMa@-FpP|O@!z5DZnZGHvM~yHax{Y=*U?o z)n_KRRwkS$APz)nS*0Q}#jc)zFd?8e@_Kb|Fe(Xo&RuI)W5nA9_e~;tjF5r(YxBH{ zG+8Jn`FA6i3}vm7O=I_>Is%G{w$F}Khizzz&aZY9ZUi6n{H5|HfSeO%p(lRIXxh28 z^xJ#FHLi4#%ydVA=f?shJp{?8J4Rd`vbI zXpaKI=%x2fedK1#>>*=(b&vv4OHmQgZsb_}*jQC1dAT@njuF#eOg`iogCqG;mFkz<2&RK^XvB6do9B-}wVkN~Y+5 z^p6f|8W!=xLMLV>XnXe5=b(ETxZ#UNN9KH3B9gZ{s};2hTnogPIcq>xJ?j&_e6)Nz z()4G2uMTw6xjC?0yMBPjR&lQjo2&J8OtTW5@Y9G&W}%hp>faYOU#q(LA97;_MBwSl z#}Un1vj8TvA=~>-6WrczSkU+0J-N~FU7)?~0Pa-VA@0>GS7v?rTt^r2*A8YmQ2|qL znkMHL!q)GWThHsgSz28<0E-RHj{T{fF+``=TLQ)TDLv+C*KrQDfE`0<6;-*YW&2v* zN(@*}?C{)9g#FF(U$Ay^fXyTZiYqMl4I`f6+9XdUvMHyDwa=~xQ8RS2Hm+~9OW?eT z$x1)P$$msZTX{?$*)mb^EX!=Q?V|`^IIMA8#k!` zE}N=f^1PxwK~4R^YyZyO=#2)w!{{e!XllvGqWONmL@m3`0O z{ydE%Tx6tQA>I4w6^CO0|4YGkOI|lfI1+v72r^hB$^R3&;CxpkbfJ(-W4e-LTSP>fKI$y;Jlf~&w+fX zfjq5=@yGg}SoK44f^06gS*sYi7^*hs%p(*X?$`uI92~2g`Lduk=V$4GXP|MzVB-J> z-w&O%jaZ>;t<3G2qQ5@^Vej-+Mw>)Dcs*y9Bg_lkZW!(|1b`!-72{b)%n`sw0B5^f z@!ha_kL_q)D){APcYVnpl;L+nygq%`q#Ycq&sq)n3cTk7DcgoI;K?tTUNg5ix0%y| zqX_LZG<stFymR{|={@vGzQQC}h)W@cp!e z@_c-04>}URm{2+08LQ(-ID?}160Uxi({(~hw9`C3P~kk+&h)`GZ!sw7*2%RM;-*O# zusq^=8(i|0aWhv7GuRPs@A<05ht9nATHE}o&3y^3c(Y}r>y}7;Om74%pViwBrNr@PgXAAfJdir~#&OgecSGD(t+NhUJ?c}WU{IZTaG#%4lzhZAGC$M`3| z_Bth@kRw3IP&0fr%aFO{(1R;Z05Cdq5me&XERKM>OfCmZ7%4yTta|c?e6i*HFAdT)Fia^IHLINj+a9S@tCj`ePMysgv-Xak;l+; zHGy-PQbQ~J;wX{P;NhWPlYHH|iO-wKH)^!X%T9JqX{zX2E~D9eu49n@*GAl@ zw(!R{=wZddl3Rq?eOkvTh|y_qKiv{-Sr^s`TfE*559mtmy`gLHG=A#f`RZKteS&A& zY<)w+i|{L)EXFmz&13dcOPF85%M(C`_WA10ap&#WOwr}O{wkAOwxi~I#n6+Dseq?( zmRHZ-REirk-KkfMA8T?==FtjUyZtClW|RH&5X}6-!j=-^n%)W|GIJN>`YN3a2;(9X zi(cZ@^UEmx7U%Ve)$)$sJPS*kGpTauXlGtRm;T)rGYqk6dAf5>MjS5gS;2z~p*{!Sf3Re^gl!ZLdV33MtsQ_Zof2WhNRba6RHvNYA9$M}9Xy zEkk~hI5ls4CH`pQ>k6*uc-Pj`?&xB@TX?=3>}=GxK3&9@8M5lQH(tvOU4MGztzokI z_%+Mjew(`9`?Fh{{=4}}l%={OaJPnj9jtQdwXuxy>{W)V$8PN!ze-Y*u<6R-G>4De z&!-L0KE3&NKN#B8LVMQ&zVCl?8?mexy|#H6p{?PWGE!s?O!r;#aEpp-=5M_3w5ux^ zV6Mn^H$HW_=v0Xj)voVkY9toDqYJeaDx66KvIC9)HMzb#gZuqT7FOY4m*tDqM%QS` zI8BEvi3@(bRgJClthoZp#=6&hTY~bc@GIc+Cu?cB^0$^u6`AKm9}fBs9f{}cUV?V( zfIQV2=0$EwLj)|EcOo~&sdV4bC3}pu&Vglt)+$Q+C{(>xclgHs$cSuE^6Bgg93?Ik>N8>nisgdWXgRfAHfu$a~1RYS0?qm&lN{pl5 z>gB@(d5f^+X1<;`Z@AQ7*Yhqvm*4A@uWbyxbqt4CZg{vDbndpn?ibuU8tzhF!?$0&)_4eR zphKUWn4$(?+$)KHzW-5t4g(|`l>%#ip%k3HnI?QFCoQ1|T~_>YwN~}ly^#4|T%BWd zZd=l}V{^yOj&0j^vSZt}ogLe@ZQHh;?AZ2~bGpyh{q|Tto}bTHW7e8;R@HsoRSj3< z$s+2i{5en_E#AG$`9*%n_~F>cYJJQ}bnUkMf%B6~?PSYMV*{88$m#arD9eM!`T2Tx zC6-WBe>fU1y7T>Zyf!NXza5;wh-- z!W~)Oj3ZYr$ML?MRBfH67^Kv;f3GEm(^;J!_>nNn8YfJSbXsyq5?5D8OV^w)Bn(dN z2MFR3Y@VCO%WPHTngx)DXbZo+s&+W+V^H*Ki6Z85lgLc)H8gLZxJ9)QC6B+c8Mhb2 zm7rcmrlib==3|GRJYh&nS0<3DH~U~^vJbZC@rN0ay7|Zm8BSA4MJ0PNIkPXnp%F2P zP2+d%%ta@-dtb@RVTjBLHB$`MkG>)8K6`p}iS?hlPfbhCZ73cNbL1Z}5C*B}uowh5 z3uwjO=Xl(|p2xl8f_~c|Gzbz*H(tW&@h*>(W!GAB8^oJ6?_Q=VUDb;{LEev^u)CSh z74;T)wpDw6iM|v~rE#&FJ!KVm5^lH*Y$Jslxq3aZv|@<5*u?v&_@7zdzKbAnKEB3n zER8Yp8IPGbFtw57W3+3<@m`JIibZe+!5J9|wvnv;-3-)i1nnoD2QRy2Cu zv3|lAO=lQTPDbpbBWZ{Smyeptr`+t%!$yc+Q6T$WEzC$@3B7EJ_k!MpQk=2;3Vuh5OHbkEOIwPk z)q{lOp)fSCIomHZ`W6I2i9s=$gFSpQe$d31fD=tW#_RMb_|5~4+Hvs``igHQM`8ED zH(SQsv$EVb{`A=sctpd5kbN9rW1i@7a(yJx!0#hZ%>sPmob--ogsX@ChtQe<%CGS) z8rE>+-cor(=1HER^+C=E5YvW_dCvxkOLpU%<0InVW&7@6N1T8`;j~Br5eZv-5P;%> zVnQA=?Zf%y_GEn`m%293_tL=RvuiIKUZRPq&VnZdmU0!tONV+F7_*{DC1B$OY0n4R zi<6lrUjm8&iBji^ag_?aA(`V0Ka)8{nE$jXG7ZaNoLU^EgetNn7iFzN-_D5nS8H|n z+|AD`^zYW{(AA$5=5+j9mw2ZXi+^yvKveWupi~Dt&NrwpngHK4e-~RcNRM6Se(wlH zi&|NeEaBIigg+wY;o{Fz0GXNJx-0C8g6U}_A=Eu4NRkj*xpQrFS(suBkH9^Mo0zJJ ziF&bM#tA-%d3ehhlHW|GX7Xb?CCWf^;^a2@8>yH=jM_I!>I+ajWz5JFULz9$Hxyc+ zOQzYZq=tK8tq$866@8lj2ZEacI_d=y5MOeN5p_e@Z;2uW@>Ta%Uy)+!GiKQts!8M{ zghZ#UDW;=g$#RX3-8fE;0G7JZXgeM~xV4K7rHkB?F3J@M-EC~TFF_w=*>Wu@kRt=k zV~ACl>+>ZrE09aDyCtJoNoN9+I2as8xq!GDY7guzb0hANr(=GYCJUIsU%${@dBWU`4(Gr;uTLc0vnX-d~ ztHg5w3TN1jWKxf{n_G=dHpc-QZJp%WAx1`GrLiOYAc^m%Kyeh)9&z3K%a8 znYdyu&@w(6nPb%~Nr^j&v-!9Wt`O^n3$sD`^vgZ2aJa#s{{Vx3lXf7`HMz?`yUBoj z!1^4~`(y;R|t&ZrDBe}&sUF>VG7h_-Xe4ZDqU5MuM7{|{2vbL9sLavfNB(1PSV z$7hnu)vp3J71#=fCiG4F@i!V{xf<40p&%b+A{O8DK??+6LtD4V)cQM0JdzjqDNs+2 z%hrcz1hzL30fz#`07ZmF6Y3=TB6g|F#!#A3Wme?<;&)V%>o5|dK9KnJ)-gX;*e(B7 zp%yNu-GJ%ieKOFihW$dGghJbI5F&`rzCySA>p!0Z1od@NBY(DEtW*z4)(wRUbltM6)?s`>++ zn5~39i!Tpu*JyW5-r$EBk1s8Stqjt&zP|fORY`Io!Q7eW!|VtII0?;qz?a+e%m^4+ z{}VIbXsFlM3BXptc`zQ32(Nah>SVI{g7#ccS_}~H?Av*Y8-uT&5@<9I)WagB9V;19 z^0=mnQeZ%weo}stALKgykxNU|x??-6W>}^L0N@~reEN{gqIm=K{=HAiw^X>rFl@LZ zH@{`g2EzKXf^pp3L=GsZdW+qRBD3uT1sci$(WdubDM1fcR#iijzucgeX*7PLVZs}1 zGEuv!+KglA-@MHzYDA+}!K62@RkkMD@srfqA#1cQIc|YV+NG1ejUXM8H66D4uGQ%` zPhhd^8=j-uw01(^wmLyBJD~J~P(@Z^G{9^lp!ARbU$nC)T^G=g6f8g$j^=a@AEh=K zE9t`2vWAgABhro@6Z})i;C=(%S1x36!j6ny zQb3-p6$ z!`#kK-0-P(5VptjZ@yUB1gxy7L3hs|Yi7d-J#irQ0p;x5B`l}Ygx9*=x#>pg->Uv4 zZJ7k_f0uCOze~9J&CTH4ovft3gH#8V6++rFxXLzfBPk{|vJ;G4@c%ic{!+0=ECTsE z_8EaCL!^0b5P2H8hKo*i?dx#_r~`RC9wBn(9JyhZLM*q^(~)OR)F0#Qe^4j(?66}) z9z(3WN8Y5hioTl0FURkjVP7M;Ghd6&q)dUhk$d%&yx=~#1WV$LC6a9>v=-fmniXQv zQ(P0#gfIz|pGfVOg=K-qiQd$d>@EG08_9AcudA<*&3tQKsbhzC-PT{x_di)}dH+#m z8|iFaY+Du`LWC4yB5SKH!5dxSB2zOTUNp@|lKgAJu)is-=_9DCs^w4k3fBwaarQmU zJ;TpYDBZ4-YxI8+O8=~`YXCKVgz!SaqG@0`MEcPBKn##_pB$VhI~8d`5&9(L6pMbp zwG6kPXk=KEAM=6aQEQPy!y+W8Qs{Ev^gwl|ZPpE0pZ?~SFDbX*t%P~1NTt_Ekm-m7 z-118t6ngw~2A|6XK;D43&20QKLgmP0B832|j4;E!Bw8`p%5UvuZ--XqeCP;`(VR6pK6Bp-7GO+B|!1tVWAkS9KoqG|o`f z=-euI!m@5JcsO~1k8i1STN{`(xJ(PxtL*wsuJpEu^n?0Akn3hvhy=SZg}F_ zY*WQk2I%PoZ`VUZaQ6A;V`xwsBU)8>B^)>@1n?6gIDYvNEu%sn2_2abjWh$u#s^h^ z60@m(K~>R`^~bF_MG+N4JY=T?Y;A8uhoK4|>h^B9B`r;XT_(bR{P4NU0J5Y}4f>@qKJpV$c%{`bX>{gt+k4IMNaN89QE|U^SNy+s z^KY@$WV5es+)iQ?rjUV!@!ePz_faNnm3*M-@9G7j3)w+a^r&Sp{M5@HBC~_dd35wo z$YSvvtb%?f5z{z(kyI|={5JfQdHyLu2eKcBp^Lr+%IS8D=^mMM(iD(Fy_2!G8BkE8 z6xtFZaRltu1XS7I%=j%nVY)~hJ}VOyQG*AiZ!sT zRAO^7JYsULi2oDt2GsaFr02v`Bq168>dv4C6+y`*l2LpNd+Zc^79t=TC4Hy*#n(@U{i6L{YG%=+ZMAZIv92^rSUPLEkjc#7Z)4cZ2P;2r6 z1A;%teqHQA(bH5&caH47N~yPV0_Mdn+GhR#cMpKZ2!R;s)FHUQmq`=8~3ZT1Ck^~5)3lS+k(ha4f_v;jl~ zILL^S_(io3`^pYON+*WSLYEWl8(Yw)A4llUY!N>Hs;kh&l|CjqdiqI*ZB`|HXLzc_ zl_fg&1LAV1G*Z)7J{_RY)$tN^`Dj)*j~%5D;ika$rx~QcaC`^`!~+K_MYNO)0nxA< zm-wEeJeW;l3_Du$)%KV2EXe}lf4nTd?OuSJeS3*613QkLnW}RdL6HnsLH7I1*B}}) zLgWC#W8%=x*~NL9u%PoHw`Y@1_r*#?igWtSh)5zKhy?4zhyYAPUE~Th{np5}MoFZA zFf3u3ITYeHpFOE4?_H9}=w}^{Z&ZtAoX(Yyn!@Y#`t4VQ}N6533!i1<{;nqog-^Kdc-6m>b9eB`*E=ohKK}6OV3* zfrA@l8sxkQvL7`BP@$sBui!I=FF003R0S{=)9WYUf_o|W-$oUG?YdwZe47wBx{4il zxI+gSgb2vK@y+sE0#-Z-_sO-!2_c8BL=f1vFiPbC_>as!!c`f!o~PJ;_-KjQ-v8u z1^M}QJo1FiOfHXaHCKO&1;gKYj$f5$Z{SyryJ?NSyk;3oWV&(25R|&;sQ+43UM1+2 z_m}|}**P0M z7BR66V^5JQz5?AWKk=c}5xY}97A?JuKVU!57Y26C-B<*2dBNOpFA|s(q~vBm=tw7x ztC*H8t>z-MacGGZgOK%6Vd0As+F8hH@WzQ3tKc&$CrwRF1uQhM($dpr5B*w9UBFM9 zmU9{NP6noB(Z!7mtiVyV8_`{O=e@icU}EAVlE3wVQ`Y0n`%i$=QG>>$a7?p7DcA`H>Cd5-Ko z2@SI(KYv>N_3hg5LYwkI*QTLiFV(T>*P;9617r*Snu;|kh?t-ClRNTasxB0g&td^R zTQ5PjnnV6FSNz}<_9t|)!<$bbgkjZ53YlN z7DC>^x;-P;^Xr#4bUl#N|Kk6}nR%*X$sJSU?hu>r3FQxVm_4Q}E{~*F*w-tr0J?fw zc_{x9zUd5L9R7p!zy6K%OH3iH0d=g*^Ga~P%{1g9%>SV_)}rW%8)VOL^oHzOxIQO) zQH*!uv8Nm>k$Pzo;aXLV*5Gk8%XrQh$35bjCM7?O%N|cJGST6z%622J>WEwKa7(#K z-X7|a1<^^b#Tix#Mm%RAk=}wx{fFq^u`&R}izY@EW0L@oU6It4?*-(%+6Y&CZ@E$5 z6)}Obn=w`iUPdqEiLoa+RcE|AR;QjiH6fP=6Anf7st`jEb2ZHp1y0-_q8Io;S+uO& z2^hJV*if1IK_=ZZTl9i)^c#n4f$I$JW9`~@ZotX;N9v_9Z?Xs&M`|~@-bug9>{Pb? zFYbNlrX8Y(Gj7*xM57l)lz zd2Di`Jl|r_=)QN&1IDkuxi&r5_D-AhLtT18d7ufpzD#xED*)3fj4CR=JaQ*nuVz3InBJB@Nn zv)01nnuY}yBoo%os48CVLkcR8Jp3(j#p_)9TKCN&Ex=#GSNmL>OGJX$@gZr-y*ER}u$7Rl+E^@fc0DFOa3LsmO+189k zKvYW1IEoAFZ7**_`*xt1GD`zZn3fh$0u<|PZ&abY;TEydzh5=sr#7GxpinC_Qb6Pi zi_n-Vzm63Q5pg_JE@LZqYwYsvwN`?Ze2jh_NPk`BYx;5>ULPG zSM5S1wuR=L!a`KalTFQ4n75KfC8s?VeumMse`j(Q7PI!x3;2y1>Zi4hWl*%*anX4h z&PpiB26)`T!)cT;^-`3_$a)`WExEfIzREHa2RaIEa##y9PCZg-4~t==V>m;)>i_xfsT@rP|rPb z!E?3{vzixp0~GDSM6!CfQB-n_Q2n-hy;{6c_t$jtf@uosQcuLI4N&g1q-R=E-2PYa z^h0L66|A1rL%_-Q8GgZ3DRCf8(6n)PHWUseac7#hmo1Xt1?>&t{-Nr(5+yB7sEERX@KI+~ zTR^z^O{O>7jRS91Yx3JiQr?|-7syTx^I9SWg&aa0*Ez}S1mD^$`&rl zj~RDmow?u*lK2m;yKo0(mfzWqwv&jb1(~WK@i-hZ2uSU8g>*sejnM}~csEuDb-1-K zt$#TIO>{k?CLHMa3Cc2mcVaIdTD&H$R&ExwDQ&*F{}H&B=ADFx>~+L`o^N;Ci&zyO zaMTF7#~hBE;=f~M9{BWK%!z!F8?#-@-&zsRc{wR|2O(?gse0pXmBs@hQ$7o1u`q|J zu82Y~W`eIBWQFnA4L(7f`yZ3ET2(%>acQcmK?8_#PXtn!A?cGaB(V7+3qY}K88)d+ zWMS$FBgKzvvIr6r9S$TCr59e+>T9LAM{vJhVtBxtEwv*h;Q))_E<_VI!tO(ir!IP_1(Q;w zjO!uMUcIF2Z8vcqo>i6DXOYAaUT#t)Pg*Gi_Io7`q#_U?NGpUAct#xs>fUsJ{BTiw zo>B0>cy)iztH)ZFdQz6V;`FPE2c{%3dO;yFaC};t){ArL_HyRsxk;%w7e523moc+| zxEt)FR$mC@*K3DMH)o0xn z(C=FA0OPZer|_{+?;Z?6o5h8frqTT4@0j$C@Ib8tv}Xc1#f&*eX>WFTVbY%C=S~uv z8`s`8jyRELYXI9e3*fxCV(C5@5(Y&;iB=}9%dz!wF;QBv9C*qg^A97${ga_{WBKGg ziU#sgobcUvV)h?SjVe3whwFARRw>C-nftS@c(2|eW4LSgRtrki!q)0wJZ-hGq~Gd_ z<3AIfKxmKMzdznIT(zRV0rqdVE?5-Zb1>I`v;fW4b@y5>$~i?vV6axhT&-LMF{4v{ zCFNVcPi?jhv=d%KE`^+&VAJ3-dP18-^)?SquLi4FqM*8(m&U76uCxcSm(A`Y=v z+gCWe;0)1RlLV3n-f@q_oO!#!ajpA3csmw^aNSQ(IzsSHR?$SS7dFpkp3ir_=Q_VQ zyD>E7-4ugUnLQcE=G-yy&QQbelG17i!ynx!CwM_KKdJ1GD}$}YzX-1$JpW%wjGHLR zZysPvmf`t&OFMdVOg0LiH?RX^gy*eUsEn0>Y4qO(DgR#wvVCoVYz4dShG(v;Z0!la zjNR&^8#vqTQ1#hh)_7TVCa<-8aGTBs%V^>Wjoz&E3Zn<{2c?Go;eKD#16t?hSq%4;?l94*M#P7sfGPQ0Kfc&k;#ns|4Zckwk zxl+aZ2Ir5Wf${$W=MAsy5)NhQ+kWX(@tx&9E;yM*JV;5elb;pa%jhNJdvqbFk}8Zh z_}f1Xyi(Q%0(YVM*blp5nI4S8MgLXU>hPk*-ynKV0O;NeV&)s6T#l4u!WA>j=m>fJ zNtr{6(dQK0>smzz01-qE`m8C};R+%`+&_akExb1#&5mpVW9IWU^0j!46`i0hHl1IB z$hB7&+E0F15uECAvHQcK--M0)kD5Cf&Kl7b#w#7W)Z4@=5jo z7W!U|B=Xsm;_phIe##&40 zN=xB0O8U|&7>)Jtm3O!bb(Qejq5hUj)?cb*&DQ#4ZSdUNnc+aUO+*7{xZGnNOjzIw zYnH=DfAS4VvfRpRJv2ahR_^fY9Kw)@P8)u+7}VP-gRDc>%bz=`T!%I_JmnaAhuyjf zX?d8-c`l9tYORXibyaQ;!iJML%uH}lb;Ys{LH`?%@3%2e8n)to-`esO4L+&EoPMzB zJ2$mmjRswSgteA1J~OZ&C_S@wu^8j1=157*qtQZ>t9J3U-a^0Vd-oJ1!9_a7sKEq} z_Q6drIvmKJr{?wf0q=EO=JzJJ<=3_l{9e0IKYd^P2Wy}6;G|G?clz69D4{9K2FJ#B z=kv$G#z^ygSmtqMt94+KzYuWn-~KP}JQ{rSS%t;L>(0HPXnX;wj4MDz`FoqLKloYE zKu!aT1Cy1vKXEi!&b$hK;$vs)<<|^H?(3RbpI|J_MBF6kO>Uo;9YIaO9PNb z6IBOoG6m(|F0z*%<}lfoWMuY7BM$iFH370o=qB8}k|pHK$rTpcuek$_GJ<&MBe30z z+O6+k^~y3(Xuq2QMS}SH4}sA^B5wjDI4pnBgbB98yeQK zE6KfM3)-h1kqr=DU7q&DZox>Uu0-Q)cylXTXZm+?PpT%|TX1qk*PhIo9E$!`!z5la zYI&a>5+X#rujA&lh~YOo5~C%##TE8=+w@{9exK^j@;eMIlc}Yt5gk3D-&Ih{*4PX6mci)xrWdqdnjKJo}N#@;G5jCgTofWpN) zZ4?8ir~Y>h0tdPS7r!-(&wj{b+hlScC>moQ0yA`Z2$HDCK~5?SIKPQ4VyFXwjw46x zTCD9xC~T?Eaz|b_!81ao{F+UG3sBPHw5S!Gx}yZSiLQP;Zz>3wnm;B=HMaB*juDeV zN)b;S5q3372MRwQ#=ctwnF61zu0l(q`f)a{up5C^%#Se{Yng3g3Xlr#q&Q|8>dY+h zN923g!&##B{b^9xhqlPcjS0NOcIi4+R4UGFJbCCGc+{1U(CJb0Z`ylwm zMKg0fMqv#tzIS#E_L*YVXR)`qty9sZG@WM7kY$qmX8g_=I6C(25th?A8S&$e*gt>6 z)M=)m{#8O`fQFzfEzjMp1@Rv!GUHVrk>WS=Hxn=fy9vBOeh=Wwy-o>t(w$2yKao>1 zUgEeI;U3O6cw(Sa(Bm7LGb#3tk{M-k2w)I_LU_p{-eGJeoyR#gAeB5Eo0NtpnkMuk z%3nyLxhepZ)TY#e{L{(=^-aTeGUvzp_ClS?YzMzlZ7jHBf6vCUvD}#~AWt@UbX*>I zV4<>9Es$5kuRzLYP!vP{r_^Z-*h{~Ra6uOjk9$Jbu)}%4wQ(%m7VwCBc}K93Xq!Tp ze#0QqAwuTps+=Vi3ym@NWg6OsiyaXN1VTIydVlcPfNIezKw?VNbkJ&@3RlaKOcE6L zM#Y^BB~Y)vE^l=OlnFJqkT!bgq!buK7DT?)Q@BSZcGv78_Vu;ufpy(cMxoP|)WexaJ!fxgl-(q| zb6MNt!s&7K=&9->kyVlg9<9{dA$cL$x&!mtNxviQ)=?YyUk@rdqC{z;+zke2_M)<= zg;rYJ?>B|o?5z-FmW`=75-|gGc62#Ny#Ag{5|O#Wx@E_omA$_q!-;hm)c z;@6Pbmt$|m$3s>A5d)CzF);48w$xWP7Sfau)ovdlL(Vz?!( z2K=wbk5>;CA^4_-dw8`+4}0B|KH0xBCfJ-{X)l1xzz=RIyc2|b zhsb~k9Geare4iXqfL7i}Y&gTL)-bo#CLiuxh@r@kwb`XQ2+Y0E;Z26Uk6mKxJ2C%$ zq*Q@z9$5}1<+_&hV5&iF$O4|`woA{QvOai0%KvQ+!Kuq>Tf4y za13MMFKGF`+4} zGxOal<~1axV?plfYcn5UPf)@4x_NPpKKV6Y>&b#nLZ)9a10iE zm+N7wO*HuWv0&}kdz1%sgp6AO?7<8P{8ONxC-j-U37KF_0`_+JQ!ugkC9sRk1O@Rp z^Y?>6J%>tfqIKmfV}kKk?UPt>T&8EUjGv1URxz)K=_5!2uv1XU8`Kf(3K{Webyeri zdVzX9!=%_4;*}@k@8gvj^|^$IYqu;29w<@ze;$yrt`HkJ(8N;Gq!JQu_Yf@F4%gT` zoJ&fnZcZIQb5U3k0C1*Nto(jJ7)M}$LxwE7+rE=k_@#_&FNVJYGCb<44V@(tb$oj` z-KkD7o;3b8mCb??yM4`Ma7NX39@~fKE`Um-O%{!V{^ihl-AQchG16r#nJ4zAT1RNQ zzgYs*8KKP5GSRA%zV)T^sM|ZWJC>GFHQAHxi|9vQzuG-Xl$z9*fX3W4jwNSues8 z<3s%#TeJy@5F;P1E6d2Wa}6ku2pC;2f^bC;zz(Z78#ENNI!y?~^!KtU5m9DlLVqF7 z2}CFIbZPJO#=5Nt1CGkhx20M*?Cf01h$JC;am0usAhmqOs4t1EyV;oG?=@sVRB2Ww zaBcN&!~r@G1+a<5pb13XCJ7-f2}%62DY2>_j8Xxyi1IgN$u5OV)|GR*mxCT4OC!a& z>V;3BiIpn^cEQ-It5lMb(7!N(f(jMpBQli*L_|5#3$eSgExc#qhtk5i(a_MwPpQFc zOGUA~xltwUP9#p?q%-QP-b<9cPv*_c&ZV!mSjge;#xY~F3r8KDZX##|yBn7ImEUif zcW@Y3NvUP!4-G!S*Ubx_@h=(O{Y9PDX9A!)=$R)tIl*Fzfum>cl%Vo=rOc%AZBhu)QWDv>HA${ zzSLRg6EWB}NM^Y!8)~z~QpQttUzsH-cx4UN)vb0w+Xd%h7nZ!SSPF?SNggv&8*|S9 z-@isc7ZzRQ#F=j}5f){}CBCPoyJ$WJ4NAZIK0YI-?sDs}V z(ln0}Ic7NW<#Rs~5&#lL#N>&EauxszP6cu;ImQVh8hb;}-0HUwUzbOp*3=JbZOhYI z(I%7prod9UeR3s5x+^=D1MsH7DOza4f<| zv2#b|m1BlRl#mne?PNvgpz2ookv-zqV}t;~^~`=^T}z)*PEpukU5;UHg63Na;3%9H zsf(n~uQm;vKyCfG*+kkSTfoQBB}fx*d1Bq?m~%5`RQRpR@{*V^qx8e(5+wOLrOyMS zle1Ib4IBbRoutdm%(|*H!s6)vkG~u-*@a0 z2)he5n@+Ypi%FdKCluMI9C_TphA}>xwE|ULzI>g+OQ$?#%aUu(Cwc~<)#GH#@(!6w z6>7xc)gEj&0fxx(65p8L@S=5o7Gg7Kq$i%H9ZlBnaCcVHw~ODOJm+>@KHmo%S?9NS z1%^rf(7`MGVt;G_b){wAYZ1q+N2wOIDg#fN*CWV{I7`726rMj9Fdz((S%!3%EQ64T zkgFfIs`WT@N?5>NmnqX&TyoYj^=FM|-c z?2KWD7Lmt!z7BrGLoS1tyTV~pz{l>7YQ0BlSIdho5e^+iamAUMM#q!vqCAfCCWjqp z>xI_RnM#2gKyZN9JV#`Bj4e6O#%3>i7bQAFXj)&wW(U>BdFpO8dX=6hBwJF;bzhgM zT2d70cxDOf$`y%K?n8;Xsy(8a;Sn00N>zIUSJ{&jQaBKI>u4D{ppF28gkm3zlyj^45pky#vM=&}P_+iXoA`K^Pq0oPke z!;I6>Wc#7YW|VMmc)mT+O;g{T0Zr zZpDhXszFMh8Ug2t0;OBepsn}FOG(|YB#Oq?1}hzpaAvMG^4rBxP1&6MdLY=4FDqEpf1L=u^I zjWmzjsKBPb^S)p<+Rm6=Yj;IgE@$TzqEXOHEEr0Z1aSNAKJ=om; z@X6iQnnbK-A^NLc#$W$=k@m1>}}u$xAvto-F0sRr<>vpGqR2ZHPeEl*z<);W>;yoK)eVw9neO-M~e=;gE%Ro=M zSv|%2BazR3=wMELh4tuemGWfE< z$iVbaX}|jG&QK)bXA;KjU`Ryc+J+IAY(pmccl>1>GiW0Hxm-{KgET?w0MAX$Zf~C| zW=!YFgAH|$u8`R8vUC|=7rG9-$zr`6p@$w|kH!R0{IMQe3yZP;fmIHt{!nm23^S3UL(qGC8(G}x+z6WVP^tr% zv2Yy>?GXxCRJ0R~9#KQPyF>hJ0MYw%-ZrT@DiUO3KW32X-T=wjP8+7Zd(3M-|pTLUE+j?q?ihD1s4~$&Ujy$FOp{1H%=q^gWnyS+b?V7USo<@5B7t+@bUIu*nCAI32C`)BdcOY0?&rF9H-0}& zPnm8mwJ<^=VIIC!u-civs z3B&0KUAs<&12Ra+{Hz+nB~2VME36>1 z*D<+{U@xFt&mBgTc<+ur6LZG$EufSKS47B9fWiVPy`O^ICp|gPm!co$UIR~FS6^55 z=k6$Y&T-&Jt4&Z$>t>x?lDKjoC>+|hwch!qzpu_JA4;7^F~3qbl8- zM3ui9isp$MSKH6lRR?#}Q+d08?Xiy84p@YwCg}v@PSAk69xiSDl~SOsdG+V}hCOu!jQr!}Q(jHpY39aK>t)c2 zGkbU4b>YU#Rxen7`kn~{{k)(Cs)bVSS~x=(KvaW>bOAwV?Izs3k|-*_PyrC{H-9oL zG#;!w*eIeQ5X=ube-W29{)}DVd_YTI7}7}RM5KjSlN8!?MNB-Dt6$u&ce+mF(*b3o zoB`Eba~K;{-VQnsBh&u0tY1_uUi0(4$*dn#st+@8*s`F_+}HCvl32~)Uu||8HMd=) z{$n1T{u0ckQJJ4q=CpRzOCdgEk zt+c8NADcTqKD*;H;%!(f&8rD1h1zv$gilOK#C_&2IPlJv6;d0DoWzOg0r?WEXai*u zb9+i2!Vmm-doIj0csMFe^laWO%soeTN}3lmMNt0SwWs&b{&o0G(*xxE!SgZkM{a71NQKAFu{aPGl8BrrT)orV zqrm+Hmt|KuLSE_V8c1KH`r4Sv&i6}@s@^vmOKJJ4?eb(&Oxn3tSPjFlsK@~taDr&rme<=OC|ww62pkSn6fQSr z$NiinzmAU1^%!L}NOjk%IUN%dG7LOCX_PBh)h;kFaBufU7fZ>@r9p4zlQ(b6-6tNX zj*qQHjq`kTKwC&N__n1R#q*ye9R3djS@5%Uj)1FoU6<=jJZI2I4IdQGW&<1BE2J+J zCMzG*;jW#}>`QnSo<`mlpQGL#zszn(mXGfK-hMU~UN5$lhBL95jTg^r+oP)=qIc6e z5NSSkOs)q8{9cp;f2vHR{Mu<T0TqtHfkQ>R8jos?oUwAF#C)X6cx%0JL^&tJD9awZ5YJQX_kB*D zE50@AmOcBebg_4K`e&+#r>&g*+*Y+>w4}Y*laaOHQ2w+d&xuR?j1xydgbPilp%h&y zpUSG1UHq67Qs!JUp&%B+*3LIb6plu6R?OTAO0qq7F53#Dsk+}4q;c|hO(0t7_qXFvdWH* zg5d?5h|@SH>Y+JLAp?Lv>g7XgB%kQ1xQfY!8?wANFxd;H>fM{20SN7MV;6{K*AWP) zQ^!hpq*wkzdKA_2|BbvxZJm>E=*8TGr3w!efJ;9{2kwi&BA+y*?TG1$&8Hw;2#~}S z#h^{ATNbWJ!N(-X=ZA|Tlm${l6oU;W+o$x0Il>M>X@MM2+)J1L@MOnzy4sVE zeX%sDT`z559|}yi$?YiCn>QBa9^L1Gu5q!rW$)JQHlpCV%8cE zBO(Wd=oU~z??{TXcAKI=qW-*n^hLRZpA0QiGl=1c2t)jWTbgd7PxD{~3Ni5WSnt7X zfwR?1QhRKJ?f}U-MO>j#ieNFR#v%rUpHfMWFbU8W1QT;&vs-2_}|NaFJ z!iGF4SQ5N57+Q~`>VH~H|NIg%_gEo9F8wb3-Di5APl&rOLO-NkVyi$MV+@YzY!i}V z5jY#wdk~00B_=D=!3f4v@6!ZWDc)fc5q6H+teaFik}V010d0mpDs*BzBd3b+0}c~k ztXFV%1d=VuLb{HY2SX~Cs?!~b6j}laaBH7AMjlx8&E~tMlQ(W*NObLIV+?!mGJaZE zUdU(!nJ8qZWe1t3G>xaGm1g^*FwTMag;kKeTEfieB$ z_1Ey1q>8id!H*@LOh5n5tbV3V1T$mXF&Xtme_iAIgHl_Sj3@yT6D`yP0vLA`MlMsv z(i#oe6}4Oj3zw}_%)z#?Y^M?|pCl0e-K6PKll8Ips=v9uvL<70;~y+N!{)7XIAleg z{+xQNdEEAFeN*Yo{T(pZ@oi46y*9&+TRSWoFj&S8!QqCTp!9H$$DZ;7ye&YK&Ic7b zwK%A_7(-{shHG&PR&vroE1`9&Ju92?(lMdpZ5LCm(K``a8b(>l-f57yG_GsbX>_2| z*{BC3aIa=}TAc{V)kvu(G>KMiF7zeues9md!XvywQ;8xdNKHXioyN-J;w{weaCoMR zX+hWLmP#$`!aRTDjt75xSvF)US3U2m3 z-?{fYXWuq{%=KrsHrp6|_SMID`}0oPExX6pBJvr0f7&Y*%Q#B68qWFD<&o}ocHwBU zSuFYh?FY!V$UQ7Cdlo9YKYcE5>5j*S#7W-sHeo%Od8@*3aCv;nL8bJ5N=Afaf!*!;!~9_-eyED6eLpG1EKYugAq63K z#Q5w7pkHuU1bgrl8dFg{;3a72U*HuA^l?lzi^Umyj4-cYYz~psNAmhE4M9&4Q|Q*6)2QfC4>to?7^6nk{Rtl}`yZIa7Ngd$LS5=rGAT?&Kg}Se zPAO<9%>Us3E;3wLq#7Ec*0ay0Z`)Pm!JZd-9IXXjOEwa{!ufsetwEVow0bsdJmL~P zWN}nF5pcOCtd4afe^B`%i9lTdIxGvOAZ!DB>$!vy7nx+oJ#X#7{XHvh4l;lOxnkO# zBt<-C`d8VAv8V!=bS0)VWvQ5d2?rkmF7t%WCdp#cZ%mI+HK;zalh|3Bn`yJ?tp500Fw|nW1jPg zV;##g1}2$7A$mxFjaQ?;sP|(FiIADh1ZZ!T<#|yt1Yybro+jnKc~;GU%c`8KR?69x z`f7A$IAN)3l?6>oR!fL927}e#+6_lq*Z3``*`#v|}a_(D4+GhelpfAdJkMwDaTlkB7g2HHWe5pxQn~I!D z^Er<{`X5cLy)rq>t=owEqsiny>@~7pn$2HfFq2Yy$MQ+I9tjXURA`cZ<`t`YJsj&? zzWJ<`T0Ype2k_-jdFFH~imy_st(cW;K zTuNy#ly`p|3*IjuPrmVo*CTlpb{%O&<(;=c4J_jPF|#~ z8dC;MweCXhf$omN+291R6C@)uFz=mv&%afB~HG5i?t=Axbl4# zcW*xDUT3sh;gscBlUpU}g$WRw()&d!vc-6}7pj7zulqdkMD0howpwuEYjZ=oh_Q%s zoXV@e+O?u1^i58%(12dDBdq1C4Kh_hq(dU8QF?KpvHTfflK zL}eAKZEWOn&q&WGN#Vmnw|#ic60^gViXVOyDALz+1*XoeHeIrVwu%2CJ&?Ej(ZY0nw z*}F|n`R&vfd}a=sA@=`08UYdz%X&AVVa)++6M zkcxhcO{%3B=mB+`;b#+cO1#Wv5TzU! zg$uBeT165tq=}}R^q)|`h_(hi#sG`XI2UQid0?#9l12S!$^t6slsvCecIr z(W#xsYjo}Df+t{@zTNGz-P`A~-PL@#MhDjM_SS059wCnrYnsJ`Gq(Nr#9*r-6-Kq@ zSb3eF^|`}b#;6Ka)1~Ahg_eGYJlR#)K2l+SO^H(v{U-*^QsliDhc`42->f#42aRRa%uAj=d2imQXi{VjPqvDBbFG+t}(Bu{3H% z{^K`N5)jZxuLn_Wd+c&kTmkk+{nDGzC%@R?IJ}qaHyw{jHbH5&_A{hDKY@Fsj>O5( zhU9e8zw{tLLUp;svP<}C@EL`J+{J8Dtg59G*#uV07|H`L71kMJAwirwyUfI%P$K7X zbMYQU1=DAcL%?y8p;%|{4@0bs#a=w+Lfh~zKN$a%y?^X~c(@Quri;GJ%Rp<3B*glxjLo7(l=h=m#tyxJt&;i}0#TD+;93xcrIxCukyz6@PYQS27A*R9Q{K4~~ASS;$u^&*eI;Wp-Sx z)je&y!)od8FJZbgw84g_AC@VGaZOuvzHg^muS_`4v@ub5XMTYEijI9+v5M3ftQA+! zvSNva#VcM$28K=xDUQvF*(o|blheCuu_U%)?Ve?6HD-VT_RNI%gx2=%Nrl`boh(y} zZyhJ=|HwbDz8I@dvqunZF8Yq@&k5s)>6L~`owr1#e#d|q5Npbt!D6vAX{{~C_KRYy z=)30wefsL(>{asX+J#>{;GWNUIc;Bcubqn3}fd0J8_ke*- z!q9nC=GSX$-ca=GDnw&q0HWaH`A`6@Hao*gi2&Wo>GW_QQ>t9}MV_jGxj#u_ zbor+xTMc?%J$wBHyGko_>r=f0VD;fx42sw}z0NJ}Et585 z?U_)Re>X{{+O=W(YcP3CWD?sWy=+J<_u%g1569Fq%twlrw65cq8M%uXq|w>cFUcU( zyaAtgGsf1St%QPFRmoWi*a76r+RSN^C=_1?fW?lubMq5>-u{Wsdlhn6UFG6Ez+F}^ zyZ+8W-&*mJl1g8UNvVh1NR`f6@ga#`BpMDWHtA|P=tz1oYQ?TrsTPNi(yjxhEheq< z%#EkbSpV`F-q9mvVyxVmd@IkHX^RW^GpKx_%MM5*la!p#o|TMRKGJ0B{AephHv`<& zo|r|rgNb#?qEzsR*XtaJUtkLz<+`wC55)&RIP2ua(_LG zKKSDVNbChjsD8?;C3cB7TSYW}|5YyZ$3!e{6Y|!z`G6HM*P;$B*(Y9Jgthgc!pXYgsCB?-1!&osvQ!FQ(F ztg2%j)@CGvv}whR3c5(7B_tZmQcjbTiWR9^z<(Am8QjiFBrw!d-lFJ;0c}>chhG3y z?NZ7EDww$!xs@p}Ro3w>6lYlDBP`2RugTGKm9TuqCmb%ftuuB-a!kF_o2>W2=bIZr ze=^n=7HO`!EQ5wMCXMJ3^nO`Pge|E?^;ex)PK^KY z7*~Os@z6J@;m7(*aKXfwc(pJ$T`x89|6}B<^WIe;%qUoHl-K2x+9Vi#sp}^2zXc8S zxK=Y>Ujot%kyV#3Xr?Q2XJ{L;%?}MDrAHdp#d{<+Z_gwM6Y5_NaK9=EHT}k$S8i2> zZNPmGx8>zfND;Jx|2_KV?hdrP=$2jap2=0!QlsLFhaZ2w>X022{C`@OM825FT<79E zjmk5Rrzs$lePu~{gX=Q!ZJW^Nn6a|jE}`YpZ2s;I)8wRcS)YoIsDZrIQj){Ix zeco(5a|bvf#|piAUoQ4Vnb_YCl?q~Pds5ZL@4oF-+a@^p;q-O9J5_RZZIh*(g^|2O zomxFxt2oQG8T|D$_#=1^uNmtoSUTyVCciYN678_)6M`XPZPNBPbCIMW)OFud<1=5- z=I@etrr!i#+pGf24OExoSef5%s^s=Nl29{08vdZVbhrt)9_p8zCP8r3rdck=>+zUC zKkNl5V0&b7N!8Ja+7GRi?!5E1ORT{x<|?lw({90ii9bBCHNwBjU@$7%jr{q_+MIY}#%E!1!*g5t zU;q>kpJpF>b&W3VkE@OK^uIC71s*OpBdeAi)Ny!n;hD=Fp>zvfT2JgT=GvT2x|_OO z&6!QNypGJg(*idTByQ4$NDz?E=Wr6Ri&%o`Ll=&vzSpIRi4Ui`T}xGy4Z_^UNF?81 z)6uzz^V>iVA0s=4<0k!(Zvqi`{Y{+d9`&4|85;rp*2-Ye-|MhSpl&wfKxkyQO0Yh6 z8@*l4AcUkT^efU#OZnk?Cb&wFJ~A+1lx@mka7cwUg#);1^VIc25t> z(`)V{SW@I;xG#Y_nO9(JApCtaM1V{M1aF3zjssp)Tiqs_$2Md^|6!1>< z3}Wn5BM}|sweZm(1Chj3k}k{?b*N7#*bZS_HOG|dzttDkG>8m>Dxj95KRXSVXB@o` zvm0>*1B-W|BRJ6hk{3~@=z+(rjl;mGu%=nJuh4j8Shl#8xvRv*)oBrvgj9%XCX}AW z1xO#O1eKkRWo-x{;d@BgQ&7XHV6|4Ipx^BW&`gW4Gt%-4#yV3u+bJYJnC&t4%7~wu z;a}?kkio0Ym%gNf`wUodtLO|@p(VP*pmy#iLC8M3FNM1kTIj3l;1od3rO^0lk)FY= ziu3Df835a_R@;8;H=4U9)LhQmpxk|zT_(x~d~qCZYuF6e*4y=y5foZ3WgO(Gf;Z_g zz&UU`>0pY`xOr2mMY?W80GTm}osb$S>^{l1f7V43zjJG5O1#RLoA}&nwj}x)#1BOTMxcQ6^f;v0t4XaeL7GDF6vz`udsK)&K4%f@scef%Fi#6?f8__+7B$ zqXcpG;cDgNp=aeZgXE85@7cxI*7}y-??=!J%jvj=$1d<})DNa~3CAhWEcCN$&$!;^ zC}?}9JOGWq<4pNe@-fkOetmVi9=#;v&O;)+;Kk+d`17rx^elr=ufqtwXXU~4I)8(S zB5ov)C+~VB&x>X|QG&SAt{Ckr=y??et1pzhgETvlc!P6jF8(l%KQ;>mw&Zu7m+$R} zDuFm+9gZj{jBf>dB{?RJl@awOWsQ$rVUJP)f9$;4F29=d`+NLTBO&i#g->t0e?*ja z6ztGv)eVN^Fmx%C@%JxMvxEmJ^7>7Bapn3C}eX;|S^qJSf!c>bUxL?6TAel*_{gF+A&$$GE?T)F30(A?<<>*SCux4&a zj(SGKS1a#L=0DGl26qR)EgVSxb_t6E>*@^{kxJeRv;UFlDX@3%1uMG?B+{Nf0+vx7 zuoVyg%mv*VBNs?E&y0CZmbtU6l1rxPRodg&{v61KL295$91pigS6%ub`{;lk8Z{RH zzO%-5Mwb;=Ge{D4a7JumOUw%MJT|cm^SH^k5(I9mZ`^QO2J~obRsROW?H$|$u9DzI0 z_9sxbpZ@C>-efQ{+E9#wmu5jrq} z-IgC*ELiE##wmYn;VIcyp+eEgthFh#UCfoDHpV>bl)V8KVjtFy41xu*DIs}dogP1# z4HlgSF<2QJVJB4(zuqb90z*xDBHc)ka_LMaA^8*3$>##{T|T62tKah8-FwlN z-q>tOBRqB7OWJN7U3{*|wdE4PIhm#Npbu6$M&exh+PX}1Aq8uf7O##Nv5?pj|BIS) zE~wmY7qYdgt8MN6IsPnOgjHKs8@M+F(d|J$b{#w-@z|yd9rjA>{v?5=wA?;AK5bqW z{}}qWe$`XkS#SpLs?}r!L`Y#;sdXFgWH__>aI&`TJc13k$ck%2u(vV0zW*4+g&+gs zMf9NDhG<~|#(cU+&Cz;h@~HD@n{#a=nXAJn(80P_jVMa|P;_ORU}q@Gs8=9`d7)eT z5H*;jnsu+D-$At?vQr=)+ssYU5Qq(_i6fE6FRef&EUOt)#FzlFw!imd=lAkr;FZ*b zuhmANJD8Ksq*$Z8v`KV~ul`!Y|2wziVn_4iGTqyf27V83X18CB8n3Scew<&G;enTt*EQ*@}- zkpZ@4pX!`?F==pHD_JhAMHdWQF+>D-`Jio;I4F9I=zLaYu}y7es`LL)Lw}J&{~G4= z07wwj6&gq~`n^I#Puav&NMx(R5p3s<1a4>iNa^AyxD{rGopOkhu*OJcNeKmvL+cZG zq+8Z$SYe&X429&V=R`KdZ6%15u3KYNIAj`_#P1NJI)NQI7(ZOBbv&1fQ0TKJGg?tF z313j^Z2IV;Dek8KV{!WIh(jRhQ_R&tr}}4l%t8v?eijt9lb4ZR?z-X7!GU zMt>2g6u3#c!`+BDEHZ+YsgQ*E{k1Z(%tHRnT-^u{8~*@$8#S82W_W=*4`6NXbSxwe zTV<~eQW-QhDUft0%7Q*fKN4C&a=5%TOrWzTm8-u5#%lP!`kPuvL{uW{Ipxv48!xE| z<->o^ig6QU((L$?C*(M&KdT_3TS>Mj zrL;V72^&!lo`8hz?uV@?@t{3{p*trhYrVjB5a z76qhG7U!5Ul!b18KSaH1e=$ZX>#FGqrci-NYnvssxQu^C9|c%n@?#0qJG&qP71lpq zA4!`)WLbj&up9|v=|C%Cq8pqW%vev%be6~mG=9|Z%xcz!MNxLMHyC$ob zu=aQ4I(?j605<|eGC*-5FoGBZ81}7Ef}Q>S{6D5;o5Pqr0fqvlan4%Gd`JlNJ53Yv zuCyup{RgQ1&2|a4E+Fa7?l$G&&BdWr;lk1Skx2Orh8 z3~D$CLwF`%j;W#xxN8<8j*F_*F7n$vEldyqU&$rjVcqqbN=tmD@<F`-BR0e~daWK}IK zq)aqwm-7-1Ki#yLmhst45l!rp$Nvsq;z|%x`d_zATv-0Q%tJ+L*o)okPB9u*> z^Cg+FLbvKV{S?{ zJrSmKKu@ig<%?B!?4STnxM48)9t!y^i?T9Fx zaZ)>03eHiUZ_)EF$t!{XIpzW(;<^?HO||a;oq<*Gzlso4s8+Yrp6oLcf4CGJ9ZM0e z>t);Q;k!bQXqOf4@nA&bERYuD8qqaNOI=NX?3-R4kh0&af?v5jbYX7DR zhq-x#A}RcBLqVR_a4L#?u*ZJM4J)qo2hTRB_~j(TTUSFGv^sg`R5D8Y8|K@ z83;R_vF$|B7Hl@d{3(i4LiqT|*mmY(Y?DkHvw^{8Dn1i4#0JZc-0w>>Qc{_HcDIn% zH?hNNr=kWqVWKGR6zz%o<}joEb}6Rwh6Ohf;vNuNW|ovi?4$3jq~XtmS`}{%!&t$w zvP75PYZl%gQzpaZm-$94U5tI4`=4{PBHRHPSbFX$0r9m@5mTd*C0;u^z#HLhJ5bLZ z)9*%hUoxg_nSWbwFuW2cyos5#Dj$GBoHU5XQ>LnQsp42k1typ|gT8@?657$`NgWXd z_*AOVETM6DF7W8UWD4lA@#w1o=oIxn+^kD78A|On37HoTuv6z4=E0R1QE0wh|NSCi z*MeO1BZRo0lLhJQZhF$s?%(o9H6l+MTJRFoML3<&pQ{Sv46`ZrTU(cs3*jO$Z2izs zF_Fe&E#X!RYluOzGSX8nZ>$^x;JA{;_2*c&AVJ_xH<%(<(uQe##aFZ87jF>mpyl4h zcyOU1AvQ>2h8awdYB5L{ExEV)UkvhJJaP~^0?lv`1Xtg{pfmsSvqDXrR9u*j!{q}+`yHKph zui`Ydn*YDcnZJJ5K)NmA1u_K-h%$!^+Slxg4ynQ`%`)r4M9$nxijev(cSs9YvS2pq zm{$}+60wdDOHrGE0?5z|MFfJ{O2L{>0>(3w+97FayzSVE@1$H?V|8E&J+ zfnBkLaxy8PJ@DON7g8(i23lUGnhO{C)~v_a9*La7bci#OgnPdP2VqbEVm9+vLO3Z4 zn_c&QcO6?Kc#|xuag{MFVkXElAar+}(FKkl!3R7~e8f}QjQVZ$zblzS5Yi#_8t~rS z=BXYtI7T&)GKC3-IyD5}<@}$+yg&SYxK{d(PJk-J2{%De8OP%$R$$6VL<2%#-x-+N!XG1IkS&$h~lUYd1VK{WXiIUkLSh?pOhch zx#+WSmjk;ugxGo z;zTHG?*)R6+h`JTksv7(!MOvykde>h05YA3XnF2v;dy*$$3=l zXpba7V+|(QQ&;n}g^`QScGrf3N`MoqdRb&MI?^phH007Y3H=L<|964wi4cklNK+Mt zqPIxIGQ%F0nf?mm(##$vv$!qA9~RO*`U^o2UQoM7#lc5<(hWL@VNRPeGv#NT?~;-- zHZR_=-nRhmRp*T3g~QlP>Ol^QlZ?PnTOIK-eUBWuH~D^U=?gL>9+z%uh>(IKe0)lv zro3IQIw-x*^!ypVV=5qcp@w(pYm!~d`~H6w4a}lou({Zn!Tg(zYCnw`#N@9>nH4o6 z@n(k*t&+)qIPf+pF91ETok`4Y3%OE>5CTn_3N3!`hls5P8Cs=U?G*(kY^>>9w`=q`Z2)q^Qe^x8KNOPXeSw->b=S z50w7h^Y-s+B`)za?hZ6|WO+pKVmuzf*9c-z8J~ox9q^DQc3a$Jc$v8nd|V`V0%!44 z%;Y&+@a_nVco4$$$ebnKDoc-YalrRrjj9~F7P)W2I$k+NM`vUp*6s5E*u`US@7C6EbCjCrX7|g1wQtxY z&qu&h8#;n#_Y-fTD#7B}S?G7P9z}AF?JcMeL2|p0oA%Qm+XbUWTlL~mOt`UPlDA&n zmud{(qE-Okfj?K_@Q1iXxpzh3s<9RAt!A-G;K?R4WOo}%8nt6iBB7_!R&uz^j=9wn z;o-G8Vj>ZJ`X$4WZ=+fBbtfjOK^h0 zUa44W|B&OvIE@oMC;65{KfdYea32VZOU^kie8WHLHk2s9=XB7SrG0!ccPFDyVz~$3NONlLY%qPV39TD z8uhPGjzsxPUO13;WItR4 zj7TPJ^6HBABT+$%TuU8xq1MhX5b{a&cLA4P#k>W2qZNG8^}00_I?~YEz|2SJHqKc&XYiG znUHt4E*U4+J zf-1^N;&xL@TVS)1=jt*gMmKAc&hq`&tOThekoV;VB@fR^Ia%O?6!Rjggi3`I3?xfz zTNPuY%+p*J3Q&F51humMapo=3A`cABnfU`eD(S?2z+Lrkaw z0dNkkca|;#dPs$z=KM%cw`FT0i-0HD%4-Vw3)|@?x0V^tnI*LYTKRG zB&MLLAS$v5-=65X$o^5xI@uC~+>6pm==9maQlbnS8aGRa0a*BQ=!=Dw7BV)?Hu3n& z1ULJCLG?*1kNY1Xr=ZS$ewL;$V4#u(#lyCgoZCX4QQDul`S(nrmS%J>nwBOmz zogB;(+*=RQSYEaa9tU$*35?1sUJNc4O`)Nzesbd+grN+xE{U{8*)cdR1|8=2O@k8rV~5%j^R zUUp|hSCh(nyYD}svC27k-f(q){|mbqM!CXa^ZwzDwDCty1QDCzRGJEE8{w12i$ z%NK|KMH{$g&fRUZ}CPh^7`GZ;bs*N@IKw-$Ud7**@?hHitR(r}4Get^< zzM>mzTyYwRjJwTY?E>3NlsaM5=WW};(dB={&FB879)+tPZP*ch!l zM_CyJ&4bLw&RvC(xX{a^m^4t`j3Rhp)#^r_j)}1D-agJ;OyBH>h$lymT_tZ`@OEXq z``?N0ubsyt&QEJ$(^K3{OLOvm3J|Ph)QLHiB^+hTnh4woWVRIut5U8#8Uc@GF+7G-TN9Jt`OPw=MnZcQ_;dgOu@;Wyqp2#O4)M&)>^ypQiLA z*g7U{wTMAPaG&4oE18SrhX^kM8NQ9$EbEJ$3G=2$*zXu5rdkSnqkcWEUX6b`drfu$0K z7V-F|gQffg1;sqv0JLiNAF~zV?{1D;G*T}gKPOmloCNu?+%eZaUL?Q-2R5R|;_{DVOqg!iS%e>3WRVWUKLD z{^T}SHk7l3f4b*dF?K{gUZx%*OwBJ4DbWpoQV_AP(VU~1bbhM)pFqWamEF!y06h&oLwZa1SX0PIL3$Xjj(QG-9G6>sKaIf4&&^z*#RR6!)+)mW z-)MB(KJ=gcKWAi+thfJ<8ZBxwQT>Dl7ds0`@D%naysO60UAX zJugbvTzQ<~`3duV?hSr4hc&|%hY-ntYa;yW!t9vqJ$bZJJFT_TsEM450(K^b#mB0q zLN>NBJY-yruVk{l>;(oza};!Ti7_8{tT%2d59jhPf;lq4E)b0a_lcy@NKMHQij#)l zedO9D%sJVjW(0GYLCNB}!!M(rFJ7NrhDkl=$$ch$roTAi;L-I-;dB|?{hZ88&jLfT z+EHOaPjPZa?zy2~>px5M>WXC%aOMPou+>ZOkqYVkdC7gSTJ~BuPz1Jw|8B=+U%jZ*Xs#t zDU<%~gViQa-lB-9?_7=#ul6tcVHnot6xplri?uJmh4L@W^znAB$@HWo)OJVzAoR&z z#h&_uRp+vJ$}_Jg&B{RL)Yw{JEe9o6@8c`@P3T0Pv{#k@iu>5%ZO0hTPYSL{maEIp z)?|{#kKcH^{(7~cW=2#RVJ!EoU+ZwF71?G49#b=0E~4a0PCK_!XH){NXP(y{zWhWQ zqc-ULrKzP4$v{f|FKVQCIwy^>>RfWx*Xt@OR#orYS36N9E7TLx)78v-Kkj667`plF z-$ei=IjMv+H%KEKdkgEBenfyAOL{3g@%-j0oiJq~_6Oi;H2R5gNEg+rolXC_!JrxK-(Gx>RgYq_q$$!UKg8^Ig9^Aa|i-^uv0Eo?>`XK&B1)L(XYMFR={ z!Yxkz_NroaA4^h!2LJv0Jf%$Yj_&nj8LHGluF-yVv)CfT{a|Dtjo#nBL7uLx=9_Ad ziB*v=+VqzgRyRj#srD#~hqMq>p?Sz$^uK+-G$*bK?6>MwqfB`AkwQ(=y6g3ShiUGF zIB=-GB6*C--4?x?KL$1JN}lrmC$7+5bVzWUjqE)6FIoYtY=K*H3*V+!4YMXnX6{y! z2XM{f*-w0U4WYJ>U$#77T!V(RNU7wwZ783!>Rc{ z$H0-&P8{#TY1rt}lm|#}I)*P{j07E@?GMXV+Cs96OPS&VNqvDCO^hn&}_@sv&Ksq^$$^U3`{AH?0wN?B>`l zezu@Nwa)KQNcA7=+Dm)bC6UmWg4HnE}7e%SZ~S8$Fx_;&i*`0nk8Nx zdb(_%6yj;-7CX-?oWm-nx|SsjE?rbKmrrCxBD-VR$K2wXw5c|_#8K{Xe~nkIXskOx z`i%BZ;bAkhbc2cC+p0qNhSq&%X|&_Q(!sLZ^s}yXwsCgk18Q`Gy-5Vd5P6aH7tZ8Y@NDP$<&188YT2O6M^Ti55AxcMF{;cPKHvmFTDBlX+pGPpp_VyDW5C%I-HJ zbWT0XuYA{|V#?8?w}6l_fRk-6U(%Kw^EHt8rcS>`-;3a^qQ3eUof7;FV?Id9e5o9k zBCKN}h<6ccL(%nw7pg$(OgDMGZJW2fA$L}^#LOW6ek6WEN%xn9w{xxiMuSF`9IyG( zz*-3!u`#p>p8ZTh4)g6cVBRAXwPgl#pWr)OG9Y_CRp%N=xkj7p; z*XfqMH0O!u3iSCror_iLdEm$~0Z%2h`ieGvLM7f{hI&OKy{Kd~lh)6Gpu%$0{B#2Q zeU9dj;k&@)Tpfx1uaD8lW%p4wwKdyD*Uv7aCOdw6nOVAq|D;wFfwf@TS?7PC`io9= z0v;hV{gy;^R?w5*-==`}8frz@b)2i8uG+*QBL*`?y-2&(Vu%ucd$Wigr%7`8?bc+) zJsLPssqnaaZ*r*KL>z#MlzMd3^f?qA3V;bWSoRT(*+=7K@geP7+?#$P_FsfV*~d_baXr4#kVhm@t!0K#wHgXYynWZ(Xv@ zThc=s?Gw(wM_4DX)Rv91J?L-tkbW-D{BXNCY_Z5tUj54>s~8@_f-dIaa*1RtQ-$8h zPW>+8L~}$ePm#t9L-qL!0#FOK)O!zWf1V%1;D7iGyZLNl0X-hJlV3?gNG)SjOghlg zjKG`1891i{a)Vy})@m@D=>69d%L@?Jj;CU-iTT~=qWp_)0@k+|rl_j04o7V|qYj!% zyC8vMwnqqHb)E?YusDAC`Op41%lLbz)mIju)mQ1_^%m1ww{ItQ)q=DE7xV7ZLqJ=l z;xdM+7@4h0OdBHHzWAcG{ooweeK9US&1s(ogVW_j843BZ(5o?RcbZciiREXO2h-bW zU&54H1&oMaOiC9oIMabr7)Y{Wnn67O?gdc7C;U6oZ9XHNKiAv&_>NTSGtfhGlHGZ* z0z^$G;AUG235yy1FW-hwFT5qOI!;wXiM_D=)ZfLnuKuU58~lp@GN?xneCHENp`u!CQG9t5>-nq)+v6}9EZ^$(=I4QW zU+aD7-CAlV7Ab}Hq^%aLI^wGHES>}-iV<)kUWRx4JeDZZ1qaKN&(24`Xq3574B%fG z1rPh6;?`%#^%K-%un@ju_|PDOm+cabu)Els;V0U=MBc-@u+@ZtL8AEK5a!^Sc_%4k zKxDuJ!P}eD(CMsrco~( zrO*C|p9mJ_{jz#AaeuO4;k_dyClIt@amR8x+-B2mRe4miKDYOK+B+IyB0YW!C%HHJ zXu@@voBG)*+_^n;Y`G!iV=ZNA=y2CU=c0 z`bJbcv*=8cwLs;M#yzn5lmk_7yU;+%A`YS>xNw?q}0Hx0Jwy zYkBm%+z0l{;=K4`Bz4~J)gd1r^R?eb<9hG6Z!YdxnF7Vb1AX|<*Yhr2y3wV-KNw{` zL|~#~W=0=2d4X95*MvV9Kb^}Iid+33Om*UoTqzzt*i=63Z{;6`mRdS?>1Ueg=&gM_ z{sLkuub#h12&s2ud+J%uD;>VcP4U#8|5C>ql{m`Ie+ektFf(f%vmzh|Oyw|ge3ySe z_QrhkBPP;Al4>F0jE`KV_%k(DG2`1x&pDI0((tP>@%~+KF%Xpf$xA^W`e#I=Qy(7$ zR5&GYBAYnb3>QagkC;)|Dh5Q>j1`o-NLaG(1RuMGzD|!U37|M4!3rV$Q(=0sWPw{j zii~`qT~!ItS*nPom3OQqwJihRLyo&Ni8Af)1j{^669qwR-r799hzK*8CV&k78mloc zt{ke5iSio5O~zBUj-_76^0^3zz00+o_gN698y~w9w?|LP2EPBc=)yFp4iRt(FvzXZ z@%cXAwCP&DxU@JnO%`Ih@V^m}3^-!%BSoGM11*}ja=}Ll8^3LdnukQThy`4`XxM?bwvCcUImyhAg{$jYNnzAOEb-op4f5Rrc z^h{sB`}Q`fwOGHico}~hfh}nK9<4zP<7Z9Z`ShzYsG}Kp?AiVN9za0uIxvPJeoSDL zDUNaEJEI;I)#kh4^J!~#NpsXP53ldgu2e~To#bhcbNsoK^!fia5aV%Y7-^ZH!Vo7_ z>0c|UE|j<2^Kac)U21>_-!){f{BB|uKvUlE%9zA7bp1+S3MH1G*o*b;=9n7 zUw%oGkM?c3O7e`V)8l2?<6C?XP{2=y)yXfr}jNrx@TN-e3(7P`V`G=wjcm$?Hhkxb)sGE zC|(#i3l!tTb^IVIQDF?YG=eeoaNCMr9Ojgj!TJL(9<}9Hc$fRNxptcSAn$qc6PfXw z`5T$J$>XZ&P`D~}itF-Gb;unx-|B7K%gy6)WrUsmzwU8#og>%p0`)fKjS62&T?NRN zG{raE{fP7(M$OOW`S33?J*z_L!Ct|bP*CwTT#|4@^P6cZcqb(M zMV_`hz&f)2nwlEU^v_Fh;b85_j*0I@ouC3Yt-;)_@fip!0qkGCVYgx(N#j%fbDxZd z&S>#po?4)pvry>9%X#yfErigLH}DoLNxaiyP{F^F?pZ&X4#LltjZONsr15dU?9`}+ zDp5W9=k68X;^$Huf9-kI3NTiLXdZL*bk#=SnC7VlX0nDPc+SZ(=d={G_OtBSLp zy5Tu5k@7>72Z&=^LJXrVi@bcX?EWB!hZ7)X3OJqzzC==lRdj~BL3-@GJEcw<)Ss{f zrx+m7CI(zp8dLvWHKHw@oHF6!Mm@Ajhv2-1P}MrcgJgRD4FQNZ5!%fs_aZNVbS93h zlD_VusL6z)L{gd;Nssg!|3Wso{k?o`J|Ag3xkfIz#;Wc`hNbv^clJn)=4$JhcV`=? zs?=@|^)4sP9&zB1HYTzI9{gBWkzda*J!-P^vZ`|*QzL!-%U`Z!XY>E#O19NnWK3s- zA@ym-SX`LYu2Z-+WMycl2A zOvOVZ#(O|)Z#6Qu0~+!n@OARxF5d^{2Zo6~ppct^NJMYWp7keVhLG8TjnrOlBTWOv zia_|@$Gf^v-~}&=U*Bz;$T~9;qD<=-ah4q0&u>1reFV>$1>%0&#Q&A-o!-5Jw8r=yfn>E5T*wlo31tMMIj^L@+hX) z7DMo)Itlg}WN`kay+065v@lp!p_1eeGH^S!)?@o@i;F;BC_$JZ9jP9swnEi+xPpTq;#jT^@$=lYb zcR!iSM&^O$Zq48?alkHdRnZe@$|AI1)B)w8pb$r-TCRYJL_PWbPfN#BWhK3%*N+XO z0>wEMo6=J?#yz8Qs&BmhyUT0wCH@v-`K;d6&Hf{^n!~g#-S5&j(-;W;CEo@@qH@2h zNTTb&$|$k^J(^n+gO9pVj>^(XvQeP~!%|X~mamLThJStkTowczZ5`yfNMBT z<6D*td~@SpXVwgHzK_t~jugsfM&L&lgD|*{?$#U%CC|vc%X&fny^Yx=sc0|zDN5`F zb5~)F24<)O6T@WFp&y-d?OZ_bwj-v?sY>Gl#imx6PK^YWUYp%t5GlM}w+{lcJOBH0YRqrGbwC{){Ds>rY z)=Wi&<^ZQab}gKH4osEvhbjF@7-ir&jjY8_suJ1Zk}qqwW-D?&SOPl3e9h8+Rvyte z&e>24vsOiX2{d9=1sD)>oI5Z>7$Fu;t}x`cV@B7#`#XCC;Pxt!PTcd>%Th)`y&_TP zgNnA1FG5RBvSp*`&+L^YSg|4VT>}K@5$D5W9^#26xAF76M~3HAL|Zexo0TWDb;=Hb z{YtzvDTaBp+%KVcqTr*I+zeV?^6@^*>NLGz^^bDSF^?70$UkR<7hDHsJ%R0rdAur? zjsow`;cZ-k7mqnZszjzX3o|M%zC@-|e@5eg@Ay>bf08MB$-L&s#fRy!T>la~V?{UD zWw~YefrAWp(>#fWXie&$N~Yg~5{gv*)*7ewC>E!Mw>IH65q-gkV%HW*9{j1np7v7= zFL);oj3)T~RQL}_@MYy8hJ*&wNoW(CaTfcXf=f;*(y16xPYRq2k73U9><)~EVg$VE znK7hbwLq8&(X1|39$&GzOY0DG*;SNA2`I0xe1neCA4O~!^ z)tRtu+#*MWmBI@K756SAtPB)GG-s+f^qY2(9QRk$(D(DSRfzBck&w$d7~Vcnkcd#N zYyP6+c^}49?Pu0A=sZ~)nQogI77$y>EO4<^M522?o3Bxq;cB2knC9Is^O8aDW58N{ zHmM)XG8{7G5jeP2-k(RklxLbKAvP2$FbLrY;orH#J&*_f6m(1ANGkAC%&fI4tNy3? zIc#h2;bhAMB)*3cJ*W^Vs51cF!C?>2X!WQ!#*odP>N%T(%BZsCPWzOcFt>kf{thqEyoi#{v7^Cg%_-}a=yV-eSAERtY?3tWHsOu;EwHbeT9*z3j+EFAhyABTI@^0^t&8&blf<~8F|gVA_4`czSMCziDl}hoL)g@gyCXUjGv$ebx{iiUKzC~5{pi& znjlZ3%TP@rBuZz2Oe=3!+Imcp2VLUOyP3_`S9w9P^iAfkzrG45wWcGBqf3DL2n}S8 z-;I=F`hsz6+hy+{z2ALHO*(xqzv`uTA>e?1UsaS%z+cNhM#1fMxYANGb<;O{Mp|5# z-{z72)_%ho^Fdm@_MN-5WHQm1jxhaTchaBBHHhfkPoM*HA3JuG;(`Yb-T z!f}^}Xv8<(S;>|e`pUAsdUN}<&P}q(sAcJ?THI_zxK4Hc+RJ?8j4zA-=H?Tar!N$% z;I|spx*D~NqEJM!{hX`}&@#)T^@%IB58yvOTQq@;X~3a}LyGF*HXJH+1JNOXB6lDc z_~`lq7bNKF|NLO;wIZqUA-~`f%mPXf5ULUE!C}_e#YGKL;M47?mGp|Lr!={!%%<9J z{;KtKmZG|mmb7LvQ4^#)zdQMMaX?GFSMJa$-}yGhvCMZGw-G#KKT^@_A> zKIi5+Fexd7pd9!5@pqkF^@YN81mO=XN0jOcxH+7X#2n{44AbV-V|v~Wg5rQQDBgoE zZSTg$GEEm(W6N`Cfv<7iEWh|jHhzPw347&z#}MfLNWzoH*-eW1jDE0}X_(yIt<82D zO@Cm!^5}0mNgP$|Ad2R`m2b%vr9$2QB({DIQDr%lOAB1dECRsxN;hNTY4#0L(NuyVcpa(-oLU!O;G$L^Q!y1n229enC ztztE|2AnS#!G1G`%>m+!l@HyLBxp_S6YS@qV1d8%G{}$Fpqd{uC$exFkQ< zI?oLyvGq+F$7OPbCg99qbvL}FvDpe~{77o33@LRi%eND6z_BNdcJ*0B18|PVHcKh1 zeo0XA+Yq2GzVt0c*DY`^eci<9g)iu8sL#z~P+5s`wmaZ{8(Vg>^mA zh9w?R%CNk9qqVN*g2gLd;RM&^VC%l)c$~hk^{~~IP1knMp^lwFrx+x4B*px^Y&t~I zUSszf*AnN`*($X>rdO-CqcgFJ%GJ+hMDJfSy>B|S9|N9ycdqCG=1fzgRi6GI7k`Kz z?cG%%n6+0;H_3;5j-Ix4>j3jJ7J1tt!I5@V8FkwUMWwRWu};0Dn6V#SQ+LmSqTOev z#qaBYdxpd&XamWBF0XCZYyD^+ocZ!mE?HS~KroedOQlY9Jzwysg7=WKi~(n#ft=%6 zWY48EM;gjWfQlPA)w#bv&NlF48j+4_^Ld@m7~ju+Dfdp$OQ1{Mygl&wxjn+wOjR;l zZL)3KHQ=oK(@y#eQ}&za$rJhXCIhq$h2fQDfT@{=rm2}M0~XuXuTKD}?Vb*BNj{IRhLJ#l z2fo|+a1GV#GfVmBx_nllXLERuHt+Gbl7NV3*7%6FEaGJW>v5vrmz>CUoNe}AlN%cI zRx+2qYMyN!>Yi;C--bMetQ)Q$kO*D;WyS;yu345lDvGO&X`}?+R zKL6+nKp@)MJAX^E^dKTwlMdwzEO3@^xjA>hTcO=^I-kbZ=HVn}zvXl@K@YAkouTJ! zK}0NToV_$rip;g0+;Om^hWTzmu)<0ip-IaMEm27{#j|mGsg`6KEi0SD+0NHzw44Ga znKy1i(A7n;3b#LtHNrGo*uJtV0{*5|KFhyvj=eJ3=HD}2OSaNrVQERfmRH=_ESmlx zGaPZI?Qlh2yxX7Vk_PRr3mo%+LFHBn__5V5obDZ9xq1 zaiNY$%3(;>)N*J&-!PGJA4x3S%=x8J*YqmyUF8cMo^nr*UlbknyZ~74UCE8QxjCPv zXyAJsTtFed?`R8{DbJtc?}PL@@=Qa%KuOiqvOD7GJW6r+ioxVuJo`jky}XU1^!4b; zE45m`&cjSg%QBIi>CSkJqI&?=wshf|rw#{tUg?AP4l)^u0uAkwCD4gCbn*SSUE$#5 zz3-vWx1xc?iOu1LH(`A2$KIy)!_re!wc> zng65j2$EReEo51Vo7FOF0Kh+xY+l+UB}S-_+9#AlQJeHX=}$@`C;_X*>_D;u zspJTBf37PsG*A3TL;F?CpZ;1~{mHp8lKjhgM4uv4C?7st1{aIo*eZU;C?S2P#Yafp z!>EIliBR>b18Dfn(FN_jbWDcUz~DYEnnLLg7d{kR(Y4QZx#*M%VXW<5t)BS9!$lQ? zJaVVZ69n>iTckky$4i@}vOl_zl#NG#_=$%rozz5Eq;5v$UK&T)x-v~o&Iw_wN`|xQ z)}}?LPnVQ+-#%Z{1|LF8e;@HK}%* zL>>ac_ESC`8>Wo*ZQ(*6z3RpIL>0-H^pz!>yUffT8Xrp&OgmPOw#40tnJIV%WJ5xg zNlasDbBNo?*0YC=&+;xtwjE2_o#u6SkB?uO*uRf|4}5);G2F+e$6rN0wFt}uwr-$& zPl{x`a^CLl=1aebsqBkI!9Kuz-Pd`SIo`kVgm+AG-LgxHPQ`2t)6FAC$crSBrKkHi zSIswkHL$7G+x%s7HBWH!tOfSVcAEY#?bsv~Qh zxvp(I<+N$C{rSXCI=qd0-t^QNIn#DoH)uQzs@Cb&(pYdzU;9nK13*VFmWVyKO>s2j z$L|t|v0Hg!?%mF%na3e3g#SoC-%ujc-m$OMW{f%7BJnotRlBtD7A>W;6Gh9OBADtYQ7`aeFW_3;cl9%uo(w)q~iZgcDWOLQFc zyXh|ZfUoGRUl;N{A$tCcAn-94V1Ezyb^f=3<>~xKucLRSeEQ>&Mdvcy*LZ(zM?t!) zpjp>Y*i0*ij2t>HJ@&n*SK;MdQZM$tIaP@n@j4KgmZD>;)G{8x@%sdYiwLCbEJKdD zKjK-C{rM36Zu|R3P?Af(B+2L$^6D>VoJi>cDIaVa72{g#!RLbtqZp&FmVkzYf&tDM zdCCq83mwRrJwb#O;g~;Hvwc%)ul;=EZw__t54M?UJK&RoPGOT#xJj_MY`DVcy6}n2 zXHD~WbhyOHw<$KqDLl^6bCwpLO=@UC25hQAI&s|3)AE|S2IojSuiufQQhr8FuZY-1 zu=C5gtsN#IFbgfQJJr+<9hhTd$57ZAzIeO1dKcz2NgiwpbeVAs+eKHW-JSHf+#arg z?weXx;1LTv@}K9ReeWW-+1l;8Gtc8_^bfF)D3sh>kTEuYx0ijh5Vm0##> zXdaNA=}qf0KRTyOQq`jURK8nI3RJF})MrF{wUd)-dfa1ptsk*I)5MZIrtpa?>=nDf zSHo;P2%+^zypIn^a!kB+psF05l6~xL)8u7`%~wqEUS&9VWQ;_3aSQI0q3pb2ym<wj{a1I*jY`PH6o63Ue>)NGW`o9wH(XI~=x%j1|RrR!n%;*`qvP2&dtzTM)8 zz);)3jKvr*bh+tu@frV$wo?Ranz9~~)`2F$i&4+sq*nT}>vi+yC5_q-%D*phYlfpQ zzkzhiLP0tNSn%5OaohD)Z#mip)Yf^Q9*OyhS$ECa65rzG4|vb>zM`yR{>x<+HphxP z*85eGH#~|%Bv0ka{eF^81wyU`(W=5S%nqLGf12i+<0h39hHb=pX&~DCYl7QlKr+oZ zmV|F>Zk(*FnBCx>xVBnUTDiYuGYSQ@B$O3ftGHnugdFM*Ox#<&?kC$<6nLfZ&+u^*nHrxHaf8%?e`P|iB z7n*mmyvA0e%)}W`k5*k>y|Ln=pzK{iRnYSX*7nsVP*z{HIEC?rs~JR?6X1GvEM8Jm z`(0y^??OkL3iB*0>Er5sXxndKyxXCc!O(z08=jyI(^cSNjw=7!I{&VRq>WLB5g2e9 z6|^O!P46p=wdGZ=rluxs zI)cBq{1Ca_BVQInci?<8JFkenov)Gq@GO`=wzg9U*cIxnM9k)V2YVjfIcv1l{8kff zNo48{-Ff&C4X534Dp+c*m(|x# z-C$c5{DaJIHp1eoablWo9WX-;>9aBk%mmC=^>x@TxoW7=~2bwPw3#u&p zPq$HBa~(u23mslx3<%s<=WloIwfc`ucBr(#HkB?jrk^*?<*h41HvY(;P z8K{#0NPA>IJ%+i==6SmV)n5qVx%f%cpK9IjxPgpXoauTDh7%LXnaeeg55I?TL4iK6 zG99&Exo%%6JL7lO3bY?mcV!k80sqkg_z~ybwyn|wy|*JMW*lDbw0|q4AK?j_{&1OJ zet!uH$J=w?{^r+vc7hrjb0OR(?%_!|B;^i$7%a-*8w97*FoqoU7Q^x`)9`Ca3= zH6Co_Dh}~!Wxmc|RPlH;?a4nqWkz-;v8aeg+FXKGaVBz)VTTbi2#c3?mK=?IcAnlC zdoL_aG6$C1jj&M<`5<;4+zYw+purC$r@&ZgrZko$7{3qxgieJgpaar&gd!Gx`qgktLT~GwJ*@@tB1`IPDW;mpHNfV zh@~opiuLm~P;uEd?OR#Aw>IP%Trq)7P0}e+pJR z3boGMylLxl-CnXnOauE-!)*zdjsdvo_py(fi1m$t37;y)Z9{ zE#w?1U!EVGnyaTLf`gu83RjpomPKEBN~g}ZsmGt2ZoM3i=S%Rdp@PB8hI`A|Cp z5QsiSd+qJoFO9|h`;ha!$=p$>S76b38`;+JXQ_rL^-FkUg&lDx`~`lF^TMso`^XXx zS4~@spP2s}mfH>f$znJMe(`p27$-az?;$zxPXm8fMBAG4?yZd{pAJ=r*gb)EJ|EV% zhmP3ywD@-9X~!TS*Br1T?v(AO_ccItb`ZSASXG64W|E$X<8a7t`>R2vvjsMm<>*0q z4QHz%bUNcg>_Kj+Hw@9JY{pYM8(0j;F!y20YY*ASCHICOmfF&fxK2cN9L5>5XnN4p zo$A3M*S=j*J^Emr$%x1busr3KaM6wMerjXRFyvpf4tTzExIJ!S&Ngi8vBS$>wd3Zbv&5 z%PaQsQbMfnhkNWmXivh-yKkm4e1&uhdU7V>`5neZG9|VZD?*TsM8N+YCCXus_4riX z)^G;jrJz)YDR+@(_W`ftrO~w|dtjR>Y7IM4@R-Z^dzRP>bb}CkI0%K+>z4|PQI^=@ z8#7{}&}l1iHj^Mnba`I!(oh{%U!$q5sENQp41k#tTtB@yu=+jah6dl)e+!r(ss6;r z`Rtl@{7jIPzGC4uiRK5tb^I&2&T=Ykj1p0$$tc|K=3M%(Lpy1dX$)m$d|)}XW4@vP zhyCOJ9eoS}g4A)wwzglD4-_T%c1UkPr@%L}xf11WAS?#%tv}9jqJLc<6JI6PI3j&^ zDHhY*SvmE|viQ4cEpw%B(d&+<Fp=lOXveV-j%3!fUTF&o zE|H{8v=+xmb9Ueg%5qfPK$&!JsDY1|5Q5}h>??j4QNT_`kK)O-M%+G>S zBxfb`$=G!<3xGQ#85M22;=cMe?s8NWnACiFRzi(dOu@sB>OSWvq66jOBO2|)yr>H2 z_RFckN49(*1s^wTeGaK!?2(=G@6x6FxMf@OI!7!;FsVo>Rznruj?x4sg-c>SIY@Gv zL{}4`&kdompfDxk;1Cn?Mvx+O(VTsrIlz{dqdt!;$2d2k#igdCxXYqI`a;*X+11eY1%Xjo%3%Dpd};9 zhH~QNA{a66mX_mS)3q*O7IqVLnoo-CF4m!ECziqcph=X6k6I*|JJpn=2_#=I;^ZlQxgmv!!joX0 z(BeR@nPPz{mxeMdPQh%ilWWnC3$n?wnz;k_i!KaaKM*1SAt(O3ku)BB6+`Qs ziIK=2>-X=aJ6^JEpjD{+om*1yWHo+Z;*#vNsLh;bMi9il6{+a%bH*@w@Ms54M;rn% z9$C)`G4npeC$r7pTI~@dr+Av~hI?W7EJ=$ny&Ud+dU z*TR9#f%^Fs7Np43PfkS+QLGw=ZV?IJp@w`L)m7~}W5P3}&vZ_<1P;kUk`u0$c_}<0 zs#L)}n;Tq=gZvZACrJz3-KbV0dZJyeND4xuWSzhU4iiP~`?WKT^xw-f^6KA(&Cyjw zX`8!6HwsX?rEfIf)yj*>#|3?g_MCZZ*~YQN{==h?{D)XEVd!VFIXh_TTA~?LA+ZD$ zS!r^xrX8)?Vnpz`w6DkdAb69ug4gJKM?i4a94AYMkp`rJn(2gF;a-jQ;9u!4pRS|Yh4MZ=iOmja2P zsmC@ZRW{|vp(y6FV1q-DhL$Ab>+Cq9XdCsI(A%kOpVlbPQ(T@H^CR@N*3V+Q>}wFF zJg{0EY}3op69~WaB}>Toj{A#cM4k>F%5n`?K&OP7muvIA6IR9>MNKDDihNqwnbTq> z%NsABOh8{Hf@7`o^-ZH6&ma6{ZD|72Fpu`J8>MeV=8H3#$$qG`ijm9=FfU#v;TakG z1vFco@8iJf&pYd-aW%`(?$LhjEJ3jCxHh;NtvkIc69`8W^37xD@)EGDyo7bwX3gkN z9+XuS%BIBPByqp(=;H)pFY(KxNHBj3P9UNG_YYLw4|Ydn12Vez`{1n=wH-B|)&RU{ zPl$}egbC96+)_es7r#mA2#KeJ0eXqHmYg^-KjvPmHc~OuD@4yj*A`DIp=TjwCWwC$ ztZiJW^1T5VVcjE8ZH%00gcNlS~wC1V?w8*1kypr>%t-se*IzHsq~_SSoa z8tE%1428N1^~*$>wg?&o#mn_NaEv4FuRugB%%JVeHaFV_z~udV*_I*ac;(n(}0Jx!wS!|-$GjB zLzX(R$B{m5K&S-gWWkltaB^HP4rwhM0usjrr>+GE;aY@sM+v@}bq^<)9c8EAeGxS? z$$<_o;xm>=r2ldtr*24~Cih{&stW!}6>OE(um+QYXQ_!{Px~D!X{I~mH#Rku{)NMv zD=-Pbud1m1weqyYIZi5l?x4vhSC5rfQ8(-H>=1vX=jUEgp}xJX;UeSXGaNr{KNHKw zpjG62@;ZBlVArY}{)UJwvtP!$+FXNQHS8S`e91dDGoAXjp{K30 zToZ_u(FzNNIxX>q@b6VeMRqTEnxs{w%%Eqlx+`` zWm?bEr_f))`B7M2h5FFIDeG)+5i?jPDAb!5CvW4r^|my;}sIi5wqyK~BuUEn!4kbL+nZ-9Xn86IIE(p8HPZ zg`w9>BWsj@Usq0WjKcL21ZC8*1umBTBwZ2CD4OB|VKnMGl`sZ>T*P2$aHYnCtIKv~ zK||f{wI=~8v&>QqRxI6refSuOX)w`4a=TGtnxjMcV_Yzg6W3rp8l1T>14|L6DfU{U zU*btv;V^3ST#rUzhHd|`0OIaO#dH~oAsJo>UkbKjyh@M zNRJjFb^2*3_QP8(TlW{+dJL}V)yjyrX_Kki-c6LiiTIb-=I{HZZ9TX)oH0KGb$NBx z%pTVRC3vg7(LW@m=7!5<%Lo!URz9k{fnCm6Mn&7Ja5iEhNsVH68+&IFu9`Bp;-!sE zP3pbwsK3^>7fLC~UOC4`m~($@w$vrAg+oy}P|S}B`Hh`;bIjd^QM4ioJe zpRB7=$D}!Tap1qL^^2$S;-~%EDlzCWi(=4LqYpIm7wjzAMGm#tcwEwK7}*HBo{R8u zeS-8})*pd5k2>eE=u+az-+9k@Zl00Mb6lUlCi?N`#lfUSA9p1RKseJ+)cx05kkizC z%SXo?ZFN!+dmwKD?{}5C`8?+J`+W6>vnY$}FE#P& z5@EEI^D>VC3QK$Rr)!YQq4AR|L6xlJ5qfuO9djkM^I5ih1DULTeVs^J&Mc$k=O~P# z99($pDlUNj zrfz-w=#>SQ7&_H#sLKbA@@Kj$beTBHLK%_UF+IkF&-Kxo$DbbsClDX)orc6$FH|y4DV={{bn$Z-$#q zwv`}3JqCLs`sG4JH-jADXHulc3FTXfRTw|9&DbqH?cF3zr=c;LRKIA?0#!gc_fUG| zGgWIRz;J)xWoAix*@6DH#uB72;GpMNlA8U#$8<3mvNT+sKGE?vBA)3VYU1&(M+8=W zJazq^2Smw*{=PkKm5JzP!|v0G#bMEI73n+gnObx+tZFDLk%-?<`X0b0gg?br(c2Y7-D>uP0Ce-4wCO_o)W+F zclHif^wg%nB_Ef5Y`$-pQsS`QdoKq!%{9 z;lnj?L7z+Sq`025Inpo_J%MJYm6Pu38FI_SxB3;IZ_5n%^tSb?A_j+saXSstZJ!tDg+1 z5G(1EKi3xOdd2)Kd#nc5wTYeGXA;hUFcaI7=asXj4VH=%n{2KRXk)b1DW5biwqt$2 zG>>YB3EiutR-DW#ahv`Du0^)v18(@uOhV31(la7Fmds`XkPtFTqDoU2=(6Rju z_5Y7>8x>kDTMTggGb|*VgBr01alc%`z&|VM$A)P$C8&(#{T2XYl2x1(N6Q)DB}))* zz&nu+uG+*~FKZ!+h~e>|7Nt}rWJhMzPvBSJ?W(LGXVZpFt*2fpr=^UKEiWKlsmH$) zZmL9B9M*^|C~0GZtV*A!uc&lBMgCCjHB=gHuxQm2>JVe{DMZR$_$rgvlo`giZ@fuU zAJ+*4$ap)xP#>!1e?oUW+_;*L18Ri>v>cu;AI;xBpMAZ(O*3!I4|5n5w;~KV!osQh zik2DU9nuuhDcBuTs13#%g*ahP_h-cN=cl@z7Zt@mP8D62i&EJ-n$@$g3~L+Fa@m+( z@W(_zecLAf;Fr(>p%y70>t>)c1Zdnn7e@zzHO-N&*0{E4F@gF_=?8k$n(}9yTl(wF{+# z-&E3PO66^Sa%_y1zmlOr(f5N7({sx@vR_;!$Z82C?^ z=_^BIXkA(mH6Sq@MinX)TCz58T&f#RPL*nZ4892qQInP?Lxveq*I!(A!=4POjjoo7 zjaRes4&WFQL9WGSGV@OVJ*-}K9@pYjAD3-5?_?I^MVZYllj?o;cINm<{x?sdC7Vkd zuceK9tC;6HLqYS)jpjdUZzkEapFi!jouICroAo0to5a( z>?Gf>(C%uV(YJb+;%9(v00e&)$z5AE*pE7%G7{#Iof!M0RIUtzgojp5vRKYR0`Js| zxCD_VS5ap_ZRZ-Eh3(T(bXBFVAIm(kuipr&ZVoE~U*Jr2Akea+7q_v=P6M@Kz_5*4mjamhNpN_gelp zISUPgiq%a=TE0J}*X~9ByPIQYw|m74sOCIBEG7)fe>bdrVY8u$pvqksll1kx0QJ+a z&-SUmKMl~C#$s;7^Ro#ixV|fvUwF+SNQffr)3bL$lX6Ef)hzz_^F&f^6v}h%>Zra+ z;jyYS?kOTinfJpKPH-mE@%1f?+L})r*`85d0I+yTlyooxC@$ZA-SH<5uvx;ks6f)@ zS+Z!>)ez|ryzONxYSu2XM>&;d0aV0l4k> zReE5y<#Wb3zrJp} z%j3IGRfV@*L`UGQs)gxoQ`til=k4f&hwA70cD6j9AxocM=lolzeVesk?fTo1r|;y^ zgTKWs@Y5>GfZHgG&yYjWt;eFZ_o;LBVUDz^=2gDw{jhBkiJ;H_x!&7GF<#)|19n*` zGuQX^&T941^7f7%n31OGGc~QbwRARLcX!faZdfPX;b~hAnHJWnhYtgT?%Mt%Cc?D{ z5!&Lk*~tsj7+;BW$Zy0|8pDqwE@A;MRED1#pn4_>g{c~Yq2 zb+AC!p(TA<2$x{@Uu9~;nJmU+S-cX90fgs+)ntAFh3gyzx_`l`XjuuQxW~~7SaB`A zUtM8t5k(UWnnz`qkC(wA zMQ1(5GV{R#`~u9$id(7qe=V>wl7pLGzPaIPW1Z~xNF__dt{>F_q^u{emw(*f7Yn^= z<@N2}psybmFevgUrX8xwI=;s)g$m|erjAUnIdC^6tpSOea?q~+y@{07;3*z*6tx=R(#gP1Ua z$PVdD&<9XqD|D#|1n+;Z7*CGoz#9o>Ms!J;=}=*^G;qZvnw}#{4{7m0S>7_yQzcDL z&R13aYm1;+dLfDw9Bv@7!#1m1N6CCAiM9%g%4jV?%bl)C!^!OaV$MLScr=-FSYS2s2k8!Vl(C zmPiWy;1Lgz1a6oMFhN8TlVaJfqElWfOlH0!re}iwP~G* zP0y`P-~D5~RPT{-r@U9JS3>Xb9=&Q3FAGyqG*YhB9zhgv_tD4D>y2~5D6eb z2bOZ+d%XDBAn3S*6=o34YLIbdPFdW})TxC|RWJiI%UNpG(cEs~dqz|!=7bm$!-ZKW z@vtZPsi)Hl!H$#R&2k-AW*-iDr!N~GkjwpG#SOwQZrTmD%U0ZAYyS})Kapq)gJK9{ zkYfDB>0ealK6E1}X+TiFul5dm>l=Yqz`WJ zotwTCS+8@yn{b2{`P_7BVMG#1JOjqwQix8XP#n5pyBso<%xSv>TEsDyI~~dF!lktr zK@>@`@Vt0ef;<=901IKpV6h#J=V>cc5Qy5T#zlgSJ_GFc|GqaUU#skwf~Rp1qt zF$A$7O;d$=e1JnHp%KDwk*p25BV#-X*o7JvF-|_M5lfNA6uqDcwT7pQ${VW@RJib! zB!l_CI`#j;SIHR)h=d3&VC*Cq$}*yHkx2sQKrkCHi1dzBT_5o1mx~GElf@SY;MNGft2?@?3s2ZEHrO9P$ri`PS zEYnH3jGJ9*Q)1&c!KGT>{HpPP(!8!q5KkpxVw4pM!AWQMF$P%)Y>qP ztVo8i!tB1`+_Q&BITPPpbS54&2D>+;h+=ITQSa7+kkg$cmC&oP64zEFK8Uz9yksNo68Kyg(+vJ%*RR9pZnx3?{&k!ke)7 zIExxrc2fFLM|XU1y(zJpXr_2?tND*)GykIn*iPgbEhivQsN)nau^^pUGUnMc*LhAu zf)6uY;-oJmiPMI-K$VIyLUkcJ-;+QETTo&y#k4rHIK;wP3(hJi1bYLtB5ArD2cuhl zvT(3Z&rzBYiu~hq`2X-{P5;)F7i3!#7Y|^j7m&hQaz&4IW68a=WFNO*eNjGb0;=%P;WExQ8y8~;{UxBQf8n# zOi1_;(<=PXNhmp|rwQ7wYw+7U;Tuda4(ehyl#sb3gzjgx{LPKPn!lsI|H z%RH!5xuUomE0bb?ZVp&WOOF2?Z+hCI zSU;Znkc}wpKhM^G&Jd_0e7jXt93NEfd=V>I^;5wUQvd88gArpAh~ssB%x#J&yp}@$ z(LzvrnW-2Wi%Qwyfc8m6Xy`r$FPNRnX&ck(A2fHNMvEAJa*{m~xf`7T6V+;Z*ydkq z!o{5&WZc}WBTOgzv!NkG(wtlRrx9I%Vuqvu`dhJA;dWeQd@F`kOKBaQDL?u zWz@>RL%?DnmU(_t6o?8cuMp1hV3gKnK3neTbZ%)xB^3y3Le3L$R}&||<5HFV->c|{ zfd`Rju*8mpYe#}5iV87ddO*_sS>^^|h%l`l!MK*=rJZszM^?)Ru;Oa@W{?;Z7tEeD zv%0&~VYQqhhAXXACa|Z_pJ6zFs)^df}l*`~}x@M~&CBRJtAep-z z3I|DS`l!_=W0#5yio4wfvHjeaIXo3F0po_D(l?fKa-s|-*CZvYFF{+Gl#pKWoF-p7 zl<6qhC5lHl1^l#`CQxZ7<-9!lXR`Sp?-DAOlL#ps1~*zfse;}YrCFA21odTfN=VI6 zNK6lXmO{gi1n322!ZasoP_LtG@^p$7(s$KsxA>3})=T2SnFqiav0?Vl5kgA~|4McY zqTszvwzJ%e{9W5AULp`1JUe#F0(1nnK^UU~wRmWs3Tp^}uy_nMu*+ZKbo;Yu0cn2F zvvUx+E(;l&j6art!B$EFaZ(BYhZ~xW4>i1hhLkc^0m@S^elQZ%he{+7DyulI zJkf2iom8)d%>Xx243)a)-7%46=p-wdDYrB(k6Zw{w5v+||Y2 z_e$wV(_O5EUw>`Komfv?5nDjtf7nGJ)n3YCrr{VkGOwIU?B7eb*1eUHh^EkzH+IU$ z@n|J{dSqGEb5pe@fp>^7!NbAH_va_tFW)&v;S8~Qsd(~W(ra92{989_yx;T(>#Qk* z9<83ZLvRQq0~I3!;bN{H?PR#HW9|Ehr9%*sg4mo~!l_txjbO9f3g1|-ip~v_&%7PZ`K?bdsIkoh()8Z__l-8{{8I_Oq3`wvJ z{TlTAPKH!mfIT;x@b+)N>uRqd_@WLVyjihUK1(L^_`al%_{bF0mj2>5QsLWTAI=Fh zW_s+R(eQAGF{)G)bb|Iboy5K3X$$PK&hDXh_AnDuh8bv!7tPk8BmPd90`_-qn0SBg zQ(8%*1V##anI}?aK9f^04!GH`fl18!v`eZ<3!XNY%Kx@$SzNz#KIvRE)A4_8QB4*B zjVqp35YG6J@+s@rOMoERUHVy_1mOzDPUtl1PUSw}67pSUc6CN012SQB(e8`qD`f#E zB{E_|hNe?Ds*uAP?JQ6aF8L#)0`WDz$`c|>_gghkg=sBF55dZH1W<&k)@ca8w@BhY ztBFlWMMR3XCIGHb$A&@lqNBqnjuxfNL6do0HrzpMe!U4JWZXBso5pRB1Q;G6~_A0E~H%Hiu&){3ZOxl(1(}|VCR26iM z25}ojujbi`_zxURlKRhVeZNMn16wg_Z2z{3D$mT$Z<28!E|Fi!{Ry3GuTbE$Khx-K zV*bibl-7*^j6_FALh1HEce_yz6LdK7*c%(7;4!b|BD`^aalFDv|Z~${Bm1~zIr(}xl-oO zfUFI?0z8Qa0U!6z{LBH5!O=cvC-0N5>*Co!=bM1qRp5>}cHhHu%dXjR=42ibqGYS# zdQXRz%fsQa%r$ava?q%*1)IQ^4eVNQ1*k1E`8fH>BU=!XoLb!*HlsZUB!Ob^IytF{ zmh0g)yt|#N*GR*f8fbHmQ?omrb(}f5dzX9gqiV)oyO;qy5f0GX2_LS@29rbykc{GfsUoo#kz%X9^5z*gsb|#(VZ{Vvbm(ZqV525vp~WO3T%iv(8IhU2~jVa)}^n{_@?Zo*pNu<8abEAcc0xH>gst z(`YOBtC+MtH5)sMM$LP$df&q3wgR5okae0hb?|NU|N2Z!886l^tJ!9gG}W(6P8h$2 zn-Q(3{2#*JDKM~QdEbt0+qP}n6Wg|pi6*v_iEWz`+s4GU_0QhtoZs1VzKic_U9Mi; zwW_Uqod#*xNP&f`=e!&9p=>PBi}a#B@ULn0k$?qbc}2<$N=--d#Qmi3p1M*3us zb+^~)mTN<3GrM4dC|}24d0+qt#j-GQ9h>kyT~qh zk6%Sixqz37*?ghFuwK`7?tbKO14qm}Cu;Z>$X)PXrD~z;k8E+-{itEGkIh&BlCQQV z`^$D9D`n)`Mv3->@xp5G2D`#s2`;+(X8-;F-w;Gy$ielq}}yO zdaAQp-zmsRwbox}cwJfN`zm*yUfEi$F-fm(-EOgkoj6^f-HLk@*xRg3y30ff6b}Ru z|ErmPu{(Qgth}&;M|?XZLE(RJo&l~-V|#@?Y4+-r5TQi{f)s5YS-yl|bJbF7U6;xN z<~8c?MWc0-mb9Seahp2#vnf8}AT>iHtSt8X&q9w)dEnoD^ zrftu^RF)V|3vRsd`&V6_Z~Hvj!7yh|DuwSO+g$Vczp1EsUiqs~;15Sl`V{Uq_r{&Z zt>!Yi#u$~c$_O>d_I^EG$I7?LDIbI{^RAk@jX&daKjw68H@`*7@xHsOmu`c{r@pc5 zSI^P8E~QW7MP|oo)kuiPAAI}Gy+k4#+MJgoX*@B7pE8vt_8+Up{mZHmVUfS?6hr!e zd4(m12+LA)a3OhWh&51vx)K0Y2uRvSp#-wmXrsf@&aNn(J)`9w`uU-V9_jL)qamm9 zKVzNLTfkZ-O$0Nw2pr$-M;FfPG)VXCr_M|Go}38EB2R%99AdYL{f!mAA()6Z8H@}2 zGp&-5?!wqofcqcYqS#UgaVJX!txp~l5!k;1jmB#8c89S_JjWdV3c}knk#Qb8E&SCp z0CA((WVVf4H`3nu0-4ZIApAPaI%5kHt$r{7Gcpb$lTLFZ_jh)a$m1Bfeorq2Rhm@* zK1l0b?a)r3Obl)Ey=uG)k&=!U0oo&J{I?mx9OM(~o-+58aN-p!`TD$$~@mb0qqeqWELUG7(o!G`{985t}eRGsuga zF?26k)vel9Cl)3n3*=wq1uJgH6{+Z#?)T%F9HQNvIpZH@r8M+$qoB<9F^biPG;zAM z9z&KXPhNZPxX$-iv&>BwUk$=!yD2nsvP`>a{=%*dA>`UGi>DrcedGQDeX8K~@*ani zP_==faUwubbOT~1IrEtEC|w>bevn6usY`w09ed#7p>zW-A^HO$Q^AnfH*ib=CdeQ% zAaQUx+H|7bQpN+j)?Y>4YyK>CiHpKKBPOq>iZ031upN)$cpZjB(Wnxp92tSqz#JPH zFvLOm`Tji20~9QgU!lsxDS3j~hP5=})7>IJma(`+Xy)>s^~V3nAzb8r0ks#rA9Crd zH~5*;oszGZQx!AJeRMi6Mu4#U67-dIb2G-a4-5mWy|*Si>FHj*>Zi(nrIZ3c{Fwu^v+k zj@`<8QEVC>V^pN2-A;ua?C%LTrBk0l+FqRQKC3nEo+--){*RJAYlSy;`H!pVXh%%_ z`99HPL1?5l+V{wD-7g92jpl_ibT&y*die3rr%g1+j>dcd89FZ$luCYTQ8+m6-yQ^H zUI)1ge|L`U$7i1Il|kzzuc1{Y-%45SHx+KBU{|SA)F;nuqGfY6eredZJEGn6)fpdX zKbt?9KF{yqI-ahZ%<9%D!+FoVZi=nqU0b}O&g#0Qs4T3#wz!(4Y2TsyQ9Id~qx;@2IQ?)PM65&1O9)D*1E;UP@ zV!pIPzclNUpDO$UCU)ENdtNZ{BYk4h<7MK(^QfilEcQYT%v6-mUPG zE)Gu{OAO9&Z#IkhsFRasz8fW}QC1^`Asbx_-9d`I!<|yrt807O2v>*A5od)2rxX za>;h(MAWx+=1c{4U%kP-@GkjYDn^C}n(G7VL8lrNwtY4N(L|J@NC3(pZfjhXK<+yN z5>!9o!zPZfFlIn=gnw!u;*E{$27XJ4OO57Pbduzez>U40R`%<88s^v6HZEQXbBM>z z=?k#haE(s!)*X`A?7n~G5_9ZFK0nwzI2?Z=Umf?1jMM&nsxNB0wz$0=a!t=+=Q1Mk zyDw<9`eKc%UuX}CV|b!#<73jk!GDad*BT^HYJEa0yV8Fl70On&yBH(*?on}P?2Wf5 z4whcaWYKC3!noiQld!*ppDD(ut5{t@3K=UGgpD>5V>&>waW+AO84U-oP!bcyv@u5Ie~I{ie}OsCC>x6otD z$>@=o7JMDwo)8&VpqsU$WF?%!Up#HSA}x05gs#*PNKaEMuuA(;n&fPKh>2cix|KSuW!|tt*ZhE@0wy2Zv`F;BTJ1)tUr9;JCxN=G)$Yxx8Q<=|aY{T(zce zci+hWf@3Z{`z{Fvtv$bP4B%b6!LBgQ$;^|Q-N>N(4qM@KJFmyDHurUyL;FMC{NlbT zZ{75puFz6^=4c$7jPZ}b>>25@vIid_`~_vv+{#6o?G`R|Jh~QSJZ6bODgLvh=%>&DbBxmEb$YSj^ z+2bLpzvw;*?{=NqkE#9RscEJ0(`L>S0DO@D18x`J_(5L0tVbA%TSls^CC$V^~ z9voDRK7j$$O$=V|NrL)%Jh+8>O@!BH_Qs7~v%%_!x{$Ge=gSKzOTb3MHQ#p$y_~!J zH4FC-^|D$X#N*TBl!i!{wf8e9tUr;Erh2ui9NYE`o%mW^d2j3rGGJgIrm%~%NM|#E z9d*?2hqdqze#L<79*alzAcJnkP`Bbi zt;mcbO%lfKZFiS1f`)F9^5;@7`XQSmnzKJ#uMZ+DenWm~QQO@;$j~|6!Aj80Y02k2 z8r=O5;p{k#h>v7qa^-!dyWd%39`V_l>bX3mh$8*CoG~v1y)!EVZJ~4h12#$i((vMB z4-Umf;l~B612S!U@4kpDohv%M>Y1O9y@>?YgNnYEC@u(0vA7ByER>?EqHH!qYlt{y7frmMhAvxCa2~dM8^ zK4!8|GTcT2+U91opIjg(!Aua7I$Luo*5+_ZlP0OB@6>|J_(eSEBnByl$a!s3ljP)W zY*-#8#7P<~t8W&9;EVB9PmarWzkSo%thocpeLo_+q)P?88VcfgqwG3fiMQq>@zw#e z5Qqq4)Cy0&PN(ZhRv&?`XALcH%;Vvc-)!cXmTxEMn6~Np(gB~ZMl!miqnBu$7*~5b z;%1>FV`KLFky-5BD2&W2CR$KL;3l`(KiKiw6yOc(#Feb5PtY1vy)d9>>U?sM&N7)1 zA?Uh~1t8Us>42G|Ly=ML`^tc&%n1nYwxG!&WI!y-WYW+mW&;v}cmQ6KV-JZSbG{rC z6x>S6WYWS0(;{Fd=6=4lztx)IPX$j_NU5p4*tkvsuC?2psCq)$+qV|@{j~Ka_|OYX zN98fX=TYhEW8O@TXYVvm+cWu%(0(XlEzD(;0@=V!WSao^k4I+r7`Z#IZEjKN z$T@a#>jO`4+~uMo#r5EuWu7I|J02wn3yM&V7&Y|6>ro~!&|Dff^Hgz7P!+qgiUK#gC;C2!g=~xdU+ABi$$H2!U5k3ZdU5x=1s5^*8oEA|libcZ#Kw}yr zm|YT#CB|VxFc+}E=GLDJJr+hohB4~1MQ4K~JEMoOEx$vVz(Gnxc6o^)ic|f66Fqon1P@AAA&!Hs zmu$lGMn~?CVNDPm8WkkN1iZQEb9YklWMA+-@U>mMpLsG4pKW zH`cid5S9oJvkIdMjkL*EuAnfv5JQ(IAupyye|g#BEqxpSnqI6-_=zJd0Xw+8L?oS; z6ojEpFHrRpfddSxhFBNY|0)0oGY(|_g2iQ^a7r;ghCr+G%`>+2Lm&I>_4aJ}{bprv5r$&r z2YevHhBxF)iP??|xJt?!B$k~bJ)Nd=OwP@Yom?zZy+a9%4@(q?A=E^lQ`Kd`ub-$L zho=I?VN11~v^ZI(8`9Un*gr&ZrUM3nE2^Ynk=ZZ#R7O<}D85Q`n1P|Bqe!HeOS89b zgmWHttG}s1v5Q7QLz6&uwNFFMCh(eoY6U>j_1r^C>+GTP&Q7_MYsVzRV!FuG3c4S~ z-a9>v#z^Lhe0qxCfZrmLuBmbTiuI1C-)u3tZzh)XC^qwsV*W`SDcb;oI`zoj!EsHG z_c7{Muzco&8SICeO+Rpp4MkP*G z5g+I2Hmg+o*eE6DP`9?OOvceaf4OvHX^mYz3oW(pZ`vAN7C9ntY<2zvPz|t)35HJpNaE$$2*+>*mL9 zVu!VFXX>V^8$XJg_Il&1nw1HxqhPMxcm7AEa z;&+%cU5;rT`tl?1@iwWV!78L(YeCp3r0+hCQ)df-8!mf(vW=%?FAGc9Ub!FtsRfu* zB|V(Tq_yfxsI_woH+JCeT~QlecWH`9e^3XmJ~}9l?nc* z&%g1N_~ht1d-cd~>l1!ngzM`WtS1@WX0Ne6`G{{>Jr>0Tmn?We}fI!enCcN^p|swTQ4hIeWiUM^OulQOw2v&wh9PsU07 z$Ir<;1w_Y2tU9-{>aPMtv{8h>OGHsq&j=duV+#NRbqvtZ@r;-^79^dHte$(NL|~sJ zHqwyA_*BMbuhi|U5H!Kn`qGI?Cl%rgoQHu5b>_l_^-fd-2v2{GVRnVEFXf}2wE}_u zq0BI<&ItVMZhy9?06?#kMWWg<4#^(bt}UD>%xFssgm5ThKbe_In3m=rmH>zmH#GYY z;FkEdp}STaj*B>xZt;iw&c=_ch{7*OA%mWLNnx1BssR7ABBo`RjCt>cyYJ=^af}%N z!Q(!-e%@+nT7yah`2`@%&@~KFwt@RNGWI!S9)&ijyh^FdL&N)hogFZ; zEGkr9`yFl(m=TzI;A9WMT;rQZpe(I`A2)~c44iFJi5Ev57S-+|i0kC2DJS!Y*tBHr zB0+(;p~y0M#s#ocPf1@0xsBUJvN*f+qnAI+1or-yh6<_L0l+%VvwUe=x6p?T;%j<; z-2-7HvB=VcO@!)Ld(KPV`a?aI1x2n~G~f}lqrwT*Xu)rCD-(FfKJ3fHA3vxp&fJB+ zZ~*Dj@XZVh1_wX$861V|(rIk69AK%DT$IzRM?XPYc=ja4C8 z;r`NJIXx-p*Z5srybq`Wgl>B5r;Xmc8>Q#-mj=#>W4~KBy^Cy=Qq$4aG?z-&EoXQG zFA*0AlXuR1b0deV(3%y898%l-x8X9+1(C%n5v?Uw4gG_;hX~U>OCP8DszlDzb6(~1 zQjO)IOCfnJ_>LJeYCDa>@Aqz1iDhL}EAy@jbGEM!9nVePT-4GD3MwS(VEXJqY%(lidEY(cvVcP7XK<7)&&( z*x-+3fVqJ}?r1LZwng{&B?ME!(P&GY9MNwWv66QCh@^c5I8=a_#h@M)*ic6-4&Vq# z472^_g7{_jHyhKoIfq)W0(A`|f|O7#N=X8TE_tU15@ z2Zwzj>H9E*LUq%ZhKc5t72ZKxPX?c7F@3eVlA1-(_BffGpiIWx>IyF#!BNL4v|cSi zL(X&?efxYZ^tjdXmZ>|GI0D-1I@RcelXiZK>Ql$H>ixLS=)H7nb@MsYwGTd9pQdNF zQ>Uz-5ew<~t@*rC*?Ob7fTQl8tI_9T5DCBVy`t(fD{=iv!=hT_du@{3CKJ|Ag(}M7 zuXv>vpG~(0T!SvfV}+l}gM2F0{;#yUu&>@*OnbL@O}gG1IW*Aq`S|DBo#Q}Vqx8d# zDLL3nUDXRT%2<(-u~)2f);Nc^eEjfcEqZ3z5w)qd_MO$7@z+AkgB3r++#aJIL zxCLMe?7kL3>6^c~F}Z8K5j`X>t@byK#BhjOZ1`VpO;idq(#MJKzmNGTxJVdRBxMMW zZ5>ML>vKT>FAxc&F5+x@iO`CT(+QCF#cSrL6bnkhs>NgdNPy%C#-ed%b}3STf%xY- zx^8Z4pO0?}SF>lbiF|x!WB4{A@6=fkT zA#hXi4&M_iFxikV;I^uI+ugqwa12{5?i%*L`$?M%iP^u(G2oMD$otx6?B|y*lg&WU zt$j_aQDe$9sLnP5Q_Rj+TNzX6n)49A%fvvCdzbrqD<+=h&1=;pLuV124$8cjU?%^= zHD^p9=FIEeWks=3!B%~RrZKD2@!eUx7vo#VSR^IXmomnG^a!>SwSIop;t1u&;_VK_ zq^MNnq=pz+bui^Xf4{W{W<+y9x>j4+urfIzoomdzyW=Q|1HV@B#1B}Rokf5*02Fg} z2@=>Z>f23ag3BjRl(XCAaHj>Fx;vSSQtTLVR2jdH-mck{o+aJ_jp<;DMM}3=&!&mW zW0)1NQ`(Wz9{C=HtRhsD04^}%{ZlGXf;~1H^%&3-tl2PSu(K!@jf5^HgMZbE0vNyw zL>U5L`XB@VB$QwjLMSHEc_fv^vudDEh-p`B{6S9pXzyh{^g1L#E|tz#>h52`mI^0D z$ggZW_h3u7%{loT?X3mAgrQ&oOv6Ys0MWN7llr|56Cy1}gCpr^JfHT9L})XjImmy9 zb{vGk)KSRiNa3pfF77EyYy6UzVCj8+uM-GapLdk#x|y|VlbIbD zIUJjJnxXeA6aHJ`19^;_6VOP7J`%7Va|ODfp4ZQuvUvPf++QMvwFT^N9Om!820joJ z#SKseF^6%v5F?G?ATV_Hmx=_kspcmUt`FJ~=HyR7$er9ih0$3h34et_Oqwv~+6)&I zr3O{#OMKo$C}><>_3i-lr08CR&Mfg5|FGaNWPls}co~oqm`Mh4+|&`lB-{NaF6sVy z8DZ(A0H_jlM356kmzT@X(POE`jcv^UlX?Lp2Xu~; z0o;PdZd}XuhNA%aWe9N@d&vJrJN_DocMI)LTLCMe->i=Wfkb#tjF^<`4pDS%jw$9U z+3E--(%_u>Q$H5#Ne%d}2>kIoGcK%cRb{q^D{F?{KhHX0Q&rVGE`6rJAtm){B-2yD z8Xp)u85^8<;&Bnl@NLC)C*zz5~VZB&JUsvje!-f*F!9JHURk z1mGcsGnfq0tP&%%nlL3|ye)Ykp(o=z6=6gnJ>p>~xDY>m^*xJh^ByQ#8FNs9qu(K8 z3^Lg9K}r36XLpqkK0cv(~S`fzne}wT8%KCcK~wF;W0sF z)PcYCL!(Kv+5Ybm)B&Y6C;3~y$dJXB5l1Y! zS`-i^bm9ieB8US4`^bYu1D6*gZS8;eZ6sD;f*Luczny1!j&?JOcm(j=q{nT=U%d|J z-{bgQa*?&88kCfU{R+!cVIbU-2X^@CK?}ZYptX1kK4DTueAm>F0`Kx8+M$+gO$6${ z*8epWj4J;Ys@fz&clEf!8`fBBMN?)o_GvUJC;u1(3Fj zSPQ7haj^e*?RHYRe@jGt|I-YOZS*E=+;^U&>u4?ZG5zDgXy@<_grhO*6den+^fuKVyRdRCI>$8(PhInj@t%bd3|EvO$dHppa~Zyuv(yY z3_-u(i_lzf2w23Z;vR1O$HV}jBCH#nc6{e$zzm3IpMk&;w3~pqIkpr<(klgBOWcX+ zVtSfM$xBctUJgGZZmJ`N29nbl_PIbjQW{m1+=$enYg$C}AoD?b;ZdM8Vwne~`4;k& zi<4g-0LTx)fmoB59Vdf_WH*V%Xp|F5DMiY7D7c4n;(@UIKZaP6>klCBo}&JvVvtlY z+bOXc(qD-6>G~(ZD765z^0bsO)1n!P5#gA6^`Ug&{=jz{HNRm7#FwiWBg5ZpwRmi$ z`Mx^@uG0)gcm|1c(;x$AH^y}fxWcG#20vX%)>K(64M>nY8bw=_g9;#dntRl+V!)8g z|8&QzPzbopq^72GCLt_Ck&Ly`;Q<2iDL@`ni0K|#5|KCu1gT}eyo~vDGlvT_OpHA+ z^M(}*%LJuYYhvnR4g`7P%>h)FrsBeRI@5m+i41`(!NhtVag1^ri}ENAc{~bTI^XGA z+4cw2mhVBaZGz_zmUh*Iqx{oIDHNmuHwAVRmW9ZPGRNPt>fuTIhYVItgtd~RtU#$sFvncvZ9^rV*pV2JaF$d#DT_G zC~pWvPO}1>0&dw6M%aH$2!jY9AQ1u6OzK>cP=W|O5Sl?OKse5F+1)&@03KlCISv5s zK8j=_^P_~97%QX|5jzp?8|jaJILn02pDK8`XU^v)(R`|3`O^4~PA!HJgIPdar{Lgr z=fO_F?>x^^A`?sfu|vr|2SZ<+1TNi7NRW{`tI{7ohyX6AgjP1kIn7I-YIEVwR1(tV zAWZViG;+U2bGg*l0(v>Ax#XH~7^KIQIt|boCwn8S>Yc8ivC2yzD#^%X24ie5L)R#& zGi0!Eu?QPIp#gFXTr;I@fv_jfC{A#4@&O{qa*A_hn5m=q44P`D_btV15x(#29Vah;HSiQ$xRvABV&dde67GjJ;rccF~}8CX!qtA zFv(&Z44j{uaL0+z@F7~iO=d&}?s`x9WMJbFrJ9Mg(L8^_KXfuu|Ie7_mHo|w+n9_3 z<`lLOMQtT*;Z!p{$=WTTfg6#-p0ypJgM49dX0h2|z<&E$u{d5J6ZGLBiV2dGW7LwI z&!FeD^owLWT~-_dmF)?cvD^bt(5^7jWP&9fwxel~al@#hTlcjK<(cOVtWB?k%>Mu~ zby0>rHz)ABi+Iq0WR3jx=ZZ-JG9iC>fFPZz7&0GHfSxGL$A2$c(OphxA24^^s}UvH zD=#}nsH`nwC5&f~FqMX98)93!&!2rQB5u0bCUUX_mwqCdaI~fXqX@j*&H^6Fb0gzY zm@Nv2I}ZCa4=s4U7peFz&gUxOVlKZ|P+3pPOOUGs-rrG@oF?(7lm7KEoO6J8NZtmOefpHI|ESD&sq3V99@*~k0{()~kh*1&SRtAjp}gkIuqnfw;cUoIw9 zb7|g%`SSSMfbrxAU8${BLd8ZR7XuU!@h*a(=wN3fON{)`A`=7J62rw9+f;d|;*po! z_s6)R7GkZsW{`Lc$ai$DI1MZBEn}4M?EA~uMHn_q!Np&iW0-Y_VHVa zJMH}>)5{L*oQbUwsnoX~Jf6mQp$MVs8&!na2x$XRK>Zb*Mj~anP5Mcc&RGi}ab4z8 zk#6rBjVMkoyh6`R?6!&vB_Yj-l6rZE2D~##bZ<7ma0F#s z!=);qeKI07GT))rw9G^G^^XJTD!eb#%RVzwr9<-SNeiT?CBLtVngt2906h?fd zg7`_2k!zWG(NspWIUaTr#qFbekU6sCYmbsZBMW%;AX>(szP%0zq`KiK8%;>Jm2H1m zTjD!A$#3l@L^o0n^m=F>(!MrrbAHQ%P0`3FAokPIkS2wFUXn&*BSTVyNq|P=0EpbU!TgV*PM)o z;L5!pN{7xAJ}q1~S3c$EzwESafK$OlGn0(np5gqzRXq1+g?*9 z*wnk3?zpde8u9A`csy}`z;*Jq>2!7|QsLf=0L_+AA{43Ouw`@y7+e#*hy7#3Vld__i7Uy46Nd@rs>}r|19Qu$}o=ZOmlT zAp7-TW5)?7ewG7>$q65#_nUii;gaP40AY4OA6=$vmYw)z9X@NGn+yY%{MHjIAVp_P zG&LiR7x5&=3NT_CDWoWj@^niq7?9be3fxAUfsFQ?FxK*owpUSDjN7@&SFu;{h)MG4 z0CqJ`1EI*YTVp5Lf}#u4tGC_yJ=*PC(l5DxdVy*V&>q8Yl;RN7qoStE=rpsbZwJl? z>-(Yu%qzy+t53WB9pFqEPFODmpFjAP)$K^f-|e?DlKj85^=24%L$%?z@XtVag@ z^y8Yr-8AoI^W`lJDxiBI;r&4{wwnvDi?N0*05QuR!MQgW?!C!QDDQJR zOMn3v0A4r-+|Wpfe6xI5p=>!0o=d!o36Tyo`$%4)%ryxu4*nkpY66s4pgr0* z3V8m*l=kmG!zEDNOn@Pz?fML2a4-%l;~XJSWLXsTJf6H)+w!t9g<}5vRt9arE3_4v za0QLHmJDeokYsCv6u_BgA$B`hH}75;FhkRGfp(7(cxl#E*~U5$S@!UJVRllAxwh&Cqc*j|w#s%XGcYvwk0HMMS}_?O6Yeq`h$biLcmTsx z{*vZgn4q*5Bc%o(u_uY4%s1r2xYQexj*xv$a%Ipf9yQ%V0lhka8O{t7f$`*4zdD?qB*_%vA|HQ} z>aYQ#RbmRySvpIJk`H62P1XEX3z*^a^$?LU@(gxJJ|}VzC6>Y8B~sN3L1KbPK`wEB zUWUt1hhtNvK|Kc;uTWE9e2r<);s~id#!fX9v!n)!dk8gaaFcT>gN#dgR%+-?pNTS9 z5c&wJW5$b0z>cmx1S6Sl()fON{gBgSh&nf?L@yjB*0~~5AjL=&8Qu=#>XhkN*Is;@0LY^{$X40@Pbb-uO zm-RSOYEA<;bEaKm;g3I$+Jeo9MaS#CdSc$6KL@u)y)78Z3Kwx1&r50V&W+Bo9a0h%bt zhRArRr;N!q!+&Z4-bzq`e^lE4jsd2ss1}u=G~&uf?2Vm@pLZfOkUj(v6(e5pD36>b z%tV0_HdP=&Oo+~k%>c*>RA^WLuy-&4Q*pugpZiWc(DyzZOcd^b(nN*cjBHUA$O{N3 zn4_esEMuFx6nGX=k`u6u0n7t{;;uo^K_@id|o_jG6Li0h0F zS;B>Rl1a&G6>~oU6267+Qwe~(XBXV4$62n_<|WJm^gqazr4Rk#o^u5>01v6Q)M8_B zNRy@5Sf+m`r)`;!B@#!hN|t+vHeJHG9Kn<*-4K3+KgV}V@t;YPQ1t9JxL#EDwG@Wi z{W@rjtr!P$9`67bdB%|}x>@H6sKW`^)92yrJ>caZ=s;ilbN{dKKl*|2OBzynH0+>9 z0*^S}At}&%1>gashmepT6pFKbJTaH~Jjq}2d|LIf9;Vg^+kx8K-5D7yPgMkldD&r7 z+oYm@g6)jKZPW_aARS>Ae6rW6O^cG#z{@pc#c_QgflC>O8{*3|KAMxCflr5Klcv4RdHqj( zkqe4p8C-SOB?9K`STVvv@3FE5)17(>+Y0@@!iqG6P~Ld%K4E>7N3d`@YDw*-zW1Ka zrirkdFloCs4|eQ5Yb`TMY;<~v2bmh2L;a;E{`6P{9(M@gP8GX>ERYG7%$h9q20*lm z;|fqF03PN6nqWoj;90^a(h|QMxv&Ru&G%E4d)X=P?VahZ11Eat*W$UFWW8QtZO3ws z@E!VJ-nJ)P*`K+`m~rsa{rP9;>#+JhABZ=L5M}@)Fu|7qfGEmnY3Az>7S${Cb0=(1 z6Ta8#K#9c}|D>nlHRm2DoP4EC$Ixe)R8%9{n9Qu{Sl|PAyU$7SawdET**i3xMFT>8 zOTo>}I*zGcpM=Gw-Y>O18u1?+7F175q(i2d=lqPDE44Z&A0F=b zr;?*4tZ;>Q(W0!xxhKUm*~_eW($R}tS<-o1oq{%;?^!ZfN=L|KGpw-PQl`@K<$u@f zv<_4aUiP0YaOW)j6lYEr!<~P9bFI!kKfGTcf9SAfNk(tK2Jt3&YCoPMjS*eq@1&kKIPCDOYsQ43 z4Avz@P_dtSUl#yxEG=n0{N8ZIB=%AOTb;1?+s!fPbUHasjx`jvOi3u@YB-lkf0*aA zp&(j=TzvUPmaT0V=~+-)ywsK9AUPbElx0?INtDPi?cF4G?_=88`H)>A5hU&2`hdL=Mc6|47^)7AhT>910g7XE>61V@TGk2e2 zXhq^w%w>pmQIXAeGaYxs;xdC{0j=`5sfjJ~@?6?xv=G1cz4TL@?^7_t_nb#-&f-DD zZR-|m;TCU9V)NA7*G%S3n7{hEoXeC1pMK^A{Wp&np*LPkhZp~&U`8mi%|{trdv*Rs zmEZfy&~BCOTDA>7^*ip)Ry;I4&(vT{nsIqEzeclc>2DKnxGsG^e5v1}2kxi@)Zw)f zUJ|5X6$FB-EKb2B)O%z0;eFQ98B*LAWZlmpR-i~DKF-5jG;NzS@Ae)Se4 zBaq~4U9W1Z4Q!E^Z9bOG{WozN*fzGa^`$~n@j+N=#9qy->Cxw9&#pse*1I7tO*VbXRS_iX8a=uRh#KA+?J2kU64b2pL3S|X45PdRp*0; zo9>ysO&*++Y4cU3kcX3Pjw}Uni%}k7xU`VCdi?#UUGwa9e^srg? zxprtb=i5i-_inE@+y2(<&yNLuaxA|bSne|l3MZ8iLG$=!c3g1XHq<#Wa4!3M$~zv@G-l|nHY?Gssu_3Ub_1?~?}l#e z8#T^xl}Fy|lj;6>^;2~p=hQZR-{#IA9+<5^eh0J7tlDheQCpK0Z4Cnaq>8_uqTD7kxpjTyZ^)3!mbO0|_KRpWpwc?0HoJ*~p>ht4%`Tm~D z=;|!p-q`H6n(rFV>y)``J?~Lz^z%J6VQ|%*Tfezl!zk-DyV5=m)P2&`Rom)33&UxN zgZn*^ak1MkD9iU4Ma$Bzz7ZF_oXuv%IHK5fHSid}>HL@<&)oLD0k1Ure!(J=H^M2uzJ82M zn?-SGc9R$HQ|F}4Lz+#~F5juS_qsO5GyS+;bl!pTB@5WR1Ij0fIMQr2XUjcb#{=#o z^HRsI`Nr;fpm`Ov_**1b@blH0^LXi|`q9se!=)at{;4mKQu?<~AvTxUZ){eM&zTtK z9vkMFuv4e1VTDs}Q99iF6Rzi`A8HswZ!{ACDHH&z=egN1$q4RT| z$D1Jp7OrjY> z`Io~%8R#nm$F~)8rptYgGJVHe!QG)*i&5R3vQZyZkyk#+V8|J{P>HEy)WZtK+_8!3 zGMtIE+BL&K?D}w}^91Jsk4lWs6V=5k-#kRK=i*_MWl7uPpfj|tPOy<^b#CaayR@)3 zEDW6EV2#6UHpz3T3gK8(=MO1un{wf2ZG6EH!TXii76|l}>FoMsBKFN zpT3lfb1juS{%Y!P@_mO}>$^H+EZ}eXkULEk?#Fb!4n|+GdhXr`k2pVV?%IIs0@%f| zkT`76u{-T+7RA{W;#OF3`B3u>U6=D^%?pF)x8`Rb3Mr)KLZmOr zq08u8fm?5u0gGTiuixCXJ-z6h943t7%f~@2J#5`%%zRyhU#5B!Y#13kW2Uxigx49$ zy8c-C4U8urdVXvD!C%#|!8U@nb)Vuoqb|aZ6*;FV-^|~3f#r3toMH2vx6c;C5WIJo zb11*;xo*Oy2F}oL`&NwYFqlfK)dU4S*;srX$HL`?je|d=CSU)yLbvYRV++c%S@!{` z#cfvC`NA$oi>vzi5%pk(E(dOyzTxutX|+AND|J6Odv$9yZPY(S-@MD|a7xSeNI!Zg z!uN3KrOS?G)_>b}52RINl_bXE;oI(Vscx6?>UoVHw{|8B%lEj5zKzpy!^Piiy{M@L zuA}N}n$(5N#qQgTk~BX1%?3M?^i<r0&8ptvVUrtrttl{PO=WF1s$1kqw+*Me zIe+e@f5`R2b+R6MD5hDuczkY${wrg-62xIH$mFBgg*5+v? z`sS9}MS1scTg~zH%Sf+!f1`T6ifYzWd%;Pe^={`~Ke`-+*fJ};7BNi)N}3xX6$6n% z1ag@$1XX1$QxE>?YF3AnfC1Z7*UK024)lxACO*h|>cO#>)^{Df&(HVK<)SA)vcgoD zl}NpaXQ8 z=ZiccRM7qlBdBdKqQ_OB57!e*%b{zd%M2g$^)@_R+C%a$dQE}$PS@W;E^k9D)~nl- ztGUbqqj1-tUO#5%*{~OR9zyWSsmo&s7YrgR$Fs-fxqQSb(O2%r4wlRDS-l=+aKmWa z@@z|^It$jSy1g?Ou+63x{p!oF_)kWh{vTOq!BtnVY-=2XySux)6D$et?jGFT2?Td{ zclY3K!3k{GxZB2EANRa>#<+a*$;k!#z+c3TUMg@F^j1_iU zy+rdq<1sMPm`XFV$aU=Yk#9dU*AduZ)ET&-nJX3P_{02q&G3qn`z^F~Sf739fu!D= zEjuc3h0ETNm*AH7{pITEm1xa6x561wBgYl6=<503OTMb!q-aVNj;@++&_(zCdnAlnWToIy)ZIdwz= z{LTI&_*pFe>hC`7S2vKiB4IbuR-^Go?mf0&OwQWwQPjGQWDHt2wn=8EIzTxz>ss%t zujjtEv9*Eb_Fr;Aot5S*l`FNa8-g!+z8?D!C)#a85!U`j{?tDQ6{+7sU8}Qt$7-`?=%$b?0>7uY+`d+^5=p&^$=bg0bnEM7#8Sw0wTuvXtuq`7wIr z_+9wLHrToR*|zW%xzDrZtgyw&_eoE`X>jUYp~s$M1GsqM-QB&o_l|p@(%=O2zaV&9 zmiHO|)$KlLu6yk!P`d3>bFartQpgKiYwOT`!^1l*%-(-4-ZkxZQ7^!?pnahHaWj|! z-Yvd9Ux>(8_{*=lm=Qc#x28XI)LRYXwMe}OTDUBq7D)MPtY0X;o^v$Es4+ngYG_k| zLCLf6jgc6{*)lL2G5;MrO~JS)DKz9#dEEGqzdb7taC)R zXE|`f?%FfawE@&~Xg}!ai12QG&ucsI?ud}z6=)SvwOPLlHokW6Hn4fRo7%SCSW)tM z)zKT$=zd%?-udVbBieY7zZ`#(>#M=-fh9+0J`lwU7lhkZ;Is@zW+m*M6(qemt=q-DJptg`#C z#x3ME z*q>&(aNQf>#L87VZ!vaSR{(~q;NW<>bUX064tsjYXdygha}Br#OiLZe3{2-g{T%nk zHF@(fXVlgG-TxRZLk=%UCOX2_V`89mzG~~?gT!faFdz&ZbB>UE_KG~KQL8aXw&Q?* z<=?Ctu{o`K!qTJ2!ay3H`*V2BP^l`Qad2IE!)JZSr*r~{-Ri8l;X4(ycF=csuf{D| zTENniWScwazT1#?MP9_FPfigI8yku$Ph_hJAF47VdSbTrIOBfs-J~%%*EO#FfK-TC z`<)|;Oz&fQKONS;xqs2CUak_Wu;ZyeU$cQc@V6`SH6`Go^JWD zZZ2J%55Nhd*jTe*Po=B<&gf~=0`-tc6H8r0dZxpNzs1Dzg=U7aj8;GgqkE+6VJna(QbiN|2bA?AV@%?DH`eU&kLMR|*Rl-@96(J6jL$k&3UKtEYm6y!W6V)-fdMWf~S z!mbv}KA*77gN`$i@6ujg)wh_kddAw#?6>zfnk@UpVJR9b3*(JM)WfGujudjoC5zIr zfH#_)I#><;Fl!V6TTqRi_Y&39yYccX0_#-6HAcF?cOhw3npya@nA?X&L}_n__B-4| zA&FL)5kGS2sZkS@sUIpH*NXPJvBkq|gw=fVO+${ccwuyF$Mc7S$d|ZXe}vs*K@Z37 zY){iI-P1TdC3gorbG-TiCL&u0x8XvtxvT9{>+Fudvg_*x7InRgvt9SCZY}si$9U#6 z@sF0v>@=lE)E)YpYvw`qKBX+k?;MadmESQJsk<+a;m7scG|Vm2IUVD;3IV z?9&b7B00_4UA>yy)=+9$cQbU0<%GA+Vc`#3G$9+JV2gc284`L6O0-Ayq(0F*IZN?S zYjd3i43J<@xN<)U_LKw%XE_2kTK$4^FB`2W`MA(ADey9t1XhER7#!BD`PRZRHQ(}X!n$;c+Z!NQ!q2Y^ z+NZHuF48#HYi=v2D5>o^pRbtgSqv=nQXa6f1(@SHeUtTWfT=;#8BwH1d2p6cU$`Mj zp%cKq1(_gh#iM@J9IPz=6z%1MvU2t350s+3hK1!WS8ULig-`T-{yx8A#(TZ-D4p>v z2Kb<3kn$CC>0VpDcMJFqhKoaoX0ViIYlD4f6NSj9!A9pLqVI~To=}qOr_H>cvfVCg zy@_e;vN!m3;+R;|tfx2s4MhQ$i;mRaLaSCz@cw~leNQBwTQ(}FI6639I}p$D|uzCSjX;| z$|l{n9{24EXD7Re^Np5U3vvXWo%(b^_f)-o+S|m#J*trigj;S(TA!S%3=gMvOq8!5 zZ1hFd4lIh(oV?siRk_0wi9pdJNTt;zq`^3!YFGn2@e%H7(YI@N(PX+wW^TaQz{w;Q z{G%hScS13+-tCMkwG%P+1b}>G@P2D>g5UlAdS0tz@t^I8#jg)yy)#&%3qtmX;O-ck&(QUfz8dO6GOgs{! zfR2;gP_o19M8s<2fese{?n0!}#z%Omsj+LlxSXtz#&cDGgR|+qIk5A3j6O35zYU^I4eV*HH zU9Yh+=T*n{_KM1lV_m4zbg%x%>toMk8v(%U# z?R2DNCaY*@-4I(n;4``~!p6z-xS|+!Uukz$TMTHzsZqs27=C*l+JKyR2^3sv0x8rk zj{DdaQywv!zS3KdoIWXIVgb9)77<^}XRlpji5)`%8GTRY7YLPIuMSP{jY7XfNd$z$ zQN#Y(h10njS?7WQOysTX)pbz-HFor7>|0>{)%Cc}UKniOqWJDY8?SW)DozaGyA^ejnWx`_ddwABJn}aMk7>*BYM^znxuSYyd}zbwFP;ul8j?$A5rfxc7hqbA2-o z`!ov_k-NCb4!2)=ytg|P9|a@}#m0w+{Fp5g z<6M4rKQStfZ+`hX+v3*WJ`9Nr{-aL-EM%1AO=fp5gHqpQf&J1nf>`~F)P+ok=LGtb z(x5Wl?v}WXw0E;nFDsc9ylsWA6@RvSXe9F*({<-imdSYhw>fGO$dI7jtghK%&=u?0 zk9EEi6=HEy)r5gES+68i0X(`71!>8EZT?{jfbs%vAb4o?K1{WEHw8Sk@*XE%t!KfB zZ4+wnE}YE%n0zQ~mBFOd`WhlB80zk~aAx3}*evH%y;4FJ#P*>{$b(mVjX0Q|tLw~f zvEG1UrUAo9CpH7G-ZDErZzD4wrT<)8TibaXW^yNu*2@Q)<<&icd2e@bo9nCgVn=SS z=HbXz$>x9?T{n0Y@rQJEL zAkYBl0e^v}1Vl_cX1cU2!h|>>cx1BzHDLN(jvJWV9(uWfE*sQ3KYfLdjNgYdiw`~o z8QYdAn!|RlO5s8!e?Mt=pxjJ;HR!ueyJKT@)`?O@%ZEDglQ(rz+k|0MjC*%aTRO5SnK4hVV_ILl=M`7>HUTP4_ zDWGs^S%OW82(UBK2qx-08gcML!Vr9|(e|f$7}{Q~yusV)G%S@WDZ}R#Z|vfBsQBV13i2 zx>Z2`9F%S0>TlomoYR$QJ0`~iJf0j7DXjn>mZ!aNQUfc4dP2WSCXc7Z_*~3?g@^Gd z7v5pY-k+mU&MQs+5vik8s!$}SJkmd5)tlB@qA&pVkjg8v(5CCY0`Oit65k`m#=_&95=qUdKGM#in6MMZmW21_kK$ z60hQqQMxjBC!g8qQLhIbwXIW^bGV=BehqZbZOkTG(iV7ecZ+VEzf`=C5=ODpH^sR0 zJCV;cLfGK=1o~Bl@z$ZhH7$)OuOr>^r(epMEnEWk9bw1*`B_2n>)!K)sG*N=qD7KezE4;A~+uOXJs))r_}2NZN+Q_oXNsi0?dY8UEEL4_;A0R~H@-m?6Eei) z8Kg0<tw<(lGpZKst5H2WO$DN%I(M=_lV?8zSbzbLSkn@4oY(o5iSgos>*z~7 z{RvWa`_0i=i7a)8caFT4lAZ{#zoEn3AY{95VT9o#2($SUp}p^{sUN{)6Kb9m^(}e7>k^ z>M#%9aXU-^O1VV=tnej*{`5Fj5IA=2Yj^&E)#=1}6?H7_y458#gv=FredBJ{dwx{A z+aj=UX6`N{EYxbb0NpthPFq3=?cFqOG`i2P1(uFa&#L@bzEfLTZk7SsPDLOKG@W%t zEEZRUX4Y^;+E~%Z>6_JBd*iHU`;=sV=qb4RAecEF^IR>@O5a2Nv#5~{U({@T(7}>d zAQPY&o_G@*|?P){|bu5%o>l|%3H@v*pUoR5;mh4=IGxxRTKjl%Fb$e(Y2 zW0`CqW=(OEs#X+v&oM&lMIvq>$exslAUzUXx~85os^KTIvhzkk zd}{vOT8WjDGJ}0NUYrOt+A_F(uQ_ZTlTe&4QKb+;K7@kg(g~{7w9{=~%VN~C-DrW2 zD(d+-vm_0kx@0;D;gNdB)kre1{ax8;?KhgYr3uaVN9kXF`2OtgJ{qNO6yBijXMe+< zyWIEQn$A4@&m*~BEzu+*>%;#eKNv5YHb&OcO9~J)H~OuDKTe+OY<%sp!!dA7&*q*x zxf#|sU1G^Jsr6epU6ytyed;sO`fkE<)N;a7iyVd|6zbUH*MNMeb|70Y|7U~H@>rBpT z=GHU*WKLh4F~Z~4&vrz&$M0{bVCYdwWm&qTkyB@w2+|L*TNnfpL8xmaK9%%S#Y zxoIW$cTVKIbqUk+fz|+VgukHI;?d0TC6rOWbvM1rTeFA$2v~LbW?Q*4Nc_RI$K{vw zF2n2MN$s4uNB5NbPx*v!x3lH9wTp^I>b!D)zsIcFa;@Xdg&R+cPnxTTSqHEaJ16{4 zQG$X%(30Bj^^YH=QI{K!i3|uz`scdi{1aPnl@;fy2bW!(s~`mToIlT9LIR7xlrtqATo((QV0Z#8FYYQkl-v<+qc=zNqbJ_sL$ecv3NG~?4wcJUTwcoeL_pV z(W>PWX+b>rERk*KeWgxd7Myo9RDO7k}^+Tw0?K={>>m9+- z92wXvztjdTz*SNJg+n_AH%vA3{usTETrtJa75TRrl?y#*+SmC%0Ps&V(*ki}Ip)}k ztZF*!UvYHWU#K$|IEUXX$K0$v5KUNG1@f)yO~OE55EMd}Q!f?L-pLw)rN9n{uGv+Q z%Z8{mB*O60(2=Dt z)2Cqm^|>Z_o>zO^`4Hqtvn#-o;28Vd@0IRPB$vvG{OQ5>)k|hru{G~SzWaRxZlR#o zNvpDA6Z`j~>W%I{+&)f7or)Yi@^ey`kGCT2YuLUQ>e1$}wvje_X&wc!bmCWm1 zj?GMMw~6$tJ_ph^RKk$PZJN5S9C z`Y5aRRcO{8xWv?|vs60mjn*BeZolY1hs?u!@H~=ZVV@O6eDF%*!#x42b)DMrd1S1a z75=p(-0aiN^exw5govbmPbLMp7c89$U{| z*CD6NY8*3@*G%oGqf@Y8^an&O7<7OP{Bn5a1y7FqJZXGR6R(FS9=uC12YA9Qr>H;! zVKo9Pj(!f@CM8_IHMRVLlW;o6bSFpW?rLTs(fCoA2&Fo8MmfR5+i*-+q@J)=LU?zkL`Ec z7`dWbY&O=GO& zq()1h$J%yQhn^|oqxO%mDyn41<0v)TUD{!)R7-?(rX#2YQnz6Mrs6M--`FuwYux>K z87z;C6e67YGBVRztI6H$mGz+xRHnwqvnF-_PybHe;i0IZGw%y1v_TwM!a3ca9EH#r88T{ZD+%T^KsH-kc zrit|Q`lIPa7HP3XOxJc;OCE8I9^8J&0m;%zG#ffq*=lbu3n{r`4Z_Sq)^`96oAwER z7MY!_o)%sx;4G22dtmzdT>Z3mM=5WeRsLP7?GB$oPfx}GfwJbc0Mv04%dV%!S*vpE zuZ(;mW-Ht|JNf!B8X(-kON*LwXj3rB%Ne2Y=E3PqJ>x_VD$-OjS(x^^?aiwbV~@0% z0#-ZDg<$2qcjUSwWcCU7ZbzrFq1y7^L@Lg03jF}!6STer^xf&Q$|nr^^2*mW}x%|$=ZL_NCvsHPINtAiU z((N=IJhVgt&|ZDsbdPxMxn0{1Ja@s`cv%-8S|sX-Jb&5TGJdWR8glo4C@eUMe;vBN z7gQI#DTEw4^M#Ehd3z`wIwKKLI_0}iy)4WX)_XamQNW3PyK$&L52qM$n@-Ur8AdleGOnrWH;{-(b;SCvm* zR^>m{NcDU&v~X*AQ{m4X%~_IvOYu&OWb1hP+{{VP-ZR|OI#Ye-{&*1|{e%@hpeOm2 z)}bGB^ZKWbj!tuy%iPz+xw)Xqpkd1iCt>4w8TnoMOU#l45wB+=w^{r5mSY|58Elma zCZw*EHMx-y9a%uB}|BF7wWGAN_H& z_z=LP{c^lu31cV>8E1H@hNxLNeZH?a)r^LeiMPKQpvDo=QK(gl$J?hvAB1v>MlD?M{YV9x5_7Jfl#*mR5sF(-f+|`9W;&zKi|oKw0kl?FLt(@ zDy!W$3l|Q1-I1(<{XA{@(I#faE76dNgG0W&^Xc*B@UU(EddYc^+3|JX3dt{4%0?up zuWn;N>0aYqpa`oc_qKGz+KRhBPx?W~*k7&m@yi+g@ky=>mefd+?TfdIEb}q`pSdbW ze~S0@Z60@kf6)mtDg8(BIU6EN(d|3LdU*4wt`A1Oh<-e5m2rS>BQC-$DJNRFk&NIT zSF$W+Yf#>(F9dEuKm~-@(+$srn5D5cpdjVe!x@Q)zmY7L#T8SupaQGtAk-!n!fmjl zG2H^4%0xO|I=L8)5^|Uk?*}7Nf94n-TO*iFLykBYzadWoo;avrczuh!v~d~Q6<6>|`MSG)WP%`i)^@iy*ti$dhjq+n^z zC4R?d)|3BKZ1cpToFiP_6~_hEu`7>guy zuZ)CnLekx&{OYL$TTVQF*C*pins_;f`k<7 z`Q1QZL?*B~Fws8ZHEo$IKI5S^3SMyoP_Hh{widVR7H$bOf`%SmX;0O?Vknk40dRoO zg$5s?kgZqK-TGLt;uJRPLn<+9M18`t9KI?<4Pd-@Kfm4gybne(bcR6me1m7*h4ZkX z_b5dCiQFa#|tXC4e?uVMcaEsm(o(f}_!l?_m)njhNs@Xu}4EaQAcSb#lcr zvhT5pXWWr8O0OY%k(c!8A6wxE1i=a2XnHH!T{|*sP{EX8-EortDg}}fiBm>)s{fS% zk%(FlW1|o86sB!(TUP3D5R;29CW?B~bZ{Q87J5d?Zr;1QzPQ?FhVYKAVhoiULGuU? zz3RLeU91?MjoQW)wdu0q`0gSeevWKDF08y-9c$UiX!mbZnr$O82j>qYKlI<> zjhYI1+p;Y2=$@BYKV@{h$|>trX-UmI$3}zwJw?Rz_JwTtZkf0TE9mqL#sWx(A)W?# zI|p_#k-xOaeh=~cnfTj!0=e#|2Nw+gtrv-^u;#`bpLmfI-G~qQFI^9sdgQ;{oMq*r z$b<2PO>qNB>~KM?nx)2{4Jy>g7}|PaW{1sX6P;VMPQzdjMV;_=hvL6aA31%dLtl=O zH!J;y>157#Nq)!KoGqRgehFww@?rGM)6b$vSgn9zSc@8%^0<$Ul`tz7E5p1U!^&6n z`z9p$F9|4s8Ajp)X(r@{4s%F%hzdOpx2QR7%RL;iaY%yaz zRlWVx^iYQ5Jah_F_^_XYi!|5EcMu%a1~7aZs8q<6cj3FqY3`{HR`LmWLs$rZq6CVD zL+$>#4pp4adCB-%sy{tX`;W&{2>OOPBPte*kxl&v8%0#7y7E_ong>5SL6wBfl1&oP zq~6dXQt}p4;YaGM;}YTm0lI!YxZUP-FQM5k=l{7Bg2m_o)P|B_d`t=o{leLO>$xs3MiuH+cZ3 zK^$GH6{^j2ix&$|RwLv(OwDJzWip)A%K=zpY#%-f{0pr^$`PLvez$*!5m9AeeDDb5 zF_Ue$%C&T+P-vhET&3RqDa=zyO|OSoMD=tB1KH52L0_*i~IQV@6rz1W!y$2?6G+nSoKQP_NG=#JV-+F7ODty9Mqk~{3uU;tB z@+iq>bCN385Tl+a%C@FRDv4y1%*&n8ApW_?4nX!g3v0`I3#zeHl{eNyCgs}>^>pLS zqp9eq_Mwgr*8WcH5G{mXqSsvaO%LRFsu;R8YMkdgy+_CmEtaTs5pwcmhXt2XRa z(n(cmq=ELu28*%EixbtlzSlDy#EYNQKuVQtdV#SEj@y22W;fJn*PtXY6-JSGFe;Yk zRYuj)X_pZ02axiti8D)gSAeIUZCJOpME~b!D}%%G^$78aljqZ4W3)Wbz)_M$YJElXa*;(^d_FE7(pe-m}P$ES@J+-g++ht9L2s_OE?R5gu8t^ zOvFqrOkc;zM;$Z5gyE!*hICC=Rw|jQukC28KUk7~ifc0;j92;=oegah!{j|9J}Oz= z9F9l*m*v=Fh(T170t_9Hc9q0_UO^ol-`Es4+p8ZUv>dUF9ze!EN_b*9A(5NBB?$ed z$xMKY22dX7_}z)E+AVV^HJLx}rT?>557@-tQDGP5ZeExUT#N}iN6obEme1=yj(}wxL zfhz+57B)+1YJNG3GL2~u?*ar->~t(q5s?8@?UFg-nT#>J(C#3 zgksD=jGyr(5>B*cx@>aX2rx=eJ5iR)3U2yhcEpD=Z{Kd)$>8Dgg0T>Q5l4zJK2Wnk z=zOT_5(6i0<5QUv0B3G%(iVut(4z3*+qS%yIpMkOf?4jAktn zZCxa8C3|F0#BcM;R%6_!pAX_vpV1)0+iBJ_%JiDZD7JwGtdnm64Cc2+D_q}`YXR&d!Y)}bk-3EJ1Y4?1$o!2#bt#02$z)5F+Evg zcqlZzx&`g#=<(|e82z+NCC^!Z*OkASVEsijV3h^GOj5_I1XJH-pcIZ!qNG7}o>EpD z5(Z(;n817u6gM`#Ix60Ch* zkvS$nx<+lng)1?d$nv;oBV^NATss?+uV{aE5$^L@^YI@hPcJHTuV$yN;zO9A7UJ&y zk`y)bJaTYAo$^eqVydCMYN+qxrvc3vUpBJlo8mEbLYP7LJ zb$Y)L=PPl!exrT)Y%tZB4wC~zr9o?2>gTXbfgIHcyb$+p4nty`FG%^tkp-K!5t67q z@=t2>8Jt~npA8%17;~o5lwCb;Y*6AAqld?@T@2`$5l(9!e^uBAAFLNqM+Q-( zCv&&gR^;V*a3MtzS>g+@<#h~;il(KZupp&8%N`wxjW>pu{C2&er?3A&a3_<g=h%+VjCUPV&Pc>R zu+$qn<~I`DE)x@LFvTIQPZ7J!R5NCYHeq(qY89gM zAuZ>(HiX#PWfcD<{!yd?98KgF-R4;Alkr8|JaF?f?D+36Q^77sB^@aUR~E!xWwfvS zCfgjd7&7doomur9s&!}rm|RYU5*AQWD*|})63-V(*@sxJtefD@|AHP{Af{9dsZ}+U z)Kp%9Wzm@WN(cj?APrW>gGPCtQbp56%^Hywd;h{Y>L{4y!Crbdp>-~#wEYN z76_E#7US)f6a09;*!+{^A*$qsxQfyoF!P{A2fjTF1mt0>s;$7C!6nj5-^AUit?n%% z$Dc7wE1fvTH7UrnYfRTiOVjtc{ZdDZIm*P5Judi)6s_JcsLzpOjvO}mlcHZLBs1&4 zZOpZuC9YgFUNxOO^%!a$5c)A|?XL(A2YpNnW=T$K?zWv0d7Y^se!$;fSd)k;Os!w2 z1A&*XY!fO<>G?pE{2m{gJ zA6G}kpdJrP(W%2Y@wyS*cDUko2Oa7H5QLqxRL6iLe546B-(|=td5u$DDi1y*J8kn* z5eHbfzL{ z{O3_|kkmp0k_@}3`Nrx2xqv*S#>%cWiBbv%$VtbMtgp5=_$T^_BW%m3x40dBm@k5E z$~d}LMA1H|e7W?4kZO_Uv6lNWS1CN34;NNpQ*AmXhRec#r7@k`ShSM&q4ZlSfDjQj z{7>-+37hCuTmkr+`Kt_2;lF7NU91gK`%q4Map7e9dKcpA#!0kk*^V{w z?SGZTON@JZ_HTpnpOsP}e9fVoSfWh<);q(>#HgWkC$$4B(T>yUS3Ml7#Q)3e{ZIDt z4c^p?{BVy45LfW9OF(TwSJjx0Q_4R@FW_8GYDR?5fGD#^EK1q)=o&HZAdLWIn?UNA z7Y+(an;oucy>gt%33o|@tH0v^Dzud4e1a7`v6D)`ojkD%_v2Vr-Zz=Isb5^u%=GVP z=7DWZdD1tgm>QuOM!0%mkX=!=d?uQB^;};KWJ=LJp9kEW(y3XCNaOj2Mf;Fl?~PKt z?sBhka}<;vnV*j^%;)i;a_?5UT~%=jD0yI1b1Ay?_gRYG{NzkKgWrbE<{P6r=E$X< z*CqM~UIGv8G$Wq{?XZ19$(p)PRdOX*4Z>j@tky)wwcP z+a}Rn5Y5;{k=z*br;{?lM7i-r@QZuo_%50K(J!qf`GOz(CK^7Wl({%Z*#|AF`)>3m zH2j1jif&a!S4pw|1Ta?UQ9rO6W&8+*DBvgTPj0%sHp%e=Hx(dd)dW!XP|~~*3#IrO zn{oQ{IO|x9(AAjN*<*@oSn40veqN{-(`^R6-$)C=>K9x=mJqEZ z*)~vYr1%)7+6$_>a1IzYTiIMRMC8Lt2Icd?KXsk2F4>qq(=KY`6N@ejj_?QMqh+G zCn8w9zq3EXHi%sYCCrw7x-`^m%$Vz_D`ektLPz1nXP-tM+DrBC139b1eQ}PGXAE^e zIE_M<2*bb6e+M@>J z7BT7n;thakm)y%MNIS(uAo(1Q2urd45!$q zl=~1ZI-?4Div1#=Hah3lDh_Zgx#fq5&uX=ZBZ3_Jr2?J-_n4$Zx}_i+-uUmdR8cu# zFez0KnyCn~*3%QFb5I?MiL#G^vF%{`Wk0J9lJo@cX5y>goG{;v&}zwfTa!w0A0s#Z#m=H)4BPI}Kb~k(d z+b^WC#(!#Uv<$tL^9@H@``)z8r^V=Y=4*e%voitd;$Ni|E>dv(`65p8w8f>WVrG{{&boaH_ zJxClhPmJKt>5i0p2#E<#@pS)%tg<2zgDpFsj_5)2=2*fMI>lz_jSqL2$=SGz*fORX z?2mN8_bxRWg5iHId_`b*5#gID7|y?JW-PMRD7qQ&=KBxAoCW$Qn59+eD)8huyNlyc zvIj+^2)QP*1o{?htBYXYw^Vua6GYxl9g|FYl98@|q_|Da;^Yk*uwBU7T%p6^~bpkd| z0c*bAr=>I{$6}%_l+iGLF_L2;)I&wu4`{~3E)VRd=2-SK1krhIvEhYYmLI0#>rE7C zCQV9RWpQqJ*jkEOrM@X_!IU~g=rAMMS#<+(^yg^gkLv1XiGa}m#ZJ9&(%9UNWFcN7 z^Lu2lgx!>>O38-1>a|7oGG#L=Jf{&YvNh)d$DiF?B<&-0y7eO#eYV8bV zZ#CCZM_`4HD9?k3QBqF_(7J0wXE)j&b(pv#KT{r@0L0Y{i*n6qdX zmKxxxS+`iHdks+5P_hX+eHMO&L<3%U{FT2d0z{+;jykmFf3Mf`A}5ZYE(23_c-|pq zmNpCkq8oX)TR{`TP3Ph!r`j7}<3}iu>VQk;JYnARk^UnGc~z9UNiaLad%yZvPuG^< zSifC=aDtUv6Q?c`vmeBCA;DXbWV6)&R9UqGyS(pH+~Y9#6EORXR+~H)tWXi z9Q~ax^?2@j6qyNOw^?lS(Y#e;#pGPU6Ax*mRM;xEa;3NcEOmsv93e1@qmTamWY5Heha>Q2A##d`q_rrACm3Y~ zEAUSwhm97o=p6r?|BB$Bcb{%gK(%kjwU|XF7d1axnJ7p#yzVH-P|!N5Sv48VJ#+%1 zC0fikaV(+`;`YI?jVL{$=BJxx<@f`Xu;Y&4iaX;o21m=Nf`ckTV%3LOw1%cef7Zc5 zI^`Q|lTu_=n+#_`Nv}#Cu$tmia*hA&3xjv%(#ol9)`IX#X+zE93SkW~j56CbqV>Z@urn?@9=2 z?0-!=h88jcmkykUXAGvbhI!aB+60qz(&&la0?nIBIA|i|p=iRrK|BO5!Tmf7M3a*u z&VIx_vXLsJ#@w|yYnmZTsGT7F%DgL5VEj1A-D#bf7SWf7jMbE<0$15#DTCROXU>THHh(r47G08MZYH-%7X^n7qBnoNj zfGJXEK{nCuz&rRYqltWkwJHIbk_d^L3D*N*G>P*>`q=!h(|I$wtakuKL^nbLoqS^H z3k1cy4|`y2Rq?XXt~R)mDKpHwb_Ka}6J$D-H_ZL0E2H&x=J&dZABGiHw)+?wj9pj)YRvdL_Qge zY#0uhO;K6e{UgTTzbjZ;wVZ<{a{zFHZAvB5D?PG~j>#^JUk`ySl%183#R0#ovgrBy zZJ_Bzf4*$v^jLCOOmTkRrHBOPpYPFdYWbaodSXo*#W_oT{47Q4iWj&Kj)Nap=kad&}5{8i_UG8tKLS9-U7Hk^HC%WRN;31`<=|_6cNa;qq!gPziwfUealL@ zp<_!qch*Ryf@m5wjB(V39I6|O7Y7xlOo#27h2~r>F2~d>Oz}p}_Ih1GbnkR=YFbUe zl@HGk%HWRqa*7j`Okz=)>hnzHxvyIeJUSsqnTviFBNM>kyz&~)vJhC?DYP@IB!v0R zRGy-vNAPI_vaM{M>4@yFW3Ltwn%L_w-zfgeXHzp%=hYdqDFvKLtlX`gRb{VjO$2#~&(Yb=3!SVq99&i?}88;|6IM zs6N}nN|aR5x1d;;-5}v*rr}^K8_c|9ND7!Sl2fm5!K5(k6DQ=J6fwx~o>N^WoMVg5 zgam={RpSKqA(>afc81?hLh{y`7yozFrV|2l^X4|S=Vg!Hk`O+A)dD@z=#PZvuCRaH zL7{INE}zYC^c)n!RWdF@r{u1&j&R@sPC&n~4eY}eE+eGJ;yp_e&EYHxcFI5r$>!(? zRJ;2_yvh~jRzlUNx0Q!2WnqeL;QB|L`*5*FQu##9g|dIF=%-Fi)l?MaR}E(JmiJsq5Hff z>mma#BB&M?-;W(i(`UBLw%fKB#d$Jno`DoB(DWq-?86Q=f_UERt1pDy z6M6YuP|lj(aeQq^-jA-Q;JjPL+hY|Q=9TDUMRFe0a@bGcn)!j4(s;=`r&x6c*2%i(Y{=w`j}^*(KlCPmV!Yy?P>pB5U}x?o4DF4TJrrdsH%4d1v*rW# zZ&nXegT|>3A4TDr#TywTSWcgwCEt1_Jrl%y#c_Cdso(8CGm>eEfzNzZbmCti#`;mk zeOWQfzVz>Q5jz^=ljoH9`6bnnljptkSNiU9sbuKa_#P4+D;FvnLzDV<3%_D9iDR(w zB~oMWlpOg&ZwvFXyVxOPJInu87wmM`Ps4srTX?C{?p+xf|C`cyM^|cig^HF-=ZkT9 zPe-v2Tp?q0GIvV((TWpm?`OpRn>jO-Y0d=>*;E6%V`Kf{ulE}Pd=$OWQrcgIl6|&7 zS}xiJ82$Sp`@}vSuAf?5ftG*t+wCB{_RLW)w3CfR=!BIy{`$wXalF3EJJ`O2RP*;{ zXzD1Q_ElH6bsN)^KQ#4wMl1#~?htbRQj>q6BH6|}jTh6(>xPt1M;@%2gxM)j%By>^(AMX7;Xu&O0tO&=gG3Y3 zg-eh%q-8sJZn`3GBi0|I9QF@3a$WPX-S6g>ktc(0?vVSLCxmVee_|X>~Yr82P7>$^nI?n5V`5*s-;P~XzjUySu+0Ce%)!xU4CT0Pw zABD7guO4*OYm=^f^wDknXM@(8fW=Jp@V}CkVrvf_p8mhelI3_{)D`*L;07?)l$V|! zD*n;ZQo{b(rY%}-TW0w!#nQ7sj+1qHW$@LN{|RWlL^bW4jVWG&tfZx=f?+nV>SGgpAKE259ZHHyxP_akMlhZHr@xj>n z@o$!Xw0&ZpO5!9SJa@8D36I*#m+)d*zfn~G7il&v)>j{EW}Sye`7(7i7dt$b3Bb9c zr9_#=Z1{?qI@5c~7Ym^w+|B0m^GnnDD4PY=#82l)+Ko;-)aywDzKx3=&H2b10r{M3 zm3UW$SmjQuBR}{u=opbdeQ&;fI;f2QJHy#$a^T% zR`TIBZhp4JMyP=o{-j6xD$8!JX)sZUy-kN{rO~vv1fo<6>^S%Re3rRr{Rz?0g z@xiR#ZL`J+=@~FGUETtMRlUa!O_Bc5I0l&npN$@MJGIkfLb}bVWXrDK2G%E+lXC#O z!0QY;Lx<=hr6c_3?)2^IX0IBx-2=i^)HPRPwn1@|YSDhB46Jnrwx^(WQXC+Q_I7bc zOi~_PpRMj@)FdqQR3~VRAUVi(!62eXc^<^HK!@m0MPIE%Q&zd_qyCYl!{u`AA<6YN zdy^bilbBBbT~gn4uMm1{kAL{;nZ8K_SGz=0mk^U-E9%po9y|P;Vf1^b807&!n(>H!2>l}KyVfULiqU(44oz;InBKH}q zbM`#jls-NB^|8U)wLh}(<<-nt9Qt|+IxZ%7n+-KN*j9OOl2CoQnkI2R<90a`_7vwQ zc08F@Ca_KwV0io#Ww~-L_A}>gs_g0lU?qHQIy)I~^}Kj2^X;6L7vIGIxlghGE5g>z zo$*2c2)w))-uTW}v9d=SY!of(Uox&0AFP&Ld8Of_*9PTOeDJP2R<%r~J!`RhI?R&! zs6I9Fpn-Gh-@%wfvlnhKjYHj!*Z4q~&-Qq|;+j%z(&>c$%1`D>0y51Ho3nOSr5WN{ zHw?_B);Y%e*Nf3D^U13Fm2$=4XaePiagE1U{}`NuUNdTGjISC#K_c7ryDfptrGh@!C+pPV7pd|VK+zml4ZZb`=>YGiKEcR(TTkN_?(Aut+7QCahR?QRRo*hbjSm?9v=J27 zBoYI;=8^r77|rDbeZFOFakEEvn@LXL6etec1bQQZoN;3#s_ug7=OlC1Ys>zg#W?7j z#XZnAq-(con@{NVe|anF|M>p#zEE622gHp~DR)61AjZ%}WlG%@J19Bt#sfl6Xfn`F z4MC-WK+wyF|1$B9pZ$!a-Sis~%AY^+SxQ87Qr!-_J9sm%_vm>O-XNO5*_s^&lovcD z5q;WY&<&oK`(#wF5{{Tzzq-Dc4a4~C3ui*JUuN1*ZVUH;$cGLrJk=2VEoerOBBp0b zE@PPdkN6e=dF(k$;1!&5!p5zp`tI?AiKD8LY~9%7LcEUfvPD!G2Ul{-@1Osx73ccN zNo4u`Q&44XN~i8gdk2IU(qOU7Hya>n7-i@hJf`3^LQu+yR<9M|Y@xT$0z0-7ghKk8 z*!Z}oZ4XJq9Eyv1*nnk!(S|bI{GfH=j70-nl*D{@WJOUkVRRzRR`u*9YWe@1;QT(Z zp_ulR2kl&QMsWc(9S6<44@?#}7Y%8;V?Vk_3 z@GEf^$vCwVXXUr~)DxocKm*&5df3Uc0FtQF@4xEGKI#0ve%Zv$1-1Y#n)i#i#l zv*@wZtAA%`^Xx(3nALZ)^{2ieQFjpZyLLw*T0z?X{A6v1sBh}=@aERn$B)1$fHJIv z57xWDXR*oV)&Kw?07*naRE74zm%UC>Z4+;xd|pghW48Edsr|dr=Zd5V{gcy zGfZ>3@#FBuU;S>3JaeU?GXHFUw>`V*Zk?phNVI7K>uorO!cqJ%tv_Rwy}D<{0=09J`}V16rccP4BJE^1gpgDd#E{X zFOPPXWAjXFd%L<#sVQOZjo-`pZ4G+6Th!UWCX#V$#mCraw6hWUFCAW2=ZDUi`wp8M z+ubVh(aZvY3_j{}>lBDT3ivbzC|^whU=YM1cgp?sE5Jw^Ous`&l|F$?4NX7X`FqZw zRz*tDZZQ44BjEjEIcJU7)f;EgX|i|WrNbwT79DN(j%<7ApG|7)fmba1``_ZoWR;k{ z;PR!8`~yzMO`~J5S4K(#of20@+mBUW1v1z@y)*E%quU2g)}AhL-6XdVYk<9+9#4~b zs}Ow(iQ(x}xi$tw;-D>Lv_3lyOH6Dyk>X}UR&{2(4#S&b6AldeGRgj!NHG1f`6)}V z{8;MgZ09LidONm((&S7!{OOm}gjKG_80ZLyNK0{x1$XP2?u#Xn+(4&Jd=!~A_4 zwTH4i(bv-ppGs~0Rr0C~=}S26_b%E6%sk)bOObgc_wHC}Cxh-&=IqE<+I>`nmU@d1 zgQ30p19`DihwY-q$=I4RR>w@!7Htn0*Fr^~V>04!4SqZvo;|kB*%r)vHcGVN^tI6y zm=?^c(xq7d&dJDOae@A?<%?)dvwZPJ&+HRWLo6tekfp?;E{hExXthW9KTJJq0rI zHR0(xltgis2%yyZHyOb@NgwEjhB}*g1N?P%I2LFN5{FX?b<318_OVPPt^`| z(lO3q2;|)V#6ZEf7|2d>FQCwT7r~}JN7RH9S!8s<@t4p1oW{vL=rE9LpF!^VW!nWH z2ax|@&i@|LK=W;0PL*u1X8tXB#pcW{Ms`1))-A>RspI@UqgZAV>rBw@8;iDc5Z%|L zyxeDtXOky_Zua~}n+U!o3x@Xg2QqlCeo^(+aqFE$?C@>vVS~W#PksI|P5UQ;TH0P9 z9CIVd2|PX56h@wk{)p7O4(F^o<_V1S^-e=F&;fDmcoW{vl_l(J5%d-WC)W&n{|3A2gI$JZq{cQOiGUkL;;_MCFn?!LDuxsDPqx(t^DWfj8f-roplKH% zAf^-j$)P3ta!f}04Ty}E0(1+R{^0!lGBgMYULZNp--XC_!pc898Evnp!5if;j19cX z>iSBeh`b|ZGm#e_$Dr;#%i^(N;!L#Nka|GM--2$2GhKAETU{K&RU5KEx6 znSbi};J!`)pMNsPZ|By|HlosU%x-mhojC={7Tad~LygavdS&*V?U$?CAd2~+YhGhU zLmq*06v~U|-}TCQ;|N1v42@8mPw1Agz&0N%oXkJ*@;wOS$eJa~`Bfb8!yogs_3Uix zh9hi+4_Um#IUeR`aO#i@?~T#?pFVxo3jrMEO&t0tZ;on1Ho=jJZB}NV@D}^ksd{rj zG;tjppAa|S%xQJd_WCBT<1(pFlxPpN@peon6~C=6zEWv8c5#;illwJFoZ!z6bu5dm zwUrZTz3HHi_z>#4gK|*e+3?V6Y)mBT%Gl>MBTlr~f1&i9*JZElM8gYwa%LP=+CTW5 zr1bu(S`6-hoYQ;IBbS>@=$!WsBmMp%4s)=5f*WfR`|W3Zm)CFL`2_%7`^7z8^4NwA z{wG3n&e9&w>!B};{alkCvdW7qSOeLP6}rw#?mcrAA!Be9)ZwMv zjA#()>%*q(s()g))b8JbMWl;{i;nW_!heA8ptiV!^?MswCTxR9C%+cIkn>NB^p4{# z2i;wO)#L-eh0`|t4o3;c#%&W{ly60*YK*1Y_f59(!rpl7Yv^V}+u6v&DpxzT1Owi?ESJmQE*2ex=Wz z8@9FUI{&hC_vi)#G-`%Pl&qAj%NF=_zR4E;-e3dYCGp-MdvSO|1Gi`Xx z{fv72axHn9$X0JC!rC0dCLC$8Iei!Lv&cO8w}RcyTiIv7n;dnG_t~S=%PadZD@^CL zb#0w5a;}K?wP432<$9U1C%lNv_hej{4!<;0lww_J*COYybAJA8EJB8`i{m`^PS+t7 zA`3Luw{`eQm*)hB0o#;Rnam?~;k5yTFs%vZp)oXwbi62R{!B~5X3XHw-kUn;nf=;P z?6Ly}P%Ohx3R(44gOhIOr9nzxx|OgfE9YwRelO`iEOm|(-asdlg41bo1fdK%ho_v9 zJ1VuGwe{C^XMW|G!kOW^qponysm2w4pYr|XCbAJz>tu#6+GyE|?1{(MhM9gpw(^R0 ze6Ucs`lmO!TB`At=+j{X+fkx#&b>F@TCzydhw;sq)4MrBw?+Mqkv`LS^1W@Y)08hS zu}+8Jd+{?-w8B_vg%y8voRmxYEmv;BzxQiLaLzG`x_f=IM~LR2XPH#Q?_x^Q?CUOI?VXXT`6jRxGSKJn^a%(cOXNw$0^ z>R0BV{nD-UbX`1nd}q+>OKYJdH=G&>UF?bIzf>dBhrT#d^d}}^WtLD6pOK^{Ny<8_ z)}OIDe#|eK$9_bleOuavN2B|!3x;~J@X1sg1?f1UyiX%t5Onx03_14l1=x1NPAo$K zP{{{|K6nY4FVpX)tqU*mkG>1`>C_#Ae1S1BUa=O6j#@aq{6l1e?-`;j#;2g4peDb2 z4SL(;>i3w%E)BZSE4#N&T zU#bylF&eq{X{*0M6kJ}gy8s-onDsO`)0-;#&972F842`1zM94Xyqm~kX^+#$xUERB zN8KLA;o0DRj+o$Ifn8` zX6QyEN!qgI^4Ly7z3X@}p49lCQg6dx@JB>rIZzcciAuE-k)@FvYnC46R zPDD;|JHwrxc(DOuc0e?5;<{6~*lyEezPXn68N!qM>V-hOT2ojo-mvd(8ab_$#d8+| z-wsVf^qesI*b%-)B4GYl(@W=u$zkMtSBe%xroTKPx-~UuX!6J4fognI?03vx^gSB{ z^WxQ&w!E`?*N^GSYNOXC>J>5JG4pjiB-=E_{M?x0uf1C%tPRh(BIlo5zA@*M<=GHF zivQxS*}ku}$PCaRtTT?ii#@e>jwqb~ zWN&r)0de!xQ@Cu_HW1MLXHO^8v#}a37%7IUBztW{7&r4?t*v_*1&#Bx4%abIMSBq| z<}#lf)3>1e>+G9n_L~o>1N;y613g)+pRA#88_?<6MO@kjM~lrD z$E$p%rjO1g)k{;y@BM*|<49~U*zq(c-Jk7kdU@qs*_wZC=;0vyLp+UDp?_ul1|(kt zbVq7Gh6t!a-@~VT-Yd6*U%&ftO8`v_a!h=R=v{?x(JAGm|D9J8inE_P^h+)G-*k|N z$)M|upXbPM@PFY;Gyh;zO&Xbg`a!_p^|3A#@b;HTV*SLdEs!vTBcl#!z#JP6vdee- z>eMS<7HF0jc5322`gN&&F>~Wai;=>uN}E>dwzUU$Do?{~F$R$FD?i==qNbKi$p zAS4K2KJK=(nd5x$GyN$cm|aBh@7-l6Rb4TD-&NwFyp6ZF@;V!E8wL# zVA%WjDQ`l~qDDwS^tBXGZyXV|tvII@>auz<-kb&_AMDnGv!(vi8A;4o#vzCSM^-D` zxNP`kk}|!u+DH`zsCWU6C|x zYe!>bojxsov#9V(3r?MKz{g2aPeURN`DtAY*IEP=zyeOKDFCR$1x(4^oWYq*%NeDG z_FOiz5_U0wplp4&hRNjxLS2qUIG*A2O)iUdWvp2D;D*TI&QI9%OKtYIDUyxG=r;PD zJZOu@@C8@5MVRJBz{-%n zeO1*V=L($->aJ6AIc{F|oL7!Y7@Xd?i$%BFE6jrZq(j4!T3FofAJX4ukrcSr<49X zpFH#Or@7DBYnkKmyTQN+*K5}aV{hHe`uTSr+u@iI*cxFjF9+O*`R+~|P0jYHC4G&t z`mn*BfGfRr%y!Nsuyf#DuZ@}`<9;rF_Ya8w_=CTW8IzkefRJ)xIZ2i%25`YhpFEk3 zfdE+Ae3<-X47LUAMaKW@Xoz~7PlEh7Dy{Ff(h&t(=u5A3y7BBUw=woFFuWn^&Ph|y z@rR3*iYe-}_@Q8F%9=EL3w}Y|A0gBU1HUl#$Q%busEAob6&<3RP99yrjDu#nTb~n? zp!?czR-f+KvA8K`(L3?P&jyKxu10!xa7>hR39yb!Ci;vm#qqFPGli0? zbF%jDZS0c7&zv&We4-*wk9s@_j^fk-=@zVF2J=+I6*RJXsKZj4xJB0c$+ z2h%cpwf65tka1eGTpFyxyc5@=4-TV#s%dM@=46zh#;lF^aH#Rxm_^OTr(Jd6?CuxJFgcGnCv zdx89Hbh@Hp&vP`Z90`FOL$vv1kF&eh=X;jJGBGvJwyqV{c`XCB$;zzL@%02os_R2W zb}d-UxNeez&!$-d>y>hj{`%BYC;rs?{ht6U3D#J30K!=gN#jHpt`k)6Nk-{(fw??^~IF67<8d)+49{e^-B6 z0JedFE9famh4yP-2k(-y^bw>hK~!E6Q++eq9{us`_fI=_)&~`0n<>xGb;o|~l}CNH z-AqlZx&qw8DOu3jA16_NC39#rGz!2IF1{D%#uF4!Y|o8#vwZx>p^kHg)jn zOSaxwKttu2|BQe1!h`kD{i86S81{)y{P=|jYs$KBQUz7|IfLsLmX{yU&46s`?n{Gh z?IEB!;&#fLbIldw&p&_rZ20AKquSn$`tS7n1xwoX!!M__qC$6Rel3TKT6Yp;@=|dI z{Or><{*1J{twxG18q99UeJtA`nbdv_5Z-iWcS*j%WMKTm8Z@QTT`~Rs5rkW+?(DPz zyb@v&gMxm%%4dWPV(BxWg5(4UQyGKBsX;)f4{*~cfZ!0%1y)OpmObLvrfFMdN)vO{ z{JU!S)?*81IATY9ARgxy4|;1lxsH4ovSmADeR|~8i0O42^&cr-qK^+M{cisKQKxF2ePT@h zY@zSFzL|vFMDypfkb1MS-T&PLAZwo9UEO4#z0;!&)8vH-`6m5=cUn#u1HW9qv4PWf zLHJ_-;ygbVcU>=D@Eg-0;PvcrHt2tZ9q6oy?$Vl0Bj)eg>W0--AVv-ZEWd5T6!}3a ziXr)?L3DZ&&mz{tozLsA{wWiyIZ?BHe2vc%DO}rsds|gMh}Dc;49*%>k!xds?xdfC zYl-hoXy;4ohv0nIKc7-KAI=IE+|?GV;RXP+?`~L<1mApn_9s^i)IKX)Q?w1G!s=eM z)A90C6m79|^LTn}AFH)qUWm4#rwQNl!+xJ!wOQsYx1bw!+9_KWeP*UgotLbo${F6@ zIVfWuWRtrKDvdq3yNGcHPaVy1`X_ZcqoWp$*B2RuQI~ZGBO7N)ra4FG(?WHtuUte} z`s#y&CB51B(WPL&s7wj|6m8n`0Y`aD%)XhXVX`)2RkT~b)NPHIcdV3mtyur~q{~}@ zlfaIjE=8=tfA~1|6NnQbUJ!Qxa&21Xy~aJq-fcQf_Sn`*|(O+C3-5;F~8Nxz9d`x z3*qTAe_hc%x|wfN2&>$Jbhci>xAV8%LwSln(&&FO-l(}*Yh0kdSc<2Jebqfs^uwqP z1eB-LDzPd)>TnG2qh+p-O84E5t%cleQY{|(+>4>tA$}NdgDS`#I^aGRM*a3i!S59jgKz9X zst9@Bps3+DHiKO#j2j9B{)4xVUED0c``>3=7`U*zO_HZB|8?SG9IRhh-sFc@2mCic zn{?_!j#GkOJZ#v;P!}E83Al;v56)qypB3LcBelO}i^<`}||b`Hzk3*h)s=GhjO=Z_29jGf(o+xUc zF9(SBy9g8-=73%M>rwo+U;QRE>~1b)=;IiW^(Q~1(|*`S)4p<5K07X3(1RL=MV3&nqL4pFA7-)KbH(nki;6jD*yB|ffrxLp-vepE`qZG zU!aKLOpMIY>E@v)u(%B{FkvLFM@V53Ib>UVbG=<4^vrms-Q%=s8p>`C!|6 z^1`2u#m6^WR+MW!;q3T!b8N2_c>QrM_z9Vc0+jAi4*$5Y6H5gSbf=gX%y4EKd@s%ja4Bti4|f!Gz{#&qhsPA-VAd6EW2JN2COv=;N;Y1G4+n}SrMd?O!*Ana zbx>V273iYR*J#=QOxc-h4LXl~ickMXyNtKiG(dEO18)9-*@h|supA3_oS7tknbF0$ zbpZ#z`|_~l4?=u7$UEa;jLlg4Tzfn;hRh0wXKTGH$PE%gX-F2D7duMf<*ZrOGtUmO z(INheO%?5gDc`gWy~P*4>ai##Ab>g%+U=xJ>)2ofp0g}g$ zgLnGWQKPDKj7bj(DC?2j7LZJs7=cU|>12qG{ z8fHW-#T&l$534;-z2YRTXQP+~ik^&ejz)fRwoi27{ii9iw)vBh*ovJl;bexUt^QP# z$2#$L{uO`DzqE-oC%m?}R^V57KIMduSby;|hCnxt@Eh+liIXu2ro%oDxi~2w*20iY z+SR@aKx)F|o9`Ux^N%i~b-a+m1eeOYfEJ!3q!<_yrbJK$D8Dd~{eZNIz59i~*oc}; zU<$^oCx(m`q`)26?gpGZ)J-_Q9H;w_Ex^yhU6prkF+m41@PS}Aq<{XByUT|SwmGq1BQ)0^wc+xCxME{w%r zye%kMmYewjj!Ed#gZ^qBV4<_5I2o{cCa!|^w;9CF@4?ZA;>*cN^8QtmWFS`5XFI)L z;x0mFm^NsqPbK!T3H4294k26I#DITs1#!RWSijM{yV?9mV1|IdC*QL}Sv3?^`WjZ) z--y2P3cLf{IuVDmD^BcJ2j=P$wUiM~1}T}efgfXU+%!>p#q{bW&&Dg}6g>Hur5OIZ ziF3f3YXT`!aXC`AsN#M)WbY*<{T%iIlJR%}u;y#_!Y4L3ovL1hB7hhp9mwi`9@&>| zoFVG9N4xb`Ok=qi=(iU-HxFKfo-6jMc`>UY0s&WE9Dn#WjePRZ@{ZUv83nPB;n#?7 z{hBRyj$`^@kqs`TT+29BX2-*FnV6AKYG~hoE!nt$l=iRD}wKGCMTJyp9BK7@lEn(;)`k z`mDI?b^O%N8qLh-prs<;SkU2W={lhw{<`Uyo%+U)y5a}@$w>AZh1p3)|Y!YTf*5+>yJ!Tm4V!uqkw*UYj z07*naRPyVFQ=rp9?Njo{7P`8wQI%C+MK=;!D_?mi^@Ikm?rq}rr5jMeNP>NbwCrN> zLUd1~r$d6d4ZJFAdO5GUeswkmFQ=bK?{ho!L*Iq@aQ` zvR||B!><(;e5}6Af`sJYI=e|W1>%AJ(cd_7SPubyHl`6i`m{}~&Y)hoEE8>TJlmLOFZnb8&8B!0%t!n%MGr@ps9O zr{kVR2>Ws*G6t9zrtYcRI9}sCM`#PwWW*gq>lM|PI{8nZI+~OT{MSP_FOc_ilo*-I zz`h)Qi;pho^0XPh<)Pz{huJkNrM(NQPokA{ye&JgZhLlewqel$7LnfCh+xz=rgGW+ zB~9%$4nV6^CAmw6fuVdZo(291)n#w}&u2AS@~i3>zjken(Z?hgulUKo33{yPpYzeO zSiWp9=8u2zG;ZEl$*&$g(Dx$U8X=7qeIkDP%(;5Z+bWIU_y5bgeh?Wa=bdal9k>Bd zPHOV>RS0D7j?9!u*(fi7J_Ts5Q5SC*6CFjOzxJ+FgHivSFB-0^%egO}-PF0B<7>L; zLwWl1c9_pz&ziXifp;n&Zs%e*A5-n&tgDJaeikf8o}j=J&Wq-(V%f0s!+K*T&(kl~ zS(?=605Q74s^F(h1I;(Otv3qL|Me`&^$C71y8f}LX7HXGG`Qh&10bY%=tX@QWV|OQ zmiW&;uafFkZc`LqWLujJ1$C9Yxsf!)#(_=Q3i2*ouA54q|JkFB6j{ZPW^qi{bL~2& zP`YcO@$g~I_tLcmEf445(`lsjF#)2)heTYzj#|`LupB_b1JiQ9?A_{q-uctroX+(?_pPAGT$EqER zcKD#rJ9~Sa2s#iK{2FC&{Hq^-;H1rB)n^=aX{>?j1|*+|nW#-N{)lH!>YPvz$tWkQ z6ZGlxiIR)23FX7IlE=-TU<5+eHi;V2Oy#UzXAyxh2u)D;MKJSlC>>b#43NJsOR|=(DiIWtYhIIqwh{|9Cm!4`nQzi1biFNJ6Kd@Rz!tC!P%DfaR5l4xpKn+jv+ zAHx;d4RM19vp(I$@L?_m#%t_iJ!PhzefvW`SfVM1KK&!OV)m|xYmkeQdh%5*@^Z$;Y|tQ7S(omOu`D*V zDl#NX_($8CHmdxxbbUNs_imzH6psfYpuT-8#?$ffY%J5w*;4f6*B8aKn~L4~>BXiS z6bKJCn-zYE7Vi#Eb5BQ{^N{t+BKzyFgSI)K4Hj*jfXX)CyAh~&2giM})SjVv?WmSA zYQ=!YYf~BT$vTnFiMIw9Z?QGOgXcSDmtJ73W6-;8S`K6OIFi(RDx(m2r9q#!^b>;a z0Q@)QJ=x0V^6SetgVrA_Fh0VH(35s`Q{NLg>Gbdm%a}Yl*q%8Vs!u+a4C2Zv&iqzh ziWtR}v!=qj6sfyv)`klFkp@%j2>{vT)FV@)~@8T|={UE1b#e1*;5k20B z|8Qf{AZZMYmglHvX{v7nX^>=~I4t0$pp?Y%KeR3o47kAf&W1^9M_4rc=^NG!Ds4mS zh10hS|KNXb+uy|(d?%9&K?zEKwqlP`zw^kkJAm-%%wPx738`L@XOi;ip)G@(PUl?F zeoS^bD)i?c$1QgF13>t$Q9bGB4Bef)gWDQJXSQiW1vz5U zrafGU^?`G-5?3?Dw9f%{6Q0b0m2w#jlQel@^kRwXNWei*j&oHi`p(NAyY);vg_5SO z9X$td8_)5m%2>(wfLk;KsO)UAwou2&c5;JpKFto<-RSwbyltk2fZ~cL_1PBQ>OQey z%{sAG4?p9*ajKPP)C`Kf6u>s_)<^T9`j9yhFp<@H68S0z+2pBXzSUfW6xb7a7+5Td zEkSQVPiC9pwsVhbtP*G-!_D*xq&c9-bJ;;{JuWl!>G8&A8tXok7y zG|#dj6{HPT=*Q7!*>ph4hCt4@-KZn007cu4!8Qu!NMsJf-IKI2Yk?VHe9a~2ZOfOo zW`R-;IQHBC^)_PsPlxkh<1(wK&-)mH)jF*a`flluEgTH+&g6A~A&cX`HQEL=iFI&N z?06D>7#9l!_>n`OKv97*`bE_uhIKhlWAw=&aDN@*IvnVFmS`UhUU*r6qHgcTwGVzs?>s(9=T~#2Q1z!QjN4^#;z` z`Z5WuHfiifHn#LpSHgLqPV3DzaeQ%sNx$}JAH`$TyR&m;Gm@I#yA#w~8+8Dgw~17$ zXsL+0rb_~b^1 zC;GL+3spKfX^PXTKklKajgIX$!Jq= z@Jl>-kb!Y`FC-cA*^Qh4Z6Y)*Iwx{a+qKYQ{#xDzwF@NeC2?Kx8Ux2cH@)~5N$Q$! ztSugWEqway+lbtqT)!vV+S)`tJH*@wfqLUXSH-^XMc>kAppxI{xzG}!;$cb|J4L0* zr;Kgm;RLD?O%AOZhjDmsZTdFo=e-{=w9&=pQ?8+nzI-YH+qaTMweyc)8^f3~L4Unn znv@v)as-1aMPJi;b- z{s7bsd^1S})GCZ^F|e(FjT;qY9S?O|w`h$BlT-tk*R5ak<@GCJdA1Rww?+mql)cMx z^INsRwictQICP;r{U}@Hm6dkq92n0WSMTf_-{XFA_D{MqWS5-5$OAuyxWV|8gHNqQ z;0xo1C^loARfu(CB(?g4TkGK{&u=an+VT&gYng4n_ONxy*qcAU8Iui?9Mjd2g94P6 zR1s(!vNqvfpXnCEaeKva(w1@#vTJlt5OC<(=nljB%EnpM;ur24i_?>TIX)j>p?>s0A%bmx%68-B1@zE_$?wW ze*NA@cegU~D<@&(P=VkRKzF2&x-*tZUg)udZouE$TPm~vSHnd{nM^9$U9F>0xLTz4 zWW)>ioH0R5K1OX)N*XX6G61zSZ1?1b(X>Phl~S;Yp{*A0{MF}pXGT=&^qaV*l(KHr zwCZQJ_I(C`jiGuW`AaZ8t`SysZE(q!&Qi^{#ySqly+z*SRj+?eZ+Ujblmf5^2m}?F z-{g4{X9KtR_!sn+p1>i$jfa%9wvM-esFdIIa(41&|Kr2tJ=rmd7lRid+i$b0lp=cX zXNk6!ITp?dJ?E$M+5H4eROs2Fu!VCQajNv9KW2z|NVgdCWFCs0BvDqM7$dl8wI+fj zZ7Xit`e5xRq{%Tb8-dFp>=R*-%MYJoNd1)%_fGcRpIz5*1ubz#&Q{Yw%TuApK8rZ9w9!JQ=@FcW*4st zYk`Z@_?VYxNS|ef_-7Z=pZ@V5<6yy>oH1$f!ve>#3yn1OXyqsIkwwIzH#g)f56zWX zu<@cXWXI3i=3`@dHubaudYdM=fAVb*N*a~n_Z}(?6Jh7E7=M}=vCQ6S!*b-*q-_}) zqKv%GB5NkuvVY`fJE9e>YChBI=1>3m-TF{AhuIT(u#MCSrfTt%h8{1#m(<4Dq8dqE zl!?c;{Y_?fNupo1^6sTx<<)1|v8puG>9*u9{B}4`Xcss1occDP-dx)DWhNUpFY5;( zOfP+I+-UEKmlr$xj1e?&dVrw5CtUT$0nUB`obApNn=fn|QwgJ&!30Q)Rs^1%UQCVM z*1z#T`|lH9c(J+ECdjaf<1fio-XgEg;2E+nemNRlHQbSFZR$r1v#NC z4L|jF_CbNV*Z;sdgJY}po=#Q2hY#v`cw&xI&*kIQfI>z{>~e>*S?Icwoq+d$nw_t% z6|^1ShpTRBT`$R(M&Na>OAd;D8(8wY`J80BWlxtt6;Zxw_I*rsT{m$*GTNe_&M5R- zR~jAd1(59l0yjnT`}Q6Ty#3m(er5e!`t0DG59$()4ie)-BN4h5Vn*v(^3pO&gR)P3 z{#vwe-+>KI6Xf!-{-6KBClVpdMsd9P-sdML+h!HI>vPvx^1+Rvn@E-X>LyWjz{pdi zX`A!yZ}{7nKY#mI^UnR>yV*|&gdVhnbTPOaJl7&|fz5{>4x-7jT`B>$$CszhgbN3a zK?5ZXtNOxhf^E{dlRnmV;(NlwAOp~*k-_?t+demw4K6Z%Fjd{X-G%%H*O+k;K$!!# zSNFHzyCkEC#*LZHRoRD)we}r4G4%omxP;1IQ|6zQnO4-?k}FCI5}Lh%4q zAG<+$_Hx#qFsz?GJx!N#=EXN2?I_8G)QYode$3AN6z}wyoVyAwANq^2Yytuvhe1|- zu-IjLC57daZEYihyo_UnlsW)XK&`*dZ9H2ze%eHn`+$73?|gjiU*r>|0R)1x7X@6+ z4x&}e9oxPW6;<}T6)^_0(laH37R~PG&#a8Om|;%5??PpJPtUN^IMV&zYN3j)P{e*l zLo?pvl*4XY@a4zXxQ(ae3VYVgH|2I?rbY@X8hg%J>!F2k`n2KmJ(_*LlnlPqI~)Jx z%Js(2K9L!9ZWhgnQgkJQ(Wy?Y_cMa#X6o%fa>$x@f6-Rh=B@m{+? z&}a=AP*0OEz}Ci+Tu$eFGY?pC%HQc8=dG?hENArcMO#y!f6BNbh`AeP2{7o6+NisF zt~h_bjo{(DF;YZ39HY(ajjpB;&q5hDJ$!9x7HQgfp4XOYJ~~4kBQN&N z)%di-iU_m4akh24hs`fAV02n&cb=Wnd=a8KGrTf^4rA5l)=JQq|L|(KML(#|g!A35 z@~ldh%b@?RITppgq8lap=b)`L7R&V;n^DEZ*JlItF68=kv`-!y5`f;?;ghm`wtaNN zru{wquafIB%4t-r_J=|=vU*=b8Jjyub zYmUIPte1KE5<7OYZFJ=%E>iLQ`(X>f@yY4xPI3G#&b5O%3cmR*FxoPfe3O+QV>`O_ zh=|L0K|MZB--0qE4f?)_9392kb*fI4F4oYCIdn}o@7)B3d+W(C)WQqg z-Ms6ExrBw}gSKver-ME;(3q{??%&kwSZ4=y$JV_Pry{pV`%W-5@dR#B@Q>*5kp=cW z3H{NBGwH8@;Dyf3oQ}BrH3&cZ6%?S9v_k_BUVz;+R&gQ7gBjwVh0{YHzZ#2aUZ~v# zyI_cAko;cA=sBUl@IHfh_81_)dcd+ffS|)zfDIb<^5^a9)fV&ST}aUe)et$z277is zy0n$?IbJ^47C})5OZ@Li_Ym{NB#8i@inA4eHHN%v(Ha8;`sL-7C0vclb$rxt#ZkDJ z!&EH5_mOiK64<(9!c)~~rRo)Tg6A0>yTcp(d8;}3ikxGtD#Ut6*Lucw@!mw(#G=Q5 zQvS#xDv|)RA5&|)P{oG>-8D4`^r=DHu~!uJz1Jpt*-^ir|F94dj%T&v1Sq?s?t&xV z#?RWx2rRF`0K%r_>0Jxn_*}vo0enFh4!sLIcUb@U!>8{En7yBp+^0qVR}7_=3wQ3J zF*wEfCSp%Gm4U8_pKY0npl@-Re@?CEfp+SJ@!nTa%qr(~V{~JwL`FmJ?Z9U*W1`M& zoR8=UIddqxu~B`CDZI4>#7>QsKXAnuO-bcFa<6+vkCmghfyP?*gjn9z3d3A)3DF3! zV4)bBoX0LVmnW>o_*=p?McoLpp z{oUB49{T>}9ok@!$F>U9*#jPED(kPjvD@qP`vXF|aE=2qoYcAaqTjKH*Y)n*WXrCv zX0jG8=Cc&`^t%q#dw8xW(k$Bd);G<3<};XwDrfamPuw_O-fmz z1z>%+j(0qC6=T+Ob3;6RZGEY4Y;u=@r=G6<1*M+L?`J=ybwyG&?KXIPJZZ7h1aFYA zQ}Y-UbIrcS5t`#v*LdgNukxh>KChL^%O^JL zVPnQIz0k^i_Y%mL=i0^%shiW*-ERi=F%a-jk-WZ(U=F!QGZ@PiZnR-)&O49b`ssiD z-~UgpOLJaBN`#wabcdr67M=F1d{S`4-WHI6L4RIf&A9&HtwxP7d4Te1BL=k?>moKv z_rEu2Rl}CO)VPSfBxn(XZVKy{9`%{4n+yOl(Q@a``JCRbIH^}r^g1gj4PrZrnzj1? zv4bNEHu*i42Cqetw#j7ZW-@@8&gUus|4kowRbXg9Y=GC@GkT?Mt90~qP2W=Fy+IcW zVKqW4+N2E_&j zf66DY&fI91K-F7B9Wxk|bL{$XzBtJBfo;yM-R2F%deVcHPoLwe061Q7^go+ur@I6F zvZ&0n;_$U-KvFy#0Lrt@oBW{|6a=){(uA(Y0g}F|(*;MC?kr=K9!okT`BGm+=P`are1Tuo!l|IPQtY>=*OCkL4_k^i8==_C%N~m( zsfKGSqn)Q;zQ(uM81HqITl6({_B}Wdx0Z-Fo4a95etyd9XTO*88PVaXRL7)esr>nS zx*L!VZ+1NlrSRVzh2+BoH?Z1hdHDJ(ZEgFM8;|1Sy!I24e)@OAO}qK?jiI1@i=|M` zQ_mdimb;e+wx}g)>sL0SjsCe_?t!>Y~wFRDk`o4TwEbP&@ zH*R9OnQwfseUek9|KV|dIoHw!##ceYIB|Nm*eCyVn8&bqFmM#}gVu^9`#JTIF3!qo z_o#C6DnC6fPIV(?T>bF|tEa>05NjH!v0f@0A5Bg5`dpsaO)eQb`@N2spFD~TLFQH% z_h{JfUTtx1%Q-9t(~YW#zxY)|V3r5!@h1ZQXa|1SXRIZov`W$hHXVNFBoTPy+1jDx z#m-C?ta2HW?+sC~xy!C^yfk835;J5jJn)AkXo0|Py*dk!M{rr!A3W&V_ z3-cHL!;8T;@f||n>UI;kXNX!jqry9@y_kEq)ds@Zt>JjSznXHuA(Y4lNBKFf^Zq+F zRdq4aBjxm$0uoQxd_=o;L}j{y*$WnR@3;+^vGWPlFRV~X_yO%rNC%Hj3pU&9)5iu2 zvT!DVeHx$Z==uLsCZX6DzvH5Bn3R48@eQ=JE`YLAH(c{nx}(L}%{+GaA@?R2^dZ|Ge};9q`0pkyPmPMI%#tCxoA)Tc;O@}eMB?foId@x;Mu z^zuV7M`4mQ9LtxN%F}dSy?)ykS+?L!Ui0ir^thLk!=5b)xwHs^|A(dT%E>98>gLmT(?`ZE1W-b?~4@XkoA4pV|UOTT3lAOJ~3K~&N_ zQ&iRZ`Iw^a(9i;|4eIda!8j=ISsjUH=vTWd6pkCu{sEwji4!j?`s=A5oI#HXgwGq;O!+ap><0Gn$x?!zH5dL7L2%9 zy$w!>Ii`ckI}a@0l-r|T%h%qEQ9s+VR2F2tiA-hs6Zw128yj9fd<)tj7`(Tb(50#Z z{Rd($m@7DoK`k~gS4U2M1L0G*y`c>c#J&YzJsQP|Rif)Tc-9TbyGYx1Q)xirQ%)XA zwio~$m9|`!fAx0fa$%dFBYLGj98x|TzOX<^x0}xX*Z`hSZyTK<=ysoPsMkc1@i;NY z2`7(3V`<>DpJtyIgeG@lS8Fo{hOyI97ljvh?XRfg>S=Q}Q;Iw1;-Xvt(`Uc&ubjBu zB@N`X8N+dOOa;(>)}h#`*U}=wIk~*b-oK59yz1kuFKM7Hk7V_U=206CISPkpE-del zH0FJRi*a4EP|N5 z+Jz1J!I&v--LHOW?owZ|hw^F4yO*Ch`})?2V>xzxF93>CcQd3-=BA~&q)o*D9xl{g zi2n)CQ*CH^8=v!=d-|sXJ&`83!NCX%8&+jcL42`Rdwhb6^(%i6=%zq=>xKZn#CDQU zd|+VCte6ukp3{i1`krmt+bqFI`(2-%-|3L|pgem~l&(FVi+Y9W)0x;SLGhP-c*a?qmeyBqc zRr$ok^xJH}NU966eJpJ5)SZySrxPh&8slGdR%jat9d-e??pTX&@O)cZfWP?C^4_+l z+1uOp!=;{_qvDg&j8z7Z-g-&49rtEVBN9j)9f=a#qjy}=AFuv;m4e6lLEopz~Z9T>{p?zp(vt-QPeA8 z^K>?O$6nD_nJKw&WMfdg?I7=+QRYzJyQkeTE98rqF&K>-?OEX1iZF2P7uvi`g!4F0 zA-&EuVY|hpB3|<9OD7`Gr2F~O-|?XfvvV4It^di_oGj~Pgy5V7QJQ+Y!3Z0wc>>!k z@;ob-uhbt%{WQ(Ll4Uh775-uH2w9mH zB1{S+W#8$L*Xs9z3i5{xqO+SYrJJYI>0H*Ssl*bT(N70EEDq8hcDirO9gvsK!-v_8 z#n1k|#Nsc8D$Iq}$@3YKz~!nrUVa)#ivL#E7&-5Ir&IkryjpBEO>1Vc{F@yb) z%5M~>Uo6WssQShW+CJ-UpMo-RNT(8~$2R!3c3f9u$2>6Q*N1(lSzY>NJU0%j{0f%W z5^?5>wh#pX9)8;*0pPXhOU+;SJ{xVXv2OpV%P+rTJ2dr_n^f8)CI>1;vJ3*#ugFs_uHBT{Y5ige?aFa8l_< zn_n?-r97anhUb${siee*kh(2{=Lps|aNC&cBM(d@!)uf&G{(QY(5Crd*AR_c@xcx^ zsM`7mcks%YnW2$q$-!GFISgl-oCo50vtv~HN`%4p$7f|@kAgu1SZr-E4!%|H|A@D4 zPji&Ib{QA-rJZw6qTmr)J<6z8Nohj(Zh;q!&JKC7T5J6zl6(rZI-{WEa#eII?MuJYL8(*}A^!lSt1E zvEsqdG^he6riuEMY;VpGhdSlX<+DLcmV4`-W&P0tvFl0bn-6eyW2_cq!}&cStS*JF zVtZrb&0Kf)CcmFUYK)AZbJ(0Az{tFt(7pARh57CuO8L%)cB~iy<1zihZiIySotTih z^o`xitPJx|PQ7#PSa$Qd3t|6L7h2O2+~yJ*T=kYY)oa~CBP-hu)%vdQU92x zb3Q5~)!bIJbyork!zOn&KcUH^j#Ij~eJ@T73qy^3M$C7y#X^JccKMgm-CTGuN(#vwfkEw;MnxMiCd)Teti)%o>;22wFS z@!Z~m*ya)b7Gm^-<@&4)rS*XM;@nWzBnsR4?*IizA0AM*X^2|EJriE@PMUwi;aR(W zcMZv2tbnKwmTnHM%E$OLw*d74hnqM46=458#%t)c7~4%itb47p{QRr)9o^XAHLL59hDzYok9BG<{>moJ9|6T?pOn)bw-YVI z;FI_JEsc&?XqLTebT{Y#l|!&NX2lkaZrXH+(#bf;^S}Kc|Cel#1tt8Tl^C9(^eXxQ z(@<@XWbIHhF9Wo2VF+{=tw-xGKVj<+ZIo39{SAP@{>%Tv?M0IyYlb{sC8>ZV?vjr| zW$;zGVzBJ@fK?^N1}{FyesQP$+HErSBmzC+iTwuCL8i$I%mQvheuB#)SG*0Z9BwKd zEf-uc`=ayg6(MaU%N7KzSC4~aABweKJ=E;^*7)HqxvpDqC>M2|K`QB2C`WeqKsZZ)f!B#+yUmvf{WnU2XDN3}v`= zb|Ai=97#Qg>2%X`sTJpq5H`>ckK#pRZF^-WQkFOWF1N#!Xkmolka3 z;fP(FJ4Zhs91jHNgK_yQJ`XZ9DbXe1-0Xx@*KYd~Hx^ai#8A5zEfAd)mw~ zv?J6e8~)}cfp$G|joX^RTFt|Y)+TrxCwNp-x(HwPo#r zH}}3Yh3Q&gY`u0*11GA|q_|lL=W-`D=w9cGbG`7x^fpDwK{sBhwy(7U``*U%0n@W=Yy!TDgRv5Eyfz7x-rCP$J8P5<8`NEZ1H|P40}!vH(s62pD$a? z-{t^p#>gJ?p@(Li*>^Krx9L^Ia$2s>gqKWgpy6!)VwRZx)4d1zW$ya#F`ds^B2CZQ zC(-w=UD--Un_q4kfM_4m6Ga+*t2#()&OLwfAd0_|y&sgvS?Q9!a=non0Pw;*ZHo6)f9pK@sb z;C}v#f?|+WWWQDIieGl(lR9W7tbw-M0oRZ3iz`ADC+wnQQi&s)XP%Q)YtW;tN3F%= z8x)>nq zeNGpU%i9)N@QjYQGhlU1*POS&yAb6EcIdyBm@Ce=o~8(J_V?C`ez{&j?uLQ3HhKf9 ztm|lTR)>Q2QTO)gXWssqP3Yjqrw6YvI#ls(B2VSV?++gg;x1N+eC$*2Teu&<=Jhsm z52JH|(BH#A{j<9mWDj^Sn0q2P{>`NmQPz2>kCk^}T?<<_u0nl%Ua>7#B8sMlb5(74 zvYbm}2*V*_cEY&W%eYwhG``qB2e5T{Io#XkDNxZ&q2lo=npANiO1CUBj?!V|XI7%k z{IJ8%Il=*6tK7vxoK1B);A<`|i23k^six(QoBp(kOOd_&$&%%YdFg;SBP(H3yZD!Z z)Z4HspL$UT>iVs@vh&;6)`hVfsbyzIOS!K@gH`SmT1=$)R;XB;kF>GP2M2uzauV7) zYi9vjgo+*z=tp*F2{h}gX4h;a&QzNF@Z0V2+-SZEBCnP|8~2VFvJ3=b*|2WxQ}2lJ zTjyb6*dCsp%^TWXci|M@@B_esqEZ)PtJ=WN58fr{@ey6`#@@VmnB{~mWtDr2Pz|BS zDOPc#xpz0`vkB^uxF0fh+f(?Cs!?H0@=Tv!3ht&>oPL=Xt&i%2)ZeCHOrO^UQ=03O z?>14*A1lAyz9y^8C1}RQC!^Zeo``mP4MA8#eJJO;Y|i(i5T&I-Ae!Gd_vWfBfAU1L zkN$`w|3ZPwi-p&!x}a}wBi5LH{LlZ{cc(+>d5hwK8Za02pBdoY!K(cpZG!eQ@9+1Q z)VxpgPo{AHItWMmgA`B2av`6SZd&(*OrxQx{Sw2r7bjTj6YC9(Z~2OD zkWwR$3{J)>^SETaAlMCJ!SzD3a)UdtsM z*Q6;X83fic7{O`YJ4RjR4Rx3=F~sj}6aPZ~2I`)z(xZM;*4kpgDSUB6^ruc@E~XJD z)NSy*nDns%WW=%0jh_?ocQQpx%Ra|K3%NF^3oQN6e$&r;yV8XH>o@dnV`6uBVE6%D z-S&gH^8du+TWu8dC1MMMN({D`yxI_)?b0q}a<%c&C+m%`a94^u9f*)uE2+D>G1Puz z_j#I%flWBa7iOO@?~8qDyJ1uUJk6WUv&WeDL4UpVsEyw3wTXsedv-%J_!Zt30q2to z>bxFp=-mJ?vYErYTw7W?pt=nZQCqtZ)1iHC4xN+P@C3xi)+ehz{cMrNe%rGjB7u9c zP`B7xcosqNhfOhq){|+mTPzvFN+XGGB}rK*i;5yzc+fWD zG=A?t?6`p$&T`5O-d#NG__HUToXaD!eV}P^(f7tB-NZeuG+kXB>%7GVbK5@S<_(2U zANZ8rY|-@WJlhDg!>z2yeuAfxE%dowJQ|fY&h|lb&FG1ZjpAk8l-A#ap?)f7L2^Lm z7dY6r96USnW466xd8QG)eI&ey>KuovwB?Oi^Nc>}&du3KJ=(@ioi5$Qm%8P}dVe_& zHZk_g<94xD=c%q{<2Hj-)S0ISURKTHyiww)F+p%qr88Hc`7sUD5NG78J0}H!-MLjV z8E-Rdc&)`BWO&jAh(I*FCT&wfvx^4|;A_G`TZ8{-R5wc4Aclq5G{;feCJT}oX0L0B zdD{?vtkMVthxKBndj1(n>HQj-CU;_3~feo zSKFX>Ao%G(z|Dp8q9e&a#E(xwwH>)Wj2GY7fi#50-MZuR#)YcJ!iX&}{rJ!S{EyfEI2_?P z4X9vCe$(n1K%In^Ua;XaA;gZ~8kG`O3?mcml83&YJ{dTAJ{Nw!fEZSTqE8y`6RLL5 z5Da3QEH`&1w6GNUBLOdP^;_uw}{fX;vIo36<2F*^~YI6(SJ9tQq5Am_tYS7C(b5fRcJ}m^!8Mxtr>ybOJTJsf0r6KBzrVP; z6@4J&vuM*__XkjYZIk$v>8}>$H>xF}79b;+{`qs}=M#vHLtlY}z`iO;9DAHpRaw{_ zFaJ%GaT43nuxLB_k@XRLi~Y$EVgoVbu%WL~h+uBmc}`ioYPsbk$Wl%?{uZfCF=&)7 zC|eZ~R-t^`)EZeAdneaMT=3QO;av!xE*Bs-O=mX#K_Q^6Bj|U$+&~)(H_++hR0QZV z_A_4$f7B*r<7oRBkf9~NW5U^(Sjr{N-f4G%1N_7@#f6TD(|^nhd~o9m8!L^&(HA(^K3IR3@ryh-apze-$wh-II2aDXt&=kh_e}qMpm2 zyirC<3p+O2Tz&t`*5OpoA$B9HZxuSqV|*C;{9e?@ve+M{atIq|eOB9jYwqQhGV4z| z2eoZB*w@=uw2h^3XTDvP&%b0m+fF4I%h*Z54yf?YzLVZK{J&-0iGl;St)=1X#L49S z-!`$6-2Xc$wmmacOT`7t0SGR&NL`!xN&9cpb$VXB+)1Z6iJgyIuuanOpdjV_e{-e%`#tL>MHlQRvvQ~AHPaA zUm#w;9{tJWpFhXm=`r5P;j`UvE#BrwcIRvPjYf`$#kxVtR-ts*+&hujtK;ZbCTF0B z#8uaNOAFI!n zle)@c*75q}PL_&)0;Jhq;eiksZHiKpF8-+@-8Q|(hqRcl9u6^Up0$s1h1AH_$6L3m z=ht2M&<=ZVHf)ag>S~$3%_FldnH`PMNXp-Q0gB!K%`fK<0)tc9syPH#imm*R)O)r0{Y48`x_0Ok(^TEO3y~vnvtfm>KKfMPdfN zfW*IuCDU8EwC5>03XJ`$ZZg^2%WVoucFSdWaD4(MAp-qXo_%bM;e4F0wXG_ab5XK- z@$c!^0}pfbM4ONFOq*1G8ZA8Wh!)|r;EZ;UHJo>+cN_Y48n92UkFxk^`aL;Jayw(2 z^^Sgmil6t`MO2XenSX;W8RG6XSD{M|{Mt5vYoA?|z-myGe%<7%^7Qj7Z>4s)j*8KA z+WRDBaZcY|of>K3y0g>h+*(*l$h)HKy8q51>8p7>NwX2%0F6)L!Npt{2tP#GCRpI^g z$bG(bpouRXx5;mFE>2eF$8zj_@dD$CtUg&JOU!t4%GJ~t;S?)>PI~gseDk58$6wpx zBL934xADY(KCk{Y#G*g{vAzy%8*_^e`AdZL-oiDWL;CRSgzWRz{J@Eu%+cv5epOeG zk5$iqX(T*^be{`0OTn(VRb{+y!x+9{3Jd;yOSU|C?wUAy^B+-zRDN|6{WXm4`lIaH z0YI{GT|AtOY62{d2)nT!)`Tx!{8NevkB?ylM0axo3(~<4pKod%9~)db-0$;7<0R~! zU1wEXBqt^~udglkvDs}+9|j?E*>sln;Bmh;{6(XUUk zDXP1|Rh7APHpO#bM;4r`|LncExW1YWaY6229B$?H>9o^R`Nv;xLqhOjmwRbr>B&kQ zHg+E+-z)R^CBM9JF@N*q{DBya6X^sPKGuBb_Q*~Gtk*E-D30_T$6%P!w~Hs{ zZaVoJJu7PS{G>D=8;f{WJ{>AHKXOXx#ml@T#%kYMkW6caK3^Y1yNhepv*|zZ$;3hi zT+;SYH9Rjop1(&U{ranky+{Fvg~a(SI{X&qb;ksry9B}}#2FS5;I(g%mVLitM-Jsy zAy9Pu{4^$jXuZ<^y-sH#-bkF)#@o8G}fin)o+>mCbR{uMJ;yy?4U*DfKMr`Y(CE_q_P^Rr0g@#)Q~mEA6E+TbNd{ zDIciChxp6L(1|CyN&z(vw22{o@Z#*y%$luk@rVEb7r{wHK~!4wD|F(r5}P`{ll(O* ze>7|RN~c%N#mdTS>tig(cU8@k>o83;v6J6GJ$@Ti{yeh!lyZfExc-9kUGbTenunjP z`Q{_9_MY23AB?tuGF8IMPTP?6%}hQ)#>k(OK0B9?Q+~mmj4C&d|A3c0ac(Z2z-040 zU16-zc#_PK`_UR#>%#o{2MHqbgk;Dy@3*cAUSr&X!}xd(Rf}>GWDGi`^RY*Dg+!Z3|#3%_VH?m?LPFzvNlh zJ7L@0Sv@aZzh-NM!~1p0pHHkuKIvf5f*{ZhduF+3+^`e=8p5qw2nBCUtez->p%VU~9RH2J*AZ26&rhb4Of=j0H)2 z+#ad?u(GRugSC6#bc(l&6St6)`o$&(FRs+dM@J*cThHv1D!Ahyv*#YQ)Bl^R7WEiC zi2U)X3v}pvtG9OC$?C~Cte=)}vS%2ghZ+68{ok?TE&{DTSW?9{qV9#3zbHgchK*#4 z24l}YhD?3L5OGDvDEpO*kA~BNsSY1UM8fz8dzYrvk9{AF2T0fLNJd89r>EI^ni8_# z5M*F9fBog}Ij_ZQo*@!r^$ED=qf8m4Kzt;Jvnpf7yPn&IX7bU7m19PahSBJT_t3AS zBRIWvR7&m})Ded5m;J_5GTGX9p=9v)*`A+q_>q`OH3qeNWxNo=F_ zF6&>P`JCO!Jw!gEqub>30n-OWt9$)#QrV`ZXL_&x zCJt=E`*6x1k0^UdyM80|UAcUZ2yBoi1iD=vCn-|64zPoY6n%*u>exWPSi!V4b+Fa^TI<8A@B{;Tp9wdl4X za2$Ua^TWQ_14p#*!%O0RlBy$&t`hxhe5(3){u&si@_@RO}pbF7ELccFk#YZUQ-+M z&0~v&8wR)@XZF2PR>`X#6NXHe`nIQ`0q}e1u*p9&mkGJ z>E+{12Xtyox!>}&H&-7?^yk-YC@M=eM~lZsK*iTk)|lCtFTWp0jzD?+Roza#$fcjL z{cY*MyK+cWX9WG1!lbV9^u0o5wjT8JU8izmF&<(eXyuI)F|;aU_2iu$<*PRi-h}|l z#ow;5<*zqpZ}FW-Mw~ytB8}&Y>v?_bXKDEVVVQ3}yTp<>+UC8$m1na}#_u_Wtl#42 zZzsdjEVy7xX8029%@4jf>jI07*5CRm8>7tg;eOi4I?EM*Z4x0B5??yIDSJx3zV;TvoQ+s|UXSE+K=O@wUgvCG}54ByA}d_8~i^m#_iN6BZS_CZLV`5JD|R+kz6$F#>k?qPa% zKYd&AICP}h6YZP-ws}_m-bmhBgkyV)+N@Qdt43SZY+BtX8ZT^4y?qanp$R)K@5JJ*3tpQ1}UcAcJ_kJpfRC1YbES1#hx7o z`F?W`jb5k+@FwbluR0}XU^bmwSUc*~&E>M8-2@cEf9qd*%EPqHC}sXs zNa58fE9+_h_vy6Y-9yuSvcQ1a$aP)d?6yein+f1_M0Qp?K z@?w@h>f!~ZBP{7AV|W(>2S)&zVDZj{pM5;dNA#J%FXgxVJ%z4Hb;U;uq$b?UABHbH zj?E|E&7rSwxMnZrk_{~vjh2AYduC8xJT1Zh;pES&`;HaJS!vENY#)8n8M$`-RSQg| z-A)ud7*)Mv+0D_6tj_qkc=`IsRlOR1$I+n_!ti$<%QBsN)uDzj?-29ESS2duR?!| ziIFA)@pNbZ*AF`Z$;S`ja7T7%iU}qU&-svuP4wzMxfAB9Gqvkt1Nu0A|LF~$A@j8!wdU{ zOfoHvH(yg?j&4SSg@VmPAANOXyOp~Ivph^x;8T3~)mTqP61cf2rN*Uwi+Y&!*XHRe zjE5Ub=|S@a63McQmq&vydHQej$CR5BCi#E<*-zq0?7EuMiD$om81S(;gQyPQ;atg`Tw@HnW$FTs+1gs>OC;hAyDY(M>Op_b1WfBGXb%TEB5H{Z-Qqhi5AB>ec(bOqJuR@*8Lq%GWg%^HaS#_Nx-X z_8QR)AK$7Ws-p}YWu_~d(e+_}vR_+4tC5|~isNMSKH6OCW6$&ZEmp{tM^k>WrAW!@ zFZ;Rg(+|nfSaWRh#W!E_hl?FVyZUYaC64_d`c6DM4iSOn%{xCjD@%KPXu0wa+kBOm z9ciy#+pqeiKmNAP{^DEIlS}@3WpqUM*E-cFP?U;G)v%nK2|eR<07b6OwN;%#3pjiq z(6!|~FPn{RHX}CyzGnpAeun z0{{7{FgTs~LE@J$Kk)`liH=PgK@{ji_k*>X=-BdD4aCiPq~S5MuARU0R|25KunFjy z+wng;<%J?1a?-O22ex=uYY^nu%^^t=H!ymU>GfceWX zzRl%Mdy4t%pT%}u+x$f{pCyMK!%BN&#`r1QB*3TF@2HY8JJ8c@V?K=P_Zr)514V4j zQ!$>OBa{;Vvze~AMJXJ6wq_1aTD-K+FBN9m;@?6-f3kO3;Jg6GkYCde4LZEDDcS2H za=Eh|GIDXY7$0#TRZh&gZF2_7n`_v01otiLi>jS$&O_-VI}*3c(JC^?aq*4d{*qa} zd8oP*8)H{&=qK~Wp3=+%EJ`0~$jJ3+Bn zEIt#rP>4Bg&mT@^pIrve^s}42rIkBw&av94Bf_du!ahGOSjrc^{KIn>XD8?S>$5~v z{v&5|Q^mOUHcq!_R2Dimz%O25dcQ?Wr*y;j@QS++V)MuG{ocf|{u}vW*(NMmGjYf3 zOZ>AZxqs=Z#`D4G4qoj^6P&Q?jNus2%|S-o`T_k_cQ{*S< ziq1Yv!o#m9rnzHE&M?JD<*$(YBHNt*F1-(i#u^CJ%&@S&-{M0(C%L^8)kX z*_BDO>+!hOi$Q4D?i+pvwvLvz!5rkza1%-Gx^W#bgauc>0r`m$m(yvIXaqD^1P#Z0 z#w7Qy48l--z+P}K;1jg&-6r&Km=ws{fC6j|)&22D^3FVFH*^B6_X54L4cHvSC+?52 z9p6O#p<%1Ytf(4x23xrd?pVcoZU#g0C`Nmo4ieCCT%F#C2Xd_RHu<=M@< zyY(~5_`|#_iHZF2)AQS>gT8&a#l}KnLuuIZ-TE^eo*R3EPCHwMwh!*Uz^3oe!}?_X zF#ut=8IPZuPbDT^n%w$Zy5E63x~FRaA|`5o_6HQ0y`99o`g<&b|Ii*TMECod9r*?u zfAroNZa6?%#EL<<>QCuS_Ixn?)zvy0?HgNdB%#ypYW`2`%qF!Gt^5E}vaCj8MEMhp z!mUo4x0n|+{K?NRHcxkYoWOw*uPjo(4Y^??~x$m zUO!~N?+$@l^$c`GQ>uEhzpgv`tD|o3=w>gvPj}hcCatzRDY?4y^%zrr%hKu4J_8-} zAH0-Dyj#P`z6Gk9bn3eo56>@;|7SyuuJ`}hq?Sz5)CK(f#)7N6OrN#yrr(DF1il>o zc;SZaJdE$3ZE%$?j%sQ=-g8$ zN_%-l_HIM-1)TU_TrURGn{bONADLGdcjxHyDH#Kd_Qu13Q2y{-h3RlKWZh|>6SR1e zd;UG|!T2*b(i^tfo4{Lm`845gj6cKXw_(-}>v2^*_n>_3B|C^5Qc-LA*9v!FN3dye2+T^ie_rmB+ z*01e97S4;>KlDBwqU`39^8s7vYA?Z&`#qoSya z4TKHyGvWOl4VOv3e-3zjI+hCUcXp-|RKl23O(YDDfe{Zd{gO?c&Q{d=799Cu>bN}2 zylJKR<*#N+ywgmaoy#JP} zp`$-y*DqlneMk28N%@RskF&T7Rv2nW_oaXOG7mQ7z1h1#TE)M6X!yysSRAJF83uiK zme%$K$eXBGOfH4e)#o|7+~^J<=tAZV$VqyVDo=vBCy^^-0*weRuRhNuShHGI2 zT*oI?VA$uHV3PU2(`2tYUSlGvvp72b#KTD?vds`=FCJ^dfBlvpA{Fd)@NX<5 z`fMR^R9VLNns`6QUmRaQ&(Def(T!^X(2<^vgO5EKNje`ARhybE2XcJ>e3Vn1sFPt1 z#Rvk?`|=7*>gXtDkZ7Nb>UN=FH1o(B66f-tSoS7PH!>nr27{bu+D}Pp=NdJ zW9PH^H5xwL8WQhq(DP|ne94VY^P#dh*oHGYbFn+A=celMZrtlVx|&B-_V+Z~qimdY z76P+%90`BJ@Xhc)^i?C;PutB#boN?S=<;pYuk#5|v%o2LlVKXNQeW}cuvNu)W^M2> zJ{cw|AU6=4DOY9FwM)er&PBI?-@*a-G-~Ut9EA=x@tXWh;wA}KzBc#JF|Fy(c*X*? z=ht9JAqpE68#J;m5faw;VZJ{LCl^e-fpaW=TcOL_S|?MJ4d(|5YSEBCl=w zK0j*0yoDy+Ntw=hk!?!r*FW^KgRE_otL8BeAHTD9%CXM(WG6+C8yLQwSyiB~eYXgT zllPn>S#;0xXz|ju4bnm;k0;p z21a5Ne^@Bk>?*_-|F0ctcA?fT4)@bS%Gt2-QC*UHua}P&&u1k0n{Y;f673!3Mi-6z zCNpEce%L%9U)#mGc_jMJcmB)9#VFqBPHYsy;a=(K~$prb9TERtz9mG}^ABfbgL!-Yt0)fPyxYNt%{n2SPcFt?wSW5yC%V3@ zfC>$@8_yZRwr2qUlLdpTz-a&dZ5oV2eQjQvIklTBZ{dT7;A=;*%^&#MGmM*L-t7+}|AN8@!u$X`mZk^JqnLzv>bjpy;r4@mkj0QtjIC zRz=sjGH#fUWdg2rdM2$#JnOF{nLD^(bhVL4w;m^JW0b(&>l?P=t6gmA69PRp0|S4G z_1j31bqu;9A=69e%G+gALU44SlyU)e)S!X`+#(xx!kxPVMaIOtG9synSb7VvM=MIfwp?G z?$NB+X1@;q1Fi8j_;{j+{rqJgVGh68-a23Wc)|F)4N-}2jIZA!_$|EJehn1Y8HXIE zPQtOUr?4vJzx2^1g>og04um@1p>`hnY8qc3+3X=m8 diff --git a/documentation/source/installing/images/macScreenMusic21Folder.png b/documentation/source/installing/images/macScreenMusic21Folder.png deleted file mode 100644 index d88434850f32d98d1f6994f4868892f43c918eb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208132 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%0A}j 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

1;09vT!4E%BhhNcbk0_MD;mfMl8NoI^U;!-O#( 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≺C28k^6M2A>b6#_0aZ_(A(J&GYh6zIqV2M;$mO|ClU6|2OwyVz9AougA7 zW~IemI1qSdA7Jx9YP=l77A9qZuX7p?N=L4~ zu$aS^r>-&iY8zYPKyDa|;PBJ`crbp7Y77KNHgjHbWQg9rqhWQ@9AmEx(zt6UGCuZd zdlNYJeV>>$6iv3U69ZkrXi0f3x6h`U_7#1c-Kdqj!Pam1G6zKKjY;|)OLND@oBSMa zpoHK2g)w_RSULrarn>kt;5B``c-Pk(`*7)|gD8EFq-0Z9Vx%5~DSGA6Zc+T`4*+1} z6h7H>`h%W4nw9qq_|w59K-sem*?CC4l&23J52N0sG9MIAx9EI8+{95c4V)n488)u*4a zIZ2D5{&~Fq>08DYjllO7H89c2FW{~DoYgk{%s)v)E15ahI~}ao-w{0N%m8?3YhU+? zMyg?3IWHIdfKzL#{KMG!eHX60{6?M4FSaNwV#!Y^5O_9E$-^drsh@`U#en!I)3$4{ zC40f_yIARu7`QxS-46|jctu*_mC&`5zj#B(pLQ!x*!EP?*dq+e4tJ*AJoaFK%O~|d z9`lXVkQPXB(Ek)^e}y6Tt3fwX>^mO9sMc41eV2pKEr;n=2jiTNAfV6j!t}Tt9Luj9 zxc%UpJGcKacQ1;lXbxtYy3m?hR}%){#~=Ix7cW=pKDN)p5la7otEfT8<6Sf{59L`WGQoAmh`gg9?wXyK@BkP{{VbH3y0 z{w1!DF?bOBUzEtB|FzHQHn}h=nmKm^>I)jjInd$KRFnRHm^87CTowoReF z>KFLy2e+u3Ty8*umyAnRdelgnZ9wZ+C#z&r~V5D zzM&ZG1SjL0;P}tvai=rzYg8=6#A$BWGQP&v(5mYCEdJEqNm%X010bbGAMQLqd=#F^ zOkX82XKzg#5GPvvu6&z5pJANyF)S7~t3-&gg>0Lh02UQ+FsGB~O&a?TZSjCVYl_4C zlw~LD$$N4aq2bDp^Qj!XcwcVH8)X}^CDBc8X^`FWK%Y!(YR%`P1?xE_M|<|m6L2~y zdz<9(yc4+K=qg(%!)MC|zTI7Pzt`YPH+Q9MpyRcZZ!v=J@fWK}+eSGb&9x)xrOlMw zT7Hdyey4!(z!nOr8IwM>a7#)(;mHA$0)4hHJ=y`DZzgE_1U#}(>16Uc}o|I z$YEX|Jn(&%UA_K)rO#>l@<&3X#Xc3)ghiD<#^e^w*^#31o_spWiQk$Z^xa%E&948n zs_S_n+u138auJ>fg zzt4$*AKaD9kJ~zU5lXt%&%xfZtv%U?nw_}5#@0Z1BN*I>`|?$6H~vFE&N0h}%ZRZ^ zKeWL4U7UAA753)1`gB_Zq%i%g_h>%bu2$dcuVZtF@jF_)hmSu2cb$#wC@%2C^S9t+@02LtB|c#*Acw@QvY0@+Pb)#(!*Fg3Pss4l;zR@kBUQI)&{plN>I zsr~WVyOM!mk8beMGc6USFO(_l@b#8h09;AhJHWgje=m6+G(@1BI^a`uq!gx`l6>2|ojH2=hJi z|C|b#PNUcN1jZlT_n#ccv-an|?GMBD*S@u|pMGWzsbL@DY_{(B<_Un8vag2IhDe7xQ1YYoYC>|=8*u+yy3t)k(wrV ziAUyXR^U_&j$jzy;0UH@!#sg8i~bRQcc^o^=t1u(IFtVztLuGXKQ`ap>0z6gJtd`$ zf`w(9kx%#F^LIS9_>oZE35IXFPApvF>G%TOoU&6Be))>2Xa7%`cA@e)Que^wLN%TT z`1~&Uc$Rx^=V`&b;%(oFtHw3-SE_w*@ z0QW$f4k~)q`?tCcjJWO2l&3;q9&3CCc;zDuh|5hHfHz@ill^6-$qb)OzG00^v=()- z{mH>Pc9Ke1#pKElpFU0*wt>uNCZ_ux-n{bk0K{f_idQETBziJV%*%qrEz?|S2xVcKj9M0CV<+zxl2J9-{y!vK0k=L zG4nkscw>qk@rjrF3jEaj`-aWxFEtYjBY$?K@l*+!ZE&Q74mM1?`Zg|t-5gThr-V&5 zPVV5qZ6wBHbvn?C9Ej-#x9UoSXyrL6=q5INNZYtpb6N?mEhhW{pJ7nt%-+&>-8>y~ z^~=xXPr>l8MOPmXUz0g#7e42+ul(AENPC;$*_tOX31xWW!#e=jp0B){S!zEK$8eFU=D_e=L^3^@$%_E`P#*^iSuZB zX*y_J$GqG!9^59aKv;jUSGPVp(cgZd>;5a9Iiro8*!m27CsW5Ee&(NVy>XK;J>IK0 zEN)$iSNNKAW8i7Q6+Kz@;%PNGM0a%9cb}leF>BjaH?sht2ZpMAk?PWO?lARAv; z1?L-I7}+5X>;ayAb=BeZN8YWAU*_Rn>BSr-C+Ip@bn@X1hv_2^_=*VfSmIV^H|W?P z?75G;tQ~)R@U2s(o6kDe$60Y zGU#$Y6Vm1)W>(&vLJYOdO9PR?jFH~oiDIfgg;;WYFk1rBJs^{oi~)D|FsG$(+H9zF zh03KiBnFQInhoNca(VFK!#yY6+Xms4NQ~rI)Kg@q!u7>ipR!ob>(}-t)HJ>VToQrQpR_-tPxvq{V!0pX}TgR(d$14f9(eU8^z-t&NlZ?uAg`k zc&UZ`y$utW$qly6SMo}bo0JmqBUCJ2JQ90%)Jb`IvB%(ovqg_ZIdU|;NGSDB<(pt{ z!o5uiocqGu7QOI2WYPH0IsdyE5j8TZ)OQsF+N7b}de~%C-|jl2o&0VBps4S+kE<_t zT^HLpcU9!T*oDhBZt(3_zPbXc9%3;4GcV94`uy^}ZF>G~41|M%UBYAmm>pfo+h(%* z{%>^GA05k6@w#%hsBiM^`gIxT?6IiPXOj`3;|-9s+Q!lBdom*lzlcPHmoLbHsQX2M z^93E*DW>nbfT9VrzZ;9sEAg~h(q~+f!R~`lqk2F$LB^SrlPi3p-j!g4UrG1Tp$+CI zz{HZC`*_muEE ztN+E8A7GJn`t2h_ARod^%Z6^Gh!FO=*4>w50(ZrE+I(;{y{4ie!=O;OF`SmEgRhA5!%&kXL5r2Z| z9B=&7pT65zByB&H87bLhaL|5({OJPMT(b>zGKrBZ`{yZbUT0%6ZmqGVHn)WTpMF(m znvH3Evz@Ku_xu2tdh9Y^2r2ZfK}Yg)LdSu=^k;$Cy)$Ikx?j!SR0npl@}a{_+Hm zPd{8|em2qjkMz33CpKJ88;9lv8(bepq8EF-lYV|p|93+x^}d*FpAo>}l*c}{TDQXj zJSVsP1MTGh=4wh*ryo{|90C6Qzim(xI@e85UG;1tl4;5`ko4fE%fQv0jAmyzhsTr+ zEC|8fty0uxtB=!8L6ARte{BDXH2MhQQ)iC;voDw#l-sbyf(AP!sP^s3@$;wnFB3au zs}A>4S)(w(xeOit=-@nBKy`OVXfH{ts7pq`ip%kaAHidAlab^+hWNwV9W69;f8$Ye zMG}C|uH*>n09}v9tLNz^IG0?~q#_Qkl`qKupX7j8nF!$Hzt8oGQGJs1U#>5%642e= zPNWfL$8G#Lgf6$SFzNHnz7D$AZdrg-VP1SO)+^U8L|Hxkh{!SNR=PO2~ z2j9+B+Tqjo8xruqOqx=7@Qo8a0XB-|6hHO|SJ~#U?(a17ChZ1tFwWfODOA3*2uueY z^~Ga8t!|;+n59>;w(xb`$1CVFnSEh%QU@4s{P^3VsBY8L<(|ckf63})CTx=40*t5f zYzc+#`_ywC!`)e zfH!^A?qR`(?k(!QEnok}kHM238yo4V>_llb6r)=F`C0khIv}EBfN9cWA*cUF| zh`&TspB5p8pr1^v*@#BN z!mogDqkGN79rufSwCH+1jlzmSbKJjz`4tunZgbw4$&vql2UiWC#qB@c91v;Up52RM z(bzn4^RR%Lvu0TX^2_=QA$amrKy5$2SH-4F+U+yv%`^KcMb-v4AKbSp>c^z#HlfjL zTU*3Txqb|pm1HLM{QB=|eQ2JMh#XQe?;HntoqdmH(4idOT=e8H5sjXmbLTsA4V>Sa zQ2FP6KJw<*eRKgZysa%K@R$U~Xx~?=Vsx*g3pM{R%Vy*HyDonDiP<-|MuT@9ix9gI zeoAtQIF-wdA1f({ul${Ev+xXWB zcb^@Rtksbj^!HXf*zuUp(1*aM%aZYBH=pX1Y_2;8+1N$+?PZG1uA*0FWXufEK8E+E}t$l8ExKAlq7mKrJ zi)DDT1kZ@XgkAjg{X-ADJpasjKF7HQm!N!OQuhxz?x(iU!}Z$rQ$z#9%-3T=X7Tv; z^7aLrOr1%%Y1jzQc{kx>@JdP=#5-L@8{d;go+qwPN3%qoa%HDEO*+H<*@Rkrc%}YC ziUGaAsLMo?u8G3f7_+#E=}u_|E5@5JO+0iX?TSy;NFTX_p9#eM$K(YtJ?WpD2v?_n zxQlauJ%G|-oU6NzXSlmTMgz0?M^-5F%H*DZ@Yzo?)#rpraq)Z0HvXY*?h5VZmlUHL z9AlCGDYv8vn&BI5`_{ zQ1Ka!xhOv5eEzO}?D&B^*kqjw^miIENQmqj47~cVdjWf-M_U`nMsD*w{O~R}STf2m z4z|h~N2hn=Wq6m*q9o6Zfa&pw<`N2c*09A-SF3C?zkRmk9TH)*b-smoLT zDs(UhZ3NU|k+htE!5j_Tf^N?6TnVP?T^T2lQj9*g$&FiMOg`|(Cs)jEww3*|OFiVV zb%mTWZG1i;zfN^S9GiJuht=;vj+xNt#>TZtNWG~=59AG4w3%B`;p~>w5&vmg$Gg`U-&+*j zIiwLrKL2W`KKyvu%PxpUM#WELEJ>q(CME;l!!spieeAo&=YGn89Mpoyxhb5(=;t9Y zf-ivE@dqP=vq!}N^fgGLxrvfH&b~wX5`AtOXgp`z!KKKw9Gzc`EUez{}b&lmJKQHN7MmHl?HJ5S}k@wV6x z(92|Z<(Gvw)Wc4v82nZa^8MbTlh?uI&MzIoF0aMNpKC6Ko7lyLllXiL*{fr_nP*cS z^|oMjtxq1&alBmh248z}L_9s&lU>t2raLi!_he1S>UT4T{&d=xLtX=?Wl)tySvXJg z64IP-@uV{wT#HS8KcCMI&S!gDth$(=o{uJ6@0k3#leWt{F*7f)Ytk5b4TqfXUEsPaT_;cLWpE`KIFyslR+PPLjpfBN;ro&tTk2en zu1<6?1yT6l`ta|)9dkC>QEw=sJ?yzmE^aQs_VEzU8QiPGoE3U!FqXuK>wFs=%5v9y zSM1J@`P~?b#=B|!q3mS4>kfU&H5qAxIR9l2h&ud@lUniyIX{>$_Pv|h%co!)v&VkA z@W;zkWak!oC2xQSyIi>WW%|&D_=jXKK%H~>I;J-m+Q|)j{tHMJ9nnu`wb@p_B>y;k z+`FddJjsXhq{8mmuECDy|L>O>!6Tl$Tmhk)XYYhJNV8G_6J)4w%`=b>qptXNK(I0$o?^h>#-v#QrJyVy> zUl}3pKKe3|Iz(KrkC;;JfYpxGucUNYY zzzb~GQ2*br{ZQ<3vY?Xd2fz)sC!vKE4;-=c;Ah;E;Z|8apkV71_-PDLE${0$vmGc{ zb?9|hhxe5*K0XmG2t!1P;P-7t-dtb)lt}P4A>J?_+OYGW4Vs|GV{8*f3%*?fSD)Ts z3EM&$t)w^~rd-Ug9c9h?RRliMru^YnN#9_pdUo<iHUxRijcGsaT9)c z#JkFVnng^&GQ!(D_>l#QPT!p>#`DF4!3ObToVjExZj|dnDFRnYO)~Vcawzy!WW0<%1)zj^cf;t2h7fm199rh4Fo|R zZBn{!vkC+r=$qhv*jF=F@9x0S?cX8aDPefE`C!z$L{l3*1vkGXbK@amdEqmO zQ$M@+@u*>bIYtX59_g{Xi5}h8W3=q1sQz3qo2x#Yvt`?qU?fF8oo;NM4kv>0{VlGf z{^jdQ?w@~L`{?F{$vOGh#RyUOU^iC`W%52eTtx7fopfoP z?%s`<4m=VM+&q1ennvcA`9C>$KWVV5gMko#SGuV03sD7>?RvictD9p^4zlIR!&&?F z8B5PydgC!m2Q(IR&OS^@kwD&=kZN zpg#OMGaPSU6l61|TN8q^VgI2uMrZR~{UaWaf0x_e0UK)LFb10k#cep`O!B)-aN`0z ugON<`nv~|RE;=-O?gsPu;+u2$<^KRg4UQlW(&M)P0000u9!7Chm%f=@rDw!|n8~)#CNXC&}A|oStK3(X4 z0QFImPYXY8;xx9d-BbcBEQ+3*?$6|ltXJodRWTKrSR1ur?KkC<^{r&Sb^?l?K^x=eH{QKjbt-1 zPxIal)&{=l?;ujSos>erBk z{uEFCk{;X00lLavsJ!#>o_m%7pJiQ61J~-FXRSI61{p;Bq+5IA9RT~F<2K-=kPfuj zr#)Ps`Wznyo8-DoSjC$Qiw-v#>;?%#tJ_i@M#DUOLJn48Z__cVpUiNg_ zx$`~-{kzG_yH%ie%|lN6)?u=3BN%=k+tvhIUpxw0cNYfg2%Kw*EDGN>a6lGcj9%c^ z49`uzR{)hqw!y2oy-zW^!i(8XP(wS-$4xXSG~1~P+k?1e6iO`ct!cIN7@mdm zvDLUQPzdP$`6v-!h9+S4qt{Gig`EkGiNsEPa zK0V(WOm_g+&g$pMUIBMFnUwzX^ZD6fu6z}U?Yz(v|ERE_vbFZOAX)sXFOSW@S6BD|#4&Vgk8rU)LN z#{80A2;}HqcslSQ+}GP(|MMYkXbm!9K3t`&V+XBt-MkBi{}Q?H2i8@XEjj>n8`o-K zjg=l}W5s4a+GnaxakIsS0@p9IRKS{9&-cJQ z)82l|$U$Lc*s_BIA#>P1a_V@RylpFL22mcciFrhO~6{uy7Z)M_l+r>%%j?V@P zbT;StT3!EqSa4GV_AP?Y5{a>3Y$W6c~E{>6%lGA^Q-cO?7^V)6#zuV`b{8J z7thG#8Z6@M*#@D0cjj5wuK8oXkQ*GA%06AbHRsgN0|}e3g>4Xa{1rN;LTh{@E3(<_ zVAt7EzqH5H#bRv@zD~<=xN>m;6i3%aaP2qRZMNLn3)HhPrCcjaC9O>CqF-W7FmAfd zJW))=c=q4{o9Y{fGSEW51&-wpl6sZrV4aq;|6%KhaARM)m$zV zv)l71$p7_G_MrMrxl>qo$@2yIt&O|iU9Yad;R-zY?8&~B#&&~g{muk|c5fL)TE@ujL<25!X_w z!Dl?okMDNnUp$@{e>NU&`Po?hyxH(x2NCiE{!q-w9ApsM`5+1?1Rta<8y%_+P!{(D z%sYu5O=+L3*5Q9RM1XjZq7xmqWkO#DfB{w_UTS}T*@f-ka{NT?ICsa{H92M@AXfNd z40wT&q3`Fn7N_-lOBp5<9pHzF~@DU6t{SO-Mndo)3JTgg@R+(*gOu< zidzXk%P=dpJsGfDu1&@}zrjn}d$|pR{V^F>n1!jn`OG)q;db8}5^Q4Y;JKE6$E>Nh#B8U6=)G<5 zyN?{Kn{AluxVmqwziqE-pa#9n&3GHDB6<+(>z91qX)_^be)`_0ljLg?LWkkt!($3t z7XIxMc+zQJ;NV;tlqF+3$x@A;xR8rX&=zr`SINnP;2it(OkbU?&sRd zPJQolCmaBU-!=lWoqbgcTz_(yh3l#Ezdl5L83TKZ?CmT}_*i?N6&oyp;NefX9_EOt z<)Q1TStylv>+ki2=UdB&UU)88A5qhJjYCJVk_|7e$a+jYvk|#!j|J1^+>rnd2`Frf zMK*yjptt^#vL_r3dM0wZ0j5a(CE^6X>Z0)tC%ecIN+SDP1+LRhT0M!)o?QrC!_xQz zgR93F}F9I2~?mzfK`&oR4E|s}qvz-4UfAusm%e77VYtq+8Svmz< z!xzCuCDYyYs1hESw_BcL!Ac2)ldH2%O)mvse z$|DjrH4$UUn4=FL>A3VNb@5;R6>h)zR0#24-l~+}e2niD6-;dM<)2i81>rPS4`+mI zqIEn}0(%m37L@sJzIit8K^*Qnv|6WLM_M;(GbG>&f zy54S)|NfkO=%!J)UTFLGWxp>7CI=f<0eQ3v-GVC9%g>wcsc?1@Ty2Y=#_1pyPo9S_ zR+i3}PAhn4i`3O>IdogSM_{hNgXjOLD;sY%caN@cf$h#LaXCeDOArV(1 z=jpPsD*vp~>P#po#8jXX@EA!5M!Tb2NB5h1K57brNELTh`tH?c3~)!_yS4rk?Ezur zGuT4cQxD|MR)}cLOidlbGQ$Sq^}tgQHSBr{MUHrKra<;jdjos+x~;cNy|-Zpwbj+S(Cm4d z+d49jW3nB-$1o`Mi@060$-YzZCAKV2$rQ}>?g}cic3uG1T=!hr-a9$n$QfUM5&16i zcNL$l5jUgg9&0ypv+B*Y8$JCavXkSk#n~5Iz*P~&g&-ucdMURcm0|Yj|*+34-#ti+@e_H>? zu!gR`BCIYsh?Qc%N|RGqf4++5`iTXPtv9S;aR(&JWdBr{Y5-|dxqOA=(G|TPtcS~V zxt$mGjS1Y4INqBN`EV;7jDAQCBbO0O2!LzA_G};8iS=t6TolI`38Y6p1@;&Ss?;Qh z6j?vk_4mUvelP7q7SPYcB26jvXEn&mUc0ICy@B_0#42{N-uKc-*mj*) zTdGiM79T?aSeBlh9ryn?!A7dVU^9aY; z2mM`r7V?{in66D>udRA3n{&GU)%u+|_qb*Y^%FiJ$jo&!hBry*qV+rqi$Jqg=B;xe zOQ(KyIMo`@YheX(=rv6-v7t9#o?xUUV_DJHbkepFF?z?3`|SFvUhnbr!|Dqg|do zpPo6dZ|k2rr`A_I#HPaer^)E9n?hJF7HLxsxwrn^>`EETSM|tjNRrZ z@aV4-wY$o$AeaXGOvBj{{d)@duR|bMRaO}4s@#554%Ti>?3iZ0$xA&witN$WO+n}?PiMHRb@M+b-+)8{0*pDsoMR~;g&X9D^GAe1`q zl?(EdLuZa&G~sDTVl1FBpM~+nB^B;^^nCsKlwbJV6K2`$R%2o%lSTVvF%v5ap+Eii zjY;fhvFYsAUzX46$FJ*Z%pSKyoN@|efrkcB7dRaT_`!7rV#q`V^_m*Q!eUQww?Yu)+*25botzdX8_Glsp z9I$(p7E9gRRliN#&0A>ce&^p+(^!xk`T>~}oeSY{kJ#1UYE0iilvZY!m{A9YL!}*U z8(r#kcEAOgxq7GqD;z$t*j^40IPiwmFS5N#6uwwDn0|gS^Z5i9W;Az+`fJ6r%bKiGEhAs4G^};}A<}MJPWU6BAAb76{G0%Ct0?r&yU_}@&W|@<; zfR45)0Jpzebz8CMBt7peWCnQ3>#H5S4;1_gvq0yrN)0L|8pX z_885^3%msWn8pJxez^<)AZ|OSCth$fqGI`?5VnMntW$R)*QQYD=_0wuP)B)wDum^6 zr0V_mDUtI7;7|v%nf>n}&F(ed7a$Sl+$~;<4273yXY@8VEG|FbQFlrBQ5cr8_6@g> z%&nFilyOb%|K8DjdqwA1p-kD1U~bxh!^92|!9>WpX^kyeF)Wp)P*g!mzL$k+LUvj`8unaLv;@&7T^p zgZih3#{Y4io_%$VCRYE&6Abc|Fpt)-e?{+{1v0AgTmc8Gw6$iz6Wq}X8;To*_6MSO z)EiPtZ3=zryzOLradnB*9S||qcz5mm6;n$e28gStfDXK0IB7i^wMQl1w>BC+tZFu* zh&Ycp{?9jy%2XN?GQtQ4I#wjnLQmDj~(@R zKGe|Xyb$KEO+b7_^O9x1i45i>9(;o9<+R*4h%6es47-ChGtYkmXH?1AJ{(OK|7#^z z-Rz$ok|Lf18B^)r^6>Tw+_Y)G*k7%0Hzm@kZ=&%wVf>jhh#60Z9 zdm(QnU2HI`=zmzphD>$WyQ&4^{F_++4%?(r=GOA|LnViit?3tYfKId3wecsz+3m%U z{Bsr0UxZWk1)LmEAhf!p*)D6nr5P}a$1>Vx;emer;ARWm8}*AD{q54`?To|x0kA6; z9#WVFj?u&!cWUqjku;r5MRB&;H-bU-O1kOm3)~JalpWtr-lekhl<(uxr&{tj0G(UD zDQ2XZ76Zwc6aXZ*SR*FDRaC3q`t9**f89?4ZFrE3yVclI`Z73hyTzL^G=Md+9!8=ovuz@wr5h1e%(zN4DFx= zy4JX{#(TtjNYtXI4(1Y^tnUD9DxqAt9CZIPqK#`gGAP>HECA4I63cIqP*0l1B>>sa*bXQrk>t)2@cR~yv zKtzDh6Y)m-$4_WYGEKLUw(#Q#SMCNE(Aj=-=Z!jS)bHUA9Cv}`d#ke!wijEUF!lQ1 zgsigajl~ibsl?t*f4hzc;V(iboLMxHyKX_sZOpE!rY#KAg9>d{FFq(?M61Um`5EL& zco=#qzL&utJ$f|42Ar_BNdxXo3guV&r|oWWx;wO%m!0;J5f^i!T!BVyI#~dOqKkPp zCYC@|5UH~vT6`tOAHe-X+?ZRk)4>fMzQDm0MW^G{k%)8)b4SL~E?q;FVPqNYW$bFd zez${-9|GDrW$^e!*r?KRwFn*WVtbP8->>Y;Uie?&L#6GF#!~<>7d-Sf2uGMVviE92 zfF-pgc7w*DLv`}HdD6G?{-ts?KrrE@YfGVLH6XL-9}CO^V*=u*T;%{+L`+Opln!fjVAbb2iJvyXjPWidA)~4_=I!h+5h8mhzMhjW%@ne^FTns z0x{1Bu}Rlex9hB!q6=iFs|?@vQP<59`u|EkEh)^g`i7`Z+va|Aj?_|z`T!6sW22^BJ?o@95*T>daMCNekJKDNtTUC5< z0hm-s5}vhmRTYSwyASB1*SSEuhzFCa^XM zy=77?V>@+>C`O)El6SaHgWOO$bV|7<;CDmw-4E|$Xd+V=nG|gIzW_EpzJD~v=;6_#zp!$UD75?wH!$v>JwzK zQ99X6dC+=Eg=4-@yh-+^oQT*e-wMBM0?q0pun14Tyx84eY-WmHr$f)1yZWdiIyLeA zQQ?koI{S5H`mP~K@W+mfuX$#QyJF;`{S`z9*QHu1iyB71%&sT%S-XQQi~wrXuwL5& zi(Kh$-fPntgZtayo-6gDwfF6osZ*7rrC`9%w^}BQV~n9lgZ2Tqfi`Z8`XN6izF(qU z9wjw3I8gTedxIhPhAEvevl${K zIv?ht=kWaBSpaB8FWsHlXeZK7Hj`m@{E1L5Y?(*t$o5ph)vR?M6B zWQk4fl!5Twx5`-_F`9CySv`MwQHQCLd=3<)9|PXIeJNAiat&hWw02Y!gBrchcuY4G zFQQ5TpL6I2V-(^=kOfv<3(29Of@!$7>s99+!>@@bHZjhQp)}2Yc=bcyk zi5ZWb@-Wq?Pw7S*!Jve=lFVL`pm2La*8VzjJh(Hu@+zXUfIFYHhB`#;9r-5Fiw*Sr zn36OBS$MdYXXSXQtXEz4_Yaokv?TA;(!f6m0I_Vn&;6mrS64HYC|z75V)Nji-ODQx zR-MN2$@L@3Q^(0Y3ie$JLmi*^vkXK75?$vwO>wc!wwJsP-*F5Qsu4v>?9D0BqM(1x zZ+d=64lJecPDV@ou<39!f9cSz;TupUr-H&J)l~Ie(lAz7i z;;1g`*$1*xHO-9VLs7o$ZfsjA+8JpG{t!)OF0VhRLdKmDV(3GVP9vc`S@XSFlvT`h z$k<>=Yw$w)p$CE_*tu&N-&(G&8$8D~{}F94fe=fImj28LU>}N>jQ29cXg6IKg2RjM zYm>H23!bY?CUyCxy4>>BRz)ObMBpcW7*E^@cz+ELQ=A=UDDC()?`0Ro^K*HSXk2M( z6HdQJQj^~&_g9m=Lt%J3WCvLa`g&M2_^z+9=jHw!yrS!QbLFEx*Vr{=$RFXYVZX(U z^PVkjh%E&*A&r&aiAKe^PuG;hi2Vm}oq^DlSiLJe)xl^Z!z))JQy!N0&g^(s5cPXQ zDkp^QsL%MQu!iFwfe1*=a9;AHX5s4TBbY0D8Toja+piE)QuML8pc=QD6Z7PC^U=F^ zzSR0RT*ExiL7@+l14CtYAlWw<<xS}OLvf2+vr z=5M}yqtbx6V=_@7@~6dr6*T~5eHdDx#XNyYtv;=OU%^qy6;Nxz_E{${MR3zn{r8S$ zw@l_+o5*agLc?IzU1iG}_h%i>V^g#m${K%$V;jtA(w)RGD_(ijWi=I2LaKpbM(0%6 zuLwXB18_!=Imu$i`Y+i`gu@dxlWqS6(Ob|?Mveh}`NP*R!(CMZKoN5!(HPMu%%f?- zlg5W`Y+DKrxmJ@58>^3E#BLouRNr$yu+shAB$6aXt8GKYj+(8avQxzGj#*bAw*5uP z1xX7G3On!43zf48-)`b2aKD->3w!=yFEt#2eg_fz0-(<0?|xG$!0Z(`$iVHr&c$LE zZTly(*qmBgT@e)d_q{&Dn`<1tkLv{v1y!@-qa4o#%1+6;V##v+AQ`4FmMu1%Oo;I1T|F@CnYk>-?kffPSyr0y$^y>OIod;8!QEu%7 zPhJKD@xzKLGl{J-onVE*7yaF)azN%1UGxI62r0*hI8{`0C4IEHj$Tk8Fd5@**O(O9 zf^hL_;=qp-DaSCI?M{=|nc0M*O97u=`||k2|1vK7sPXtwNUR}OQjCPvW2mVwDl!nZI{+onZy)NUgNhjM;M7X|_W+()Mf7UIEb|ei+3)~Wsm+`|DK39F zRG}0jK`G&Y@{Rg$&S-8yK zNy4X#KbZe%1=X(da%$vjf|3GXmt&qrh0bhhEmDj5V z*bILDF8Gtie_r+lA*s_y!)X;)QAspxH>A~^Uo&HrtaQ|jJ6U7wDGY-XOKOt#tCY+1 z;$!_{7E2pVH~ITeBfsG87a9XT+<9TNNHpHfzD(m4lj8w#K;QO03%V+oJj^1ow1e6G$j`wAj|d9Fo41kIUjDI zA3;&_5lD7T>Nhu><$>@E!T)Gl9Egc+&QnfGH_+2E(E2wqyX|qr!26pG!Jm4$UT#&U zgKBzWb3?6OL==J!jK=MVyJhRdXrM~rwM2H0f=h;By`;r^1xjNVm^45uX z$DA87X_1q3aZJRbPD@&{ARS|Qi1M{CTiYkHr-`Q#2$ayxfFO0_Xw!0!(2s?8i`jJtnZ&Domb5q!pSTJC1xchSMBIj@(}XR(22NS3T5wUrobe7vFm(AHE0QA z*@)2G%JnHcZ@eCD_GOctNV8a9tY1?TPVgd}x{Z0ivw%p);sbX-pe?r}G1G}c;_iNc zsj3o7TClTi8!&?#*j~nc_$<2XOhcNu<5H@9u2?X}CFnY8a=vpv-^i@4{c!qxbwXRh z&}XNqaUa>qZk}++$(aR?6t0Cx)p{xV*{I`EVdUpH*@VbIPrN&>4I0X4Eoo~7zKjyH z#;LZE;Yyz5V}HgRpzm;G%UxOK69qoYug8?E>gi!H=%*d?6Rus3Y{h#mIL$L_ZtTZ9 z0&b7Od0-_sjEmUmAAiM2xC|TR48(r>#smtPKAYCRFXxJxEA0KEA@!jplRCHL{_nO~ zUX}2X{A@09#`kLAwfgipUBLDiROpgO&1*llFLdR03M?^1a=v(FoE{32K*Kl6M}ap# zgQHgJgDmU$im*%A(Erwm7odr9cyt{mvVp?P2rRVFh?S0r_ltIL@W1u4IjJzSWzf3* z1iB&hdC~2-1tzMAfokIf{VtwbWNk|eE*I~B(~NO7CVu{2j#%9hDqYhSD4H!2meZ!6 zj4=w#{(#U2`4YC%?Sta}bqSa~L;}H1f^5-v?;_g~i+57czWo433gx5E{!z1U#UNx$ zvNbu6CLfOPMwo?VLoY=?no6asNzkyWg~!BlaR1!bN^K^p|J4>SDfh{FtSK(nX82xk zBHo`%$QtPxL)SBTNj6_cC^JJS&_vjjDND}4Kx6!l%%`TWn3AzpT^rbk7o^%|=c_3r zH1Mb)kf_gWj!f4P8unLU?=Bv^J0tt^Ir7nvrb%4f7((imT=Z5M zcc4kr-f0(4U|&|K9&qzoUD(#bCW|V{wB&L=iW29z8r^Y*U41M~rG9W`pu>9T+g`#k zp=R4o2pJv4UwRhn7HL&w)jRjKBOPDq9P1DJZ&DY(*G6U9L?5MLNKkyZW~kEHF`Sfp zJKT`miv9t`h)njKRQ0sy%va+I<6I|7osv+h1vMi&)i=O%&8DwYLEk*T5A@}s&-39m z0qLPy_@+8ZDLvTT9G=9vGXtLU;&tfMrIpeX*toEx#DE+h0=^`ydS3d6OGAMI+Hz@G zU7MdOiCtD-JIzdGneexPY5gug1c_rSDCt$2vE^uRDDvrV*ofR-H`MRS_e#bLCF!8? zntzAxss&W*#ek;DLp**@Yaz#M#916F&jx2clAa74Sae=92rS%?DW@i2t=jdZv%ajsN*$QgviMt1uSB*P7_jmbwLa5a zfI`yKGXWi9NQ?&hxP0;!lBS6#2 z{Hc-ATBPdBQshU9uA@PVgub#i7=-F6QP3Vc#)%RD9T&(>=PZTCwWuFijWi9^|l z4IpVR89@!dna}h`7sRhA0jPa!`L7xEs7jG(M0?);G}Bq70*TT*O7_o z5Vh#`2W9btE;`d@B;zl$%9xS>lSsMAVnbXJH#f1M>FNw7 z>w|aZWpoAP`R;bV^v2>NFArA=Re{kPpN-CTJ4A4hdfiXH@(s2%(+EXy6 zy{y9av@Twy_(9%3_EFs*(6j7DtrViE=Mh`qMP&9sMMuQ3GW*aU2Qn=)YOS|EF$_h$ zey(nJ9sS4vLCcgeR^mKPyjJ;D^KbC(&M=k5C>V(I;T^EWG*N}C+dTuc8G)B zrhl}DAWu&SoMUBT06lWwDqW+yB6ay=2_3t>s*aa#rSjf6iWy!eZL3eDuih9Jo!uBQ zDpg)}!gOrt_qaXLYTscyP>_%xQH4wwi8R=*D5LTLm5;)gFlr@c&Eg7l$Z>P#++;_o zL$KeNr<3?t0e=EN@X!{#V)<)Co~)Z;>pg3A^^PS~HkX**pRG`c-Iy*|QyA^I2eaYM zmVJ`}kCoZv4<_e;a@6I(Wczr`_Hst5W81I}Vo?&4h3LSLFRDItN`oKGe?*)8P|6h- z&5PrmG^@(mXbkqo!*oa#oefZ0)h>|5IrdA|h}0Ug;EpzCQXtzWjR(_|`I;visz}50 zoma{w;&U9z4U5v)$fjNO4jFbzKc#o%&^LauKuTGqQ&sz|34Ep;b1#Gyo=P$=0wuq) zs7y(9!JQ2MJ~mXL`1n83RGnaZf!L=Cr3HS*Ql=qgy$ir|iGO9Ag5GU3(UeS3B8!s8 zdwus8z4X)?CB@6r?BvJ#UnC9U(bFS~HM4adYnAOb+C_8!cXKf)#Fsl#o2-Zvas^a)6P;n?ts}HUXE+>}g$R!Vc zd-kN5mTU4`!yo$}1E1r~gq#(iWs(q4EjMDjHu;U7E^tGSsD#J1^5K^sbi7o7)m>dZ zvU7f>Y}{raI|&|pN(QWcaNCa9n~m5f(42W}M(CZ{*W_%2L0CBcYPO2!5Wih!6#{Q` z;CB9Xyo@8|_bco+vxUSOYLSL(+`O&mAtt;T_RPo*+QGNx@`5@kZzZD<$@KMN3o$yX z%;Z@jf+Lnoyd3Jo8k+XS$7 zQx}?ci*|5JJs=i-amA#9%a^}BNr=hN=6?tfJEsqavt_@YBQ;0il_(zIvO^0b`2pH{ z{Wkj{{@gGRM_1!t2;@;8#>oJ!s>w2i^YniHLKLDrOf8m?FwYgRs8s#7#^aNnWs9ng z9mD<<->|4kLgpNt!#$0S@1E4nfi0xXu$nAMT3EXCJI`?-$}}2Nf6*}p2U-*Ia?M+d zR8Ym_In4KPo-b6$G_0I8))g-z^=Xvc9EC5EYw<2;A|a3*iJ6>69oG}J+y%CB*1i_j5?!lMh7wnZP$% z@piD7tq)nBRr-`$$<(riw)p;VKJIgUG0ZR_eLrsdJ|x4H>d4LcIl11|`&*qjb043$ zYT7T1yD8SmP9U>qA4=zW31Ld@AZPqp!DpFyjn+-$5#$fX4O!u~DF>jePz_OUJ8RwN zZ+I&vpF7_n&y9Pe;HP0p7JkbN+<7P9O6C|kncUve47%0`#^69(mi_oFP9Gunmrqyz zLlZArv$adASoKNyN5_fRAL!X3w3&utGV);uHD+PqWcN-;jT~w08!lso%BFF#UK#bV zCSPngWg~t(K)B^x;;AHe(-^0pP)pc={_mujwD5wEc&F|w zhU4<#^zre(vsSy39QNac=oD)5w%^dDZlpZrqMR_2aS0@R6NP@>Jt1Oc4I`kYo45JU z(Lj0nqoVCAk0#j{lz@lQDsI)uD6E7%TXA*}p4=)whKk-03ZDzR;&Kvb_{TG@vtYbE zL3$DzU=WW^u8&HXwZ`F=CY-rk$a;tf z8w-#e5~oZZ-Otp#fjCB!Pxz+2!S|rJYBH5)MaWKot3{fGvCAA5N&8&r^%G_!?(vsM_Tj);&YsW# zrg|tUt)908M{N$n_`kDe_r^SEvs{S#wbszIXJ7gggCngXUBO#pO zmMnC5O#k?!%{)>Im9}D3#LXF%^lg0e>?}hFCVpI$(%=(UUl=&Xkc!%H-%Vxbb5<9| z%qaV~vKp^#{>-1(0n7fevQxt=6)RIighVk%pH*&tVjm|_)9De&4TRXW7b<<@agr_9P}u$;->w2r=pMf;Um7t7c=x-LD&j&!-6b5$AJma`4!zpqHjW) z70si4G5FYzvY4nv!KgD3vtNmEJ;@b4O}wgUo54ilqDP#7sK2vzl%)qcoQrJQh!lw z=igUR{F}3Uq8zYrQ>Ba4*cNLa^SPY(F!^n%rpyau)3Dnyn=dyXrCCw<1o&M4z=*na zzdA3JznD@w^0oZ_vG9(a%x|iUgD;|+r0txp3Ma`ASY)Wi$=g$Jr;o8g2+JM+@FS~F zlMvJEGbW)&SwQbkED715&j!eHf$(rm+qr2vlGW}BqvqT0VrHQ~s<;{rEL5B8Y2df%bWsjfVc3U!bysvb4CG?Unj~Jab(?x0_Ut zl-jlVfa9XYFx%(LMM{z7=wJ(2la-pI84<;BDx<5jspo!0K6N^+7?6&e&ceF&{b)>3 zf%AOPPAog}?|Qt~Nu?)J*}oVGtkvkC;BA#BA-SY^%Y=s|zK8IK@MBK%m-hr3B?X`Z z}>by=XMbCA7`c`gEzj ztYy*_>}s~HUCrij(9tt6O|X2URT84fksnCEOfpz#K8&--{8I_Xi6{%Zf_O~PMe-YQ zXrdF*+xUdpcnl2!)Ao%SoCgLz3osC?3@!TPk+0?F0 zkxEVlx_e2beoqzn_LV_>A*|8oPxH+5?>_{!1?ys({%arqe2hv=w&rZ@nzcW~`x%EL zv&}^>K8>wtuqmOft58vf>qD3r6jU<9xuyiic{)iKBb5+x!3dd&q&BVRzl)DC+}AB6 zGJL`%kKxisZ7sYp=^yC1-cYx=*N95>Wy}b^k&muk`z9W4CdLpmjQ4SW>|{Yz!vc}& zVZen>dTA;6r=4I<{_HcJr+);A|80VAf)np+oXpo$Q6H|egdLTyzNlo?PNPX3x5)2& zqZyD4TU&W>Yjry0ou$GzmqbsW?P@;z5a62dD3pO~u+*aBy_)-@3gi}r=DB8$g=>!^JHcfVHjS9XXq`s3 zH0JLOZFs9#40=L8of%H27cwj$X=!zj@&n0989p>h4%^1o;`ZaqMf4UeCW{ncf8#}W zygb_Y%vd@qK%WpX{-uw{2ld^Lsuj@HYtml~jjY5=RzoHuQ}0*dYcBn4*2kV|7q6JH z^YDFVM`D}aB`la55lH`IpW)AOh7(%eo>MUqU5lDW8$7^d>6Zb}dv^P6Jg-d5M}K3*lxqG&89J*~UsH1_CXc^6_GzE@+j zBc@KTj84x2vgvH6-*|cXeL0lp?kVN2{Y$D~y=qwnEbvuU;pbLWlOW5^`f<4>|4UO#92OKsRCEmg zT%ME6rqGlY+Po>RgEVKkQErp3k+0A9^fu_<(epl3b*b)~Ocv-X5_{Vf8{~*GIe+@5 zEYPeN{v&#(EgNvW#?$fK9#4J)zixxlJ-RXdSJdd=vAM}s-!JH=r)}JM9 zX!8BxfNiPf`mpV68A$6`Pg77myPTmjUvqE?cOp=miaAbacm;40amx5gpr#r5_G1=R z-qU9Sub4MQZkIC{m+_gP;Sze?%Ok5C^817i2xr;_`8`s2$;NBptYEF=S4iYZHh0rH z5ni~~Ve;$#{;Vx}u1?OJy*z;D>m8GC5&RM}HT@U$TgmJrU)}dc0)rKAo6j%BK!a2W zDH_upmYbO?a2);~V;(A_SSp$F=p)uDt_no;$Kz!_KoMVLh6l(uvv<_%B&#bY??X8G zXU+l-Hn&X2)mXzikPQ=E#_DG~+nN%i!LdNz2M{pN5D*Y(F%a?jd{r2HK1q($6M8ky zP~{kaQ-LAzZbhxH;pNXT_kCjyhV{k zJ(3jfYImdLB>TdKe);ifqz7xM9Fn#vCr1hX?<_!mI6LAW8gb*wel=8_{P>+E#`T?} zK*okVrNi!m6<@(C^NM0fPJISlvr8WGVOvt2=*9t(R(GyU5UnMursqX~y%|ZqjDK#> zO+~G$d7v6Q@h{N?(2g4R$g)kbTzZ~E1$G0Y9f3cY*4qoeslMoCQa3{=AvOG@qWJZ_A!80wo&UWZ_F&yi27#l})Kc?6Z?4!f(%NG=w_!@ZzI&W3VnQL->l0~0jDfYdCC-> zkq;0h$xe4+8dnac)WaJLJ~ePGx{Cq+*eMJ=O!Y;4&T+Eig+Y~v27Zzw>$Cz^n0%IIwn89R~Nv*$TJa!{v?3dp78nOvZd{;6)_NmOLS(Ejf9 zx)4@J#|-KFZbv7m*wJ?ClC&9_iU(=HhchTi3M+jd#ws3FA3WO(0#y(HqGO*-|ADHz zmd50^)FDidhgmOeAP?2Ct3De$R)t#}@@wEi%ah5(e5_E?*{)oX!`Kxr!r*)Rm&PEu zU@%Thv9Si}3Q1T0WCw)m>|6tv9dVA~jvlxreB2RtQgnn?fr=(>*^s928XPIIOG&Gf znL1gkzDdd)d`r*}7g-gzBrdQ~O3W9#LYJO0iN;r2N@D+ZA?pdvTJ+a7neTu%8v9HU ze$o`z@Qp0AXD81RZP^724nGkL4!RA#vwgczTzNRSxHubhtjfKM7(2Vdw5-7(u#3Q2 zLvQ4ArII7aSZukN(1+f}kwFH!!GnkN6As-@)d#4@=CLtO(he1u#ZMT0JDa{;B&Am- z2bKr!Vu*01K6Zf$!KQwCaEXlDB(iUF5yo4j| z6Z$}#s;|{(^qcrRu?uB*(z}@wP{zx1*0>l`>$MJIno(KJ0V6>Tl4=EZz*t}~ zk5OSDfR`6FPJb*tLyGM&3P|v9D?j(cQIa3c!Li=UQsSGkm_aU8k*#B__-KPX)Nxj% zVj5fHWx~e+gk}=-ZO5@Hh&&pcN}XKnQg;xXp(jOzILl4e&AWw(Amz z%R51=ASAh)&Dl<;n|g>!H)e)#^e>d?ZQyEOUp11zmk#pQjs^(&Nt@ZZ!&kdVz;6Yn zx@rR#?G9FHiB8g?3_zU)Av3T-6gE=F!63uNVKy5t^7lkVq9XL9Pn9h;qh9|jo?s?X z;uoS@`{eSc4FDt_$VPI1ddgxx`Lxh6vX_GW@iUL}JNJ2?qJ2ng_m%WbX-TYT8$M}8 z_XvlKE_|Gb9Sn>n{qD>0?O0=Cu_LEr)9&D>Ec_KMuzdPjzWO=QrQOAeEPz?$>maQ} z>gfYvacKPDLJ|KvfhiNfD=#}kDzHWp>L2SgS#~UdpXCy=KFx#``w64IHTczi%wjX>!q8n&c_@A+ zM^AkE1eTzuUIS&<0UD5^zsoyDq7h~yYf$Y`zF-VRW zN&KloXz7o#b6J{7%3`@QAxsptR)dhrcI1_fy7&g&w6|kqE!1NoWYJF2$F9PHX5Y%# zVID?^0*$f@=1Ey8wUdjzw3By%TPK@EOCLC)2q(U9mQ9&dq2*X*imxbZV?dLHANg@b z%7&l;*VWC@x{UAC+sBpDSTsK5#b44m3Zw0$k$b{mW_i3DSpD>&{n@mJ)slTWi#+S8 z8*SPvC(CMzeS;xwJ`~|U7n^53QYdlx%FoF-3n*>Gm~4LBx9=4b=kYSQc=mY&MJDej zckxzb3C-aP?QGmFrxM>7(8fe0joBr2RjPy-%1=^!BvbT&PU2l0Aq0tyMn>NPLxWd1 zwn4`+%T{q68$w?t&7~jH<^m^pjHoo(nukIG_a1v!%ggwMC%kf}e>$EQNvU8*x1Hog z!m0Ew#U$CPuE;O#GDQ4gt9G@@IYxVklYUL*Y~H7+D6T`gj!+Y(=7}EH;3cl3tNp6@ zIK~R+Qa-%|xa^!~+<~d`ON`Ey1sz#!VIRmC3YH65^&I|8KOlCj7stlZ2_WQDm&8#r zbsxU?*^!05Ni=5=Dm%P)x))!yi$B)Tt{nN4T#gOof-fDdtj+CjIU&!n;w1McM>h0F z7HLLSZG?aM4N=D(?olrRYW1sdrAQ_z9H;T^bYU+szILc|5U**25r>2-iez-rX?7&| zforF;IfI}_$_`Wse_^_E`i6^9{GeEQGW^o1;N!=@%)p*NgRX|;lZg>p2A@ee?YE{e z08?cl{FFH|QRzp_7&$-~mB4UtahQpdu|w>apH34j)m4w2Goq``?$I6{G`F@@cXvmG zFMWl@kg`tj%2S!ji5V0ki$O-+7!ahzN1gNoE{Y8#q@1M*G)EFQbbyr|We$}OCGon* zHiQm-#CV>G*+D5{WD1>h+ir}|3DTz|L(=|Z0pqabfG=~|xByXpZ8vBr zpM+y?psXK$Qm*P9;>x9xx@kA5#wL`NI{qW4eJTzd3}#q>=*QWdoo3<`6&&?aPpzXD zoJ75x9Fdg+LDv)94pbU1i`3XsV<@Zg!9j%Zs90Txthtm;KU3<&YX!Q1;ps6uHsDFX zGf9mTN#v)Iv^80Euzrp~B7KCW#Fs~6R(t3Ds31ioa3rg45|Fg9mC2lPdNA#XPHeK+ zbpc8ti@^9(zZtv0Kv17WJo4Hku|o`Q$mySmmZL$J`=m-QC6?1Pvp}c>Z?qw3>~1W2T_MI!fKz29mLVr zR7Ed%=x6N~RSYKSOH0`WO;8JCza<66lZ=iRMT{!RQddv{M#lKo7HCF)=u|9q`jLk^ zf;i&^@?_Usn?ObK;oE6@vM8VEivdbkVYI1yy3eW6D_JbUDm(ogx6=-!!tzHBeJsnv zueMaAYOkZVD6{ED8)*h#t4WQYl>Nz7k!q~^5UqA?Z#)`HnpYp%*WESgZm9kvNoj;I za$yKSiYF%?V{z@`+y$ITUFswcne+{Y^Q5TeCXeWay~cfzr3kYLFp2w#U-V@OSR!a2 z8H|ytw^OP73Lg0Me_-KJe%2Stt%cyN;k09jmAtjsl9%+QgMIK%AT#oV&_7nN3@*B67bc?0D!h;?Fyd*$ylPE?J!P&C0eX&uNffo0vTCbxoGsc>iX_@% z{RxgSP%!d}oYL%mv23$fBDLo_2Bh$wIEY4-2PI#F%fI2+w9uqu)H7SFeef&{Ado;u zRUm^Kq(QNc>97cIXPwWPifnXVDZ9jth}y@pJCQjnBoM}nnPa=iX{Tp_tM^GclT@~z zltUwesv4o^*@$N-1?HIp4uy{?{K*^4Udp#z9o;r`0ncDHh*M8l1Exb`vp5QQ zV%oC-|pDbFP7lUd9bRj-mRU-iobVm`i+SJ^g; z4eA|K4aC4hGPY0Dm!5-<1FB&JII{9IA>V{QWxMB2fvA@T>S|J>DYpf9?0tGhc!lPh*FKW9EDcTP$giDEvJ@~!8a0{u=x7k( z2>a_2D+(vD3M_F*07iG*sm)EE3~3?jWtcq@nP*18y6PADqQix<3WbsoS2oYVh7qpE-6|zqGLZ}AaC96Krq(1w_DSKrV|e@m=TfV2Mb9fJ!q^BGU%AxVc1WtNX|<%un& z;K(hsI6?v#=;$BiqrovIcr@Ez6a|c+YoENvNK&Ea5g6#JMmC4!i|4%6$zCa~bUTib zpzAA?CrP%K+#yO?n$oKJ2&r*EmsYhoCXaY^+~Si+ox&sjSuq4J%b+QrN1E& zCQ1fk$xGED`^U7$_lf~RWD9L{#BMp!jTGdi0{+-^QeVXr+$gs_y&In>pJR&fwntZC z(h$HMe>zVQ#ZyN8$x;S=#zK{Oj(X*ca!}66sEpCy``Z1lXyF*ShDY@mV8P zx3?}6KpK?E!K030MAi-`sWO~l$FvEMgPOu-QGk_gpXUbED5)6G88qY}S<5;-I~9}_ z2n2H(jO?5-9P+tnR2C=8Y)z+^r~oqC*}BtYl%SE8bn;U{Q+e$Jf0{u7ye=J7+$bl5 zDoXO^^yDKWkjh^JFZ{rBU{C}w==g*rav2~s$tr8O6%XoN09vJevVa5Lvy90gBu(vs zY+dREk)tG@!(<*zjhdt`0)#M&go9{)C5xrO!|6oZ~^I{mG=Vnj-!mySm4s2 za9SxYfrUCKi+rFS-Vu_L)YT`?=2x@_oFd&!hM@J6-qk?}etYp2h9(6S@M*{~-C z#z-w<)J2*$aRhPP{31pw1)C|B3Ac9mcH6b{{#fvj)kZ+ zE7pyTif^ePWa1w*RgJGSOsQ9Pm5=fRX$C+Jb24$VOnrX=bS-OSHSfp;$8=J@@t~4{Y z3Zv-y1-kY@-%Me$DY`}1v=)qw5yHp~aw-gs!t4Op-`flHmzxCkex%8Z5j$4^xg13ZNfU&<(_44@ltN= z1Zdi7vFknc+FJQ5jQm zK0A=W_$R-7$?K82%ElN794*L6MoxXPov{o#mjZyiHbZE}D?nm&IdbyOBEr6?%v+_W zf21i2sdTM1joio80%J;=uptpI#FM?$zOIhmCReyX>6+o5z-vQrp%=fFfW)sc1hpD? z4YG`@Qg)X;x9M>7q{@4^H)t(=1Wj!q#n_29cX91EiYbpdcLV}}^Cc)&LJo)}&XOE_ zwUh^q?9d)@SfS{aOnkH(Ji_{PWSH9#>?oPc)w48ppfk9$izq$H8b?QaX*RTnWyD2} zS^&`QY!gYKCM*nHDQj%yl8ZO1W}>l@zv5d4Z~~Bn?Y5WKqHrR(IYT;do*H5mgBp6zx**?P7wA5BvJu0>2#0uBOUuN28z3~lL@ zg)>MchG;^FoW%=rEH{_ufTLe@&)G^1C3;7FV+3}76xJ0%QX zePB#h>;XqfzXHXvB38SFL2$&!UhXhJGvf(D2L&N4PXY&F49(nwslejJnC9iBjl_|c zJh-RV41bPGk%N1@<2&j_RgcIBtnJ#vqgR;5<@KjBYhYG^49&N2FEe~^4HY`Gs_)1F1WU_wGGyZ^$$$HkMf z1s-JSyTHmro8&W37P0s?Cpyv;kEHC9&O@IZGb~XdZIu|W+R+cr56Y^&T}R1STT3JM zqsG-5u1(wsrxCP+`T@nqmmo_vr05UG_G9~$kzdv#uiZPmW6)X^!fwi$ z4h(K=N%i*ls_;1xf(#s87)^`}P*NmT(AYGNjAk-c2%Xr45Xg=tq>N}LAq7MgPDM=$ zU zq@oU$ceq4qXU!K`>_jmsb#-})i5FhJUL?UJQ<9UFI@s~EFjgk*51!|o`EMM+bTiYC zze48MlWFTzA|z2-+~^y^B@0!NN!#YE5+vmhbB@^|6CR<54${tjB^X70Ts#$a0Z7)Lv&0)fC67O%RtrfwO8!mKral8yw5CoWui_P z&-^pkP-EOhV(buF)Wtu*DzCoQCNYl`6doBhyTq~+CQ;h7Z-~iDpJ*3SO_*ey<&%tH zlSE9$V8*^wg*I5xkyhhawwASnV~C`cTv*fzp1KDna!{%5$!E+_AOMX8an((peh0V| z%}`IF(Y$Nz%r2ydJ%|9U?p8A zL6p@aq?r(@m#`|9WHg{*PL!;Qg4GhA3^#zM4Mk5fqm4Y+e>i4`o_yfs0j=@g$>4Ae zHCH%5%I=uBeSpoobHQ}MY;qEH%+P!Bsg78gFsNz^u82XCAN85Qpe3cDr%;Zu14mLm z=q8?A)OAdJ>38DN(A^rl8Jk(G!!Y*^%Uk5`H${|Zn3#{z?Zag!@l94&or_dym2&!~ z7Av0O7fsnNCga2| zb;e)$LD}jXc_~C6aLf2%84Z1qFTRf~*r-GRPDzHo<=5O_NP}}o-Ik%>gwm_#K}u&T z`omU(e$_6wInQS!MQmjwuoOT=e$O8GA6H&hpw_IDa!{r$;jNK4VG`=3pziE1Nghf zR{`%s^h%<1JlWMU)lYz>vV0fB>>iSf8aQgrQ* z=F0?9Q*g2T@z)M(oizA@Z*7`vjj<)G5O#PDe4|$2i-vUkj>W;ygGyBcN3C5HsDYON zsdw>epy>%~dtQpln1uy6(c2&{iQ5 zy0$5b!D}j;c3rKIsliEr)eb3blmo6jele!^@^y1fj-uy33jvgY4qCY^mae2<8B7nXZ&nJxC{Z{$BESx&)l~p)}*Jkp| z$Ewr=A)pD|iY20ucGXahkt%N=)5f-vG?1JP2>_CZj3Bra*}BvA)_lJejEmJk>>C1rR!>WQO`GQdNl zXmJRRMJbL{{oYc^T@p=NJI)x`tq%_kQ-1*!D|}(+cqUg-&1V-AU&uB3g#EG%L##|P z%I4UfN~tcx)yXY%XmfFvg;tt{E81l(@*T~Cwl+yxvWb84C6QyNQ)vg_po}AV%ZG9; zEcmLDs?E+4GKQY=iQjP?qXWknSyje^AmtlpB6sFm`?xS0@M60ItRlX9Ig3~7Yr{xpfeVZ*^rPnI3@E$0&20B z11?D}#(Ux*H5G5!;ILhNBDB&Mj$jT|ncHk50T$h)Xq9c!2-n^yq`IP7!ILNKvPfHx z4E9rDoDi1#6YY#aE(Z0-7|#QTJjS-k-gyUiy2p~kAB)* z=IGCtQZFl4fn<2@li~gQ1~wfx5wEXRy=4e_WLQ*Ee*2N*D(fx zhf*eWp5`5-P0InVTac^=6cCZCV2Ymh@k)_K-KI{^OTZ6W0+XcV!0pEr{gTnl27)!7 z?GQNRgv$bfa^cuc@PwCodbU%xk8SZ$QGz{f@|*XaPUPgPEh|9{ zu#yUbKstM=HD8}W(bENlSCpconvKKAw!f9OR= zJ2Vm#4`!5A8+h+aP5wpOSaQ-!ENauBNr!smQ%~vGhuk&Q#(5vCRFzjJ@JEJ8#RE2S zVgb*1(N{J*lE`b3oiTTKlwU;o!ZM}&Qmvf2IAm!XDU%R#__4Ko4wk}pgy2jNi;ha^ zmNn=puCfFdG&kxpZXkrM>Lc~&&k>Z$Bm5Y=U4$A3+tv`>@HJ+YA!5k8^OsldYtn|= zjwairZ?an53(*W&Z775^18|`e1Wal62~u0!7B-+QpuyFK`(V96LBGF^w0+rUkogX z@b!dCJ2^fnL+pXE=NKbgyp8|#6c`*fE@x-1aa>X!@f1S-@tFDYNn{EiWkjF=(jE|n zK!)&v4nKk8)m)NE)3k4DQ6IL7z?7gs#(c29!rI8zE8L!I{%s?BiBA|26t zV`{>el7=jWk%UhL#h9!OHZRpiUJj1CE`;oGc5t_SvoMm7xL78R4rxsq4Kb_}o@At7 zLA8cX;{P(O|AQlXbwqDaH7GQ4@i%xhlJ9PEa#wF;AU_7;7*Ur)!^JlTAjCJ{%kZvF zqQ_i>A_+pp7BZ8bI*4e|4>;P`r|A@dL+U4vq-?w2$gh}Wg_DW$W``_@MLR*QjKIbo zWU_NA9&m7?tX-hxXaF6_`CF~EXfPTQ+~7Ev&uba*vSl6`#eX}(skd5di@e<)YB}4Y z-Z$k(JXo2G5kRpGUl(l9N<5^s>>z^jM;umoVx=K+2!4(k;0+!&S zLX;u~r9>rIC_=JS!GaPL5T!$aB#=Itn)#mJ^WOWPNk_`A$~`mxd+#~tZRb7ZKKGnk zC@e}vCleJIh={7b=>sx@6BuM!hrHaC`lWD`OYKf}_@3ONyx4{a>H|O12J<$KLXYzO zoXPfGo;Q|D-XBGPq9tt9#Y_Rw=N7a}WBj8L|Fc~_3`3V^by@iMp< zQ7#K`%x%f_i?5-#e+mthgF}7<>RhGHF_*gP&B89mGuv12i9$9@F0Am=zKIk*Gk;As z`TD6&+4;NN$cJ8m_5?qbT-sz{WL-+fcKxVR#!zlyvEqWC`qv1keEfra(Ml%$R#A_8 zZokM-p8FuW$U}#%Qo5vY;eof2&+_yyY~ zEBhRVD+dDGBN~g>6jbWM{=U<);=%5}X+wB)d#iO2ASn-R82eoJDs8NSaCTe+Z@I>tc8SjtR$z&5}t<0EaAID6!ohJ0wngAcrF zgdEWj+`fyp2u*@X@~}bq`myJzVA1e`zntWa!LpRN4`>X{y2790p}s^#(LGsUU3jB! zpM_5PEL7UG=g;)w%a?*HE(2|^(t6uj+K25vl3gV>V=U7BlWD$B7$TuT;SCgUh@ecP zM8wsIWO(_MB&M61!ayshyVEJ|ibIYJc7>Wo94fJAe-LOoW`{HgK$X)I)Q0$aeTtRcWU7`M5r16$AguFB=oI{H0*oB^nRd zB$%GYnc3w`k+Y-+Ut<*XK9*~|$70y6M~SW^HAvC99jOduTf~3aNi~2-9LLznUV@c? zJvx?BW^zi<=k_57&|Z;gQ*5Ug;gt>L`!j8e6qS2RGau$#xOJspB}Ac|S?ishfiCmN zw~tV&z;MBg8iN&eQZ8-knKYG!y2H){!ic~xa+FVq9D6nceqY#`fYRCqY(l8EE~9;| z82yLYO3z422K7m&_qTF6HlOsk-Q$o1jo9Zpr~Jr7KMr#oIt39aDjBr)>|?a2)L{bi zxQ$abW=y0nRU%jX`uU3`N^~D1r6>pLGUNe#%$Vd%f$X7NB9LfIv2CUY+|mCO%JNbF z*orL>J6-vd(Ke}mX=^!CE?(rR&vC+o*USaIb|lKYY^RR1k%0{DZY%Jh%b`~vZ2-}n zSglaU)KV=-hKo`Zo@Pg1EAQpgTZ;eoJA?4df^FzZy zig1~r%@mlFtpuG+xB!8cKc(n6>FA%La)dtMd)$=m)fdZ7X=QuhA{YrC1I3yBUJ}KN zCB9!!?ShaWDxg&EWwyuCkgI>{T2yJHIgp2)$xmMTA??mtuC#|OzXtVxSZE=XLr2@34UA#nEMI?h>=NBbhUH|m(;`B*yW^9IJ|Mn5{C&c4SH z;}UV}lMQ*nsJ+uw1`ITXceWWn=Q*G^!?sH}Z6pm&ixFe*ORwugd(K~Y9liYM2W2e1 zRhB6!*pk8+Q=6R7aTeX)Q!e?EEkU*zfr`6r>45gc z(klE(`@(wndf81|OIqq$wnz+;N-6j|%df3Ya$c3OpG+k1oPd|}BEt%$L0%aQ_UuIQ z7q-(1AWT1 z!@YVm=32v4vLE?JZ&9)3K!H<>DW~iJiu;u)R46*6L~L#}T;1^UTpFndY_ZZ{K=0Xz z(?WA;a4=YrM*G!1E9^$9orGNR=$`?J`pM6CA%TEq%1DC}nFtab<&_8a*-c6>ZNvg- zee(7(_2g8=NDzrA}VG5)kyHHzguFIr|o7Cdaor;<=!^gp*V#l1`kjp;-rqiQ$# z!=iMg+jbpW4mRQ|Me<%$)g`{QPWh+cqiTJFJ!NzRTg{wX$;-MhgX_m%k-{7|7tvBa zl?$0Zv12E`qYbEI`^2U=bxW3v6+`+DmDop1!>^Pf0kvo_qXo$9_gH z`xI+PMksdckAM%n#Hc+dF9UG%DIx!pt`Lhpco0LNd7#BxXXucqQh9I&|6mWgN%j|% z$kU(4Fs0M?C^V$D5f+FQdiykKl*!_>mCr?-%lJu*kC5rIA%KLMZ5$V|(vMD{o{-Hi z$6HHqCCFZ-S&Vd%Pd{-jNQTR1naYEnHZB_dJNQIe@_7=0$qQwwe=|K#%!G&LfBi;7k%BM^kL+PI| z^20~ya0FdS({Zl!K%M?zoaXkPygyFV8qdpmWt>7tjzJPec`S$VDGw;A7ZMOJO80vo zt9~I79kYemH!w>&ij^BgWV-S(7C$Kwzt~SIgZq)?X(Jbi%3zo=&~}hB|UT zqMqsd`k(5%CoaYMj;doj_6zp*?5Z#NiG3rrl(`P#N>t}`^yF_*^Cj$go+a> zb%|bHr@)}afT2=Cuu33}PD^?FmKidZXIXZYG29{*eX%Bt#@Ed5RgDE2WM}w4<6Msai%(5UL?R-44kSXUm%n+l;;7|ny^juR+_$Gt7d4sX_Ibb z=CMe%74*^mlx3$B>9U!VE9Ju*J&XlpNk+Cal>{45rvLIpoFx$*ebwbj8ayXK4y2o| zxai3?oq4tTRuumt8(B^lpFt#v(v3aN*1oR{(#X$T9n!9?TRGTdyCF&s6VI3SQ%+8} zsxSG)v|?22^hJ|T9Q0}Zd6tnjq2SW!WM@6J800C{eV2UfmJNmKJONTEW@n=x{Ty0; z-lTuYVjM7yNfMlqVqnFAg3WdJFtONVchOxkMa=RiB1&59@_p%~D+7D|(1-YhW!wkS zkJw^0HqK^PDSRrMGIJIX;j}k-jIOQI?Bag0Rix+K9vYN*hFM?d(0vM4MAecURFS zq$Z~XD1pX`!nc&Ml;I^tIuaC+)g+efmvTz4@+bqP@<1e>EJxailS(_eIKpezO}n&s z2uNkmM3Mr&mW|lrWT%Nnf;b=aju|o>-S`3;f?S1IeBv6C2xO`z$Q3{O3DT$o_XDM= zDAq|jW#SydnCaVhbWFa>p*{AoD+9-+{xy$t%tF7|Ob0FaCWm3^lVHl4;{|m120N3d zuS|HolG(=w@(J$8*sq398hyhn4)!?m&_n&$%TT~({dkS~$THF<(k4h~%h*x|Fd805 z9+G_CrErQ9N=nC676Owg9OXGE{Re*;vdt7RCHM6esqQE8iDt$nLYCq{uZ>F^hI5rh zXH6(aL1dcpNVhZux;Q6H(PN*RM!dNj-`8*)w#v=#1VaoVyJn4iy z8Pn(#XhEE$^;f&k!H&oZun~%Pcj*Ro`#_Y~E?tF$w2y+BuW+sMeZ#sMZZ2i8w}AAA zY;xnqA3A2>>QImH;H4A3y1zP)PJXluj%B9#Woy|<7|WGqg>8b4R9n5K@-2@H<@<>_ zm9$H#G%#i%^WBnoKTrA$jJQE1)(hBE9+m8?X{iS|_-YvEqL2C!aEAjw1F9K>$fK^A zNyTqDD~BIaHvZz6I$?m77`W2Kg)NFYjhQig;pVS`;s#EZ_yzHRWoiIBeMQY#M?`M4 z%4bFw3z1k&iw4Gr$uiHCVsP71Bj5yS8bm3MO_U#D*nx9g8NLo3tvmA@ph=9u*Gb44kbmf>}7`nj?v>|rC_gYwae7N zIt1L5WE!ZlY_Q25>X5pWEca{4$#Ji@4KVKci!-K`k7*sIhnKcf1lwSvtPfqDVG~X2 zPyglkVMS_^FwxIa7Jfpe$J|n^`H?`mvZXeNC-1Z4ydZwrh!6ZpM*Lu3+ft7scpxn+ z^2Fn|BAMuGP^5hLlxyuAC$ewn%0RX$Y+ziH6t@N91c^}KoUL@(qS7c4d(?hpE0u*D ztl(m{-fa~cfp6Nqa*Ea=)5R}7Ux$*#|X}uM4Sl^`)rh6#?x^RpYgF?ot%92CLZaqX!ZhOm{QO^S($j(ki7ZuQx zA$-%9Zt0V}3E%>hr+mO6`h5vUxjf@5c}mR_FOnDlNP~{f0FWyNJEgLQREbQbF>6Jg*&^lT%&i(O zx~TWU=xhT;U6F3DYbWbUn@yujSj@-Yw6`pU1|FH@wr+V?miBVOg@!!D`huyj4Q>Lo zWm$19gH6^7dXy(Uyd2zr6hw^NKLaNrXO^_L51mwWfz<1J7KJKlLk9cs6s?N$FbyVK zkL+WxqIzU6p9QngC{+7QP){NFmu`(Z_&nR1=#}8UN&Sc)SJ=(y^9+uXnFU6Y*2 zP<}t*u9$sFqT52TPa-1vFPJbw3VbmjJ)YH;-?9%{XzHhY3iuu|1Zc61r=zV#B3Bll z95h%=(J705DQ5VyJYrNpR`v_(PWkG0=_g>Tne%r zO0&VV1IkEUs1J*FB@dt!NEv&webG)hiO7#dA{Xn-`V%cLn5Y(7f$fk?RB#V0HuyqK z3AEF)pLtfuGDM4g;*^1EBltsmicH3Ldw2$;!8LK*%pWZa^lX&AB8K=CFY3Be>+Dg==^*+H=K__C%hSuO-wJhIL*Q8@3pUI^E&{P$* zuas2_wKV0?pL3k+m5)8h;)i5o5FL90L_VdwX?M08WjVlC3&>W4bGnBh6zL!PNr_S`-IK;_#rT{?z<{G9QF7}?aASnyfV#@x1b zj7_d%b3&88w+h(xXS|_f#v6=7qHEc_77d{#ATQeu{=;5uk|(9hlPyq*#z9%uRzykT zyhOjq@+KKue@)2^Tk15?GblEV&4qQraIOY&HOd5mGEK(_&li~H zu@c3ZKc3?Cz$|0A?~COHf|9c!WnoC;*mOllLzf+pJY~Tt9D^|BX27LU5TlAc^{c&~ zD7PNUR8%1+9`Q#3j4XOF0OhagrahThl0ADt=RppGxyaVP zY14JcB~EvgEk%CXt&Z*`Thj408l*#+*qj#%MLSTM<+yHDav6pm2g-#`Q z3m)j`^b{;@kPne(d&t0-BEmLe7K}b(ImmXX{FIwNxgg7FYV5{lq@->$Nf#GLTz6mx zb!!{6r*vgog4P{VY1EfHnH=Xqu&|wzNq(gQ`_f0q%AX<1b z21e;tcKS$EerN}g=#vU+CbjEWeyEo%1|=fMdc_R<%bwQ3PXWhM0Y+H8^<8^44K#)S zEuT6uBN@mpL}YZ8E$!E{f9h9WF~A3sm1Ba7Y9gpi3;?w(vOVi?z1S-FUK&EN&qJxw>|O{a^?XdBC-d+ z(RQ54MnP%HRGRj-gZ}QMOmo=^{URyr!yFbw-%mT!E=xi%MR9vVzVD0WVg)-Wt!Fe; zT+5Wt(T(|x8#M=}++^Vy`7XFodw^`9)l>@rctisosoc$ImX!+AoVWV2U+q`GzWJiv{ z(wD5>rtL+7)O!U?iK-^GTk4e_ggcZ#=(9_|C`aWsL*Z{)KX&k2B|wH?g>se9?LukT zVSAnHQk1HkQrpOH^xSeHfU=e$wJPTJllrNvl$+K-jUTd#i?pJ=b*6MnSDe{v zzOLkU4KsSRw@-Bpi1c|H8OS3~Ki6m(FG}RGSahxl`Hmqd+mnt9x#Uvc=%EZG6KF4@ zW>eGSh&C=4=_E+E+X4QD$pX^P%Azewa7s%S#rd)aCk70v1=1BqsP~qqjsCrKVd2Pk zhK;Y-f z@bMoV89S&;KNNwZiolgn7EC6PMuDUie^URX;$PQ?jKRVXea#f!bq3Hv}rTjnjdAS?*-JJfgH;{E2mMH zHsFq=w7f7<9&Lm_Mj8dS$nTLJUnNqnGRoyB@>M{kVIR_yM@hCHfAY(YFS~u>NN4p)u>r>m3=jfNn#Y|rW2MjOus4hN>ttE31X{ZkpsRk+kO^> zim&*fa223ICrT7EazDCR>X&RVOBk$X36kM@G{&!uCjRK`AMGITW8Zyq}3LESag;>uFW(@oR7g0RF#_XTM{Gvs#y}-?eCgd8Ulf` z5P^GKKI8Bb``4u!4$GBVQZBxT7rO3$g_F5JD%Z=KoD8tBWQNsJ2~l3uTfO!-1WV64fQ+N&BBJH$Un;qLpp_85eYTo} zyK2byFR~>4CA!WIzq`f?=iF)J79Y!p_M)xR)#9zju8>v7k?4cIg|Rr6Csl?v5w2ci zd=D8+#MiSwGEHzbK`sUfKUig}<4S0t9z<>O@zNb9o#KVYnzM^A@EKVrB`ThTgfd>V ztC-8Oa`8X;Cu77(+Ax(FLUo^e3dU%vgDab81HYiV~bO z1dUklJHBH92526-^AgXg@&s!bobdts_55l5T(E)3m}jbDx7ch%s`>NV2q$D3MQVCs zq8fI$=_9qmRNv{Gq3(>UT9~n?Ihgvlvyi5Ce}<-QTWDd&BBE0*#6Zd&%fnY;rBw2u z;u^r^L??@}9EmXCw=0n;?j!MTA2xXpXNqH?8s54vVz%CFQ~6$ETyN`@QI$lL)3=I+2b#^F;+zG4kg^%KCQdR5TgGKJuAB66kYfUi~>svebr8*#VN&)dPLh^=J`}r86KFApn!Mu&8o0byBSP9Ejd*BpZFt$mD(~ zZ`OpJcuok#WNXKv^Qv&~L}-P5SY89UI`rg*2b$^R zmnd)S7t$i;@i}be@^JTFAR)^kIc@|mc+W@%ks5B3DoHIk5{{j}Q%`p%s|~lMPl71l zA8PRll2s?U$bryQ?~=ja=+DFtT#3oln~z)8T7pFle92nDLkEb|DR6j0O6Q}VhKqHt zO-~!ys^x~%+1FhHB|!96Ws;mE>&#?$QM@BMWWBc#uh|ImNoEN-5|5OSTZ$TME~QD! z{Jk0tQDfg_RNpihqeFT}R%U|n~vt2GqSs>17tY3xA74K%H(4(C?nT`*0_&&rN zOmG~9Wnn5x#|LA~SNH0fh4#jXTk}=tN-z~z;3jseN`%YDu{0&Z0)j~>5fW9hn6Fxx zigoi)PP2frbEyn9{f&nx2PBts#*rDalpMeo5@jzTY{MfFLVP>tSoodiIV*nz>IL`Z zE7L2qz!Td&&V@LK5IP;YDQL%PtreM(5L&4{~>DtmspNG07C^VLbO%2T)NpccNR<1m( zZZo0d8q6`Xx;j68DRcaI2sb_YA%p0O8N&tvPeqn)ev#!>}f;%ppjj3-Fm|$DGzSsCQ%p zYf;dirgUJr@HgtWg=7@hg8S;cpFzuE^KRAZQn7#^Qn&}j0Z+eWcY7oO93ohd11=@M zS&yS;3RQ;Sz-xb5Z)mn=N3_%=kg7|O9#hSDgC$*oOKx9& zY;R_JMlN3bh4M!_Onx6CCSbTV=v>a{AGeJVi0-Kl3o|~upRS2{OtaV_f-hx@AGt|m zmo~;D+y%=;hB+WgIF4c>b4;9ny9~9c5+$5b_|{tciGIP20slBeiSX8~mR!4^i7i=P zxMN}LH_{mNzZW6tm^LAOMY>0#xaERMh@Z60)|9RId9ku2XYyf^q6X}aK1+CL_Ary8 z6INYmgY#q$85actfwMW&@0ImxT8ZG2Op^LX0kmU0d=lg;*s*Fd;NVEqsAeaUe*Iy&rr>_m zP3#pZL8nuHzvY1MK9(V%#Dl&SkQ-3EF|1SDbKNMWQUq-V%-T@V0F*(^A@Fi@@FKsv znMY8{dr*|*4IuCdET4lBEe0uJ+MlMBk_8{q%#XKzp~IihxrDl&z?6gq-+VzVBZnTq zHZ^H9;&tNFA?r;c@4*PAdsHlEeMtYcr%67U@eNrRx?<%>xO|pX(u{?(;8~*CJURCO z6R{n$)Zv830>=+#_T9WvaTpCOjyABK;y^P;u-b)^Hi@ftt@Dk3v{|*{Pz=Hs%7UYB z{#L6?!Hc{ogV+?OZ=4?E5_ho+b!SoyAtiA?N@xMEC%~}%rcAcL{8x^U@>(xKrq{WV zRg4H_{8LjOjrN>9Ey8TpjAmpd$iN3dOOzAh3CoR3Y%rEUGK;uEJOriy=MO@7I(dGM zMfsj(?l=+|I=#w5<4Jhg?40Ncu>(U%OF#r1?-c4bBUsG{CzcBS*a99}t5msS^mE|7 zxkYo$v<78#L}8c=zKLfxWil0XjOoBc%wS`^GHncdtR9PW&Oy9KFfKt^Dv&qCpMDrw zwcj7&T9m6Rp(LQ+rNUxBKz~|oz~GsVV?X+LJ1Qutnv`SLQ)mKFgU(fcTQU+%vwBV8 z>G6RdEDPhBl~28;tuv|#rcAvp$5C=tS`w+_;Klv^XBk~WhtS=%J(Dk80KbNDorz-I z)D6HX9GW@r^dS-tdIJO)U<8{Oc{xGzjX?S0TCQ z2SCdKP^|bOMcMXIpPuyNH3WHVzDu3W{kPfp_JL7%zlvT-A8RJG72RXHgT|w>Xn*t^zQMxsw|V$Q ztjdeO>*>-uN|q_P6Q`|z<~!tbPYuld2 zdetP1Bt@Gza{cZKNArvig=jmNp{MuFurd9C;Z!A~G=7~W7Nn|b>0^`{=dUn3b3y#7 zfol`cMXg?%eF{yax;2Xj&V`fs2nplJPWQ+8`5>X!CSv}rjM%%3+gm@Gkq5DyDehscJ8BoO-vgsWdE;hc!P-Yi5J?d;;-!(O4=!iO>cLH1z= zQh#ZU%1grXM0`p^Qf8&hYKh7J#heW+<6oj#Rmy#SoApncxioCN%pG`~3OLv#$1tY>|T^JR6Rumv<*K4{y zWm*aeHXV+|mQ{CP_|)V7kdj3l-I@PKCP_a**(XXz!213DdS&+*pF<~0c0~>kB@dSD zu8uNTmew5Sc!oVu&gwUMyf(9~LvT6qHQKb!TuaTAHFpfQ)*9Qqq&Gr92_Wv#J;KU+y@aR+|*T@ygezk3**5=i*uH@DrqX6tC zTO_no$n{FtTzC)6Xs@XP1>THta|63BHcmGduSlBT!Gs>(ov7lC42~znda4SL39kMs(kMRp)WHPYDID!S{mTutKYU1oU*U^Ncj(!HNC& zMmAsJ%+#EeQS8m}*QInePYLtjker;H00(zCLuSiHq;Vgh%45f=?D3ZnXw%~wLu|eC zJ};Cta|sSk&Xact!QZCv^~_9r0T%5{S)>esE{g8&HyI&q z7?dBD*~l4_0K#`)gEyZ|!MXnCzt{_l-@hC3-fFVi^mL3wXB^S6*VVQE@0;=8Dw=`@ z-yeqFB056LXvLxy3>rPh$I|X|aAuvH6l1}HyaKNQvO~A@ zUd~k_8l>NI(%MB&d_|C}5R+j>$?V5x0z-J(VCoqAU3WZOi0?4Z%CLU^Z8FLaaU8pO zOx1)iJY}+`3*0}^X?NU(3w2#o>W8diI!lsHA6) zaf0CURF_xvj-h+>jW48lA0aCN=LLjmU1)bT`{U3b{v^jgFoWLr!dR_y^Gy}x4NLlu zBhC#8g8E-W@LDgXSuGJt*WmYM} zE~b$`q=_X6KnA{)$Jh@S2aUSsa2N#)YE&Ba#wCxNkE951?a35_Xt`oW$PVb3Sj!ZW zvA<5Lmz3yg8;{(?tOsHP6nnn6+c?H*qpZ`l2Tp9Vz7+-Cc_|ly=O(?)58GU}nt^-C zSBBQF?cU&m!1a$8bLIhX#VMS>+JDOG{5yzSQ3Y;#dQ+7iUY{A@Lfdp1MNVyFzHFD?L!NKCdR~@| zx+M2)`YliW$h-15%9IJzrBz7aFW$Ixa5;_nz;_Dx8%+2%>bpG{jprc+1av&u4&UZQ3B*3pW$Of$z1v%PHuGN51N2n||8){d9dq1Jq)1 z!8)($;Hww7o;iku771yEi69dw{$_%?g_Gso`1D16m*ZA~EIwVJw}XJu@{2Z46-+*a zqY91EZ^b#~->eq*V_ZPX_ zRAX-*?=~~4^pbnv=p+x%~t4$4sCr0J4ceJMaze(epbz6BONMn zy?sQB-(Bji!eMUZUIOHI3SetxCg3ZKHYwGTaAO3#gZ1`g=jvAirN{o6=Ck247Lc;P zJIkW{>*bAfSojJZwK&W!s5~5id?cZ_9IKI5Ps!Y4_L$`gx=idqkDIL4#&(YfVKUC<#0)O*akj8z;W%B{pQXFYd4dY7>_hiTTh?u>}e(T?TBEFB#zYo zXp`ruSAGwbK&#>X<(JDL&@naxpL--%x(-ev^PPVB={nm*{T!Qg{}1FOk4Eu-qK=~r zNN3DW`9?*yW0h?dVq^)(2NAmh%&!lavzR7oDMy_=n2eZHh=TDZd7M1q(!bafk0Bp< zgOQmM^%tN?cjn;f(@KN7h3T+i-@o_8Zy)r8`%iSCV~_O>bTGhDI}cfkWD3n0i$u@1 zN<*ZErRQfzCEtc!+3-kZ6jCs7)HdZl3Ud!jF?5@DSoxW{TS;@U7<>sj?`Asu_s+1; z|Fe{q=FK9eA9fu(;Ub}_v6x}ZJY?{}O&X-wdq zBL*VE+pj9p`qq~O)1F_bP3PJUO*#S_SI+>oA%fTScUQAT?!UuT`BB3nf?A}C3}%i4 zQKfc1J(+d5oI=FlsK3DIkUyr^!HM3d+0>9Teo}9*1kFFg$Dz;eMFuc#5-9;oU?8uI zfWT=%r~MqE>3H&$s&J9p8I1~IYf)&Q=>toC!D6B9=tMa5Q7P%Dcs zA5OiuNjFHtfHB=zNUgD$wYcKLN$^XSC~&-6>P)9pAgHD{<-I&bZ`=s@J}3O4#S0IN zLi6=N(QRU*33{7gy?-f`=s4}u`wJSX(?%L1CK3jpin6*&`uQK%-kF^e0-}wkc?D>$ zN>;}%VO`G7rOh(?yb?5z68KH0?+0S$upUvA)gFQ@^j#k{=N8*Iw2oIt(DNyHmq{@O z27(-WP4!qIZkPiV+&puZWPcN zIL=+gD4Og2WSZ%ceaiy@Lm(hlZ-kEtFUC-$3o+&&BV>`uwA!PX#77F|q zE@aFxV6s^WeZ<>Vor*aeg>e*IE%^OgArHd<%UJ{ZV2vnrJo1wS{Gl$c-`vsqp$FC4 zeT3-aJN1aIprgALz!Hk`a)HjvEmsMWST}k-d^{I!X&B8{kcwkZ^0ml&iPr0>rDGV6 zNsrU1+l2#-81p_1UUI;4b?&ZIxB7ADl_^&yizcDT(zFxdk{T}1{E2_9k zb71$k<9PPnJR=Gn6XgTq%HD)@;BZaKamU$N&!JyG;h1HU@2^73;h>Nl)JI z{Wm8c(fQnPR9?UhVL|T1_qBMjKg>6C4S{T%Ep||4U0&}T{kJg-nFf4=dTn0GZ>~w(TrqQpj03hD55}=?DhV4BSHLZ- z)^qGl`hV7M-}iRidZhgyDh5W1vAAL{X|EDfg@bI*Z=baHYX5lMQ$Y!J-p$Vp(@ytL z3khDXt@t57OUg?-%uFX(46AJXe7@)_tL~#bt6^L&<@V?A_jef5X8CaZ_q1s}m!GZCH$5bgC+Bo|xFNOV!Ty6DkXz<@PTh4dFzC>5T!<5e?l01WOZ4Y2d6@lsDR=bx*PwfM`AdCKpOi~~;Yy1dk_Fy) zH%FDRNkiu@2?PFU7>7T1X7$2b0i)@6Y(qt_X7@TXbGI|Ev@DN1G))%ZK z(x9dUY8j;TfLcAH3f4%-I@eZ&BN^kYZ4AC3zn<3NC&5qTW&o?EqDDrw}Os-@#=|TQgAHX2Fen-bX#< zF8mb_XJFvjP}bvw)5w0Qa{Qcd4o;7?ZjYmd-do@Z{>uffm0~W*I)IqzLAvDP*A|es z@I8R&Y1PK%B#Ja~hUB@N=jq~Ts549{L7ruUikbI`^5(?uF@aF%yJ-93a4EVF-uksf z(W{ifZQ;{v)>BN&@D|1vFX)LU=z(x=65O#6JV%1s!1Fy2G}bWGXb}|8(sz`_^U*f$ zmwBs`LoN`-N{(0v)Z$xQ*n4B~7ZL(dr#rO_-{g-0fphR^F%wQa99&BVrNj)}2`wyO zpI+aV=+&)%EK1;^I@X)W$Dzhc={-|O;E1wP9N~w0FV5= z#W`OoR}7=_37KW8O7^)KIP3XuBWn-y&vbKb_8zpj;J4OKgjTOYkcN+%AGPsYf6kSUuqX~b6Zji}x5dDGGiCNB+wFQvo8Tvbs9 zmYKG9XdTwnMV8LzdWxxkjvO9Z6a4(`L_xN^NF*p<@U`J>{h6a|1F#O>R$F{Db%b~Bm zodb0d`~$kQ+{c&$kq6DOGobLlS%4%(=r(~R{iw+TYlBtn3F;1m_v6`k^Bzk+ZH~|F zHMY81+-Sl!;en9f}YkoBo_{Kb8BmRi+=`u6#ZQRCNIsu8u4g<7Eli0vkP@gGwQWd!Q4?? z1_084T!6>AO&mYk)`}U9?lTEl1AfQ)pbr-wF+VU2zfd*Y&$1wsfRQ#xQO^xH zKl4p*0gtHp_N~pqF~NbSJ6+I8Sitdgl6s^Tvbjf%<<_RdXbAEFdXYwF8mAW08mJ8zR z88Rdzcj{;>EiEO<#pM#@xc0+WK!{n=R1#~jOadWq>8MJ1k(jA&hahQwXEoIBc8))Vd0|{x=$#iv&daR+pmQzj#*CUbi@LJw~<*tz#rJs z@4I=?h*xpdv_zQvjsN+7v&bk=tU>1uFU=v%lz~3+l0_1j|y>COi<>;bXP3FR{;t&DiYI zA>hVLa7bW@gf2(l!vP8cZ8|Jb{LxDg$e<3`GFonTQj;6g9OVCYdBD+y)DX>dD<02-42y^e3gC}cCEu}y@9@9c=>jXRw zMgQo?x~z4T2U~=)*HQf}{_%bsT3^~MT1Di*5WPyr8KP|Jv#cWB=loK5Yp5*c;P3V~ z^y{^J8mQGiB$%$k{DxjT4DBPC6;KTWAZEbC2s~}(-%Zla0Ek<|>0c2BSaw;KFB+Mt zS}{5q=|Uh6pMa1y>Y&vAc>JEJYLu#Erb@2GmzrM$#w?#%VP0%-%e^0T6Pw`$i*~)? zg5MI6g&DWKt@ZEMYfSAw3Z90eMt~?AyUD#ZW0+n}cwevpf|Q%ZuQo{7eY$o;E(aUf zaRoU?uWwpo%vpp_t(g;)MECnx6stmpoR-m>7cW@Qz?qMyB_88{N#3CGRI`B@Mr{|( ze&%PlNxj`3uK`M&f9woG_x%81lkg^Mpi6j#7Prc!roD=J%V*B?(We|{-}tCCV@N%H zWY&`*On8TL;+fxHym#_+=kWWZaFu+GG=Srf%aQ2nV;r!D-qX+T*yrVV-tu<^udNU7 zQs=mP+_Q=5a9YgK0IqXq+g=J+ZNdNxo_kpt`DWdj5Xr>!>LSBL+`RG9jj;|oRKgBDvr9)qc9hOikPD} zqN9RwCWN}HZpZ^;skbh0zPrY~p6`r~M#}_GVgW{pW<)+CGwerDU7eSmYy~N|?RZgW z>nv?JgZtQIsRbQrah`tGFw9=cJku|*yKosv>=Iii*H?w0B0ackRHpR@zl-|0o1UIs z7be048;oG0MWTikbFp62k<0RqB=3F zZFoj~n?N0{zIm>G>|AYvs!slPfp4>TOJq*n>dn+#-|?qQ6Zwx`=yr3A>20O#{gn-3ij2y3oBbIGahex>b3CU|N)5ciRE_yovhu7{Qjss|W5b^XlEtN8!)A{2hEIJEHMXKlB)#m!I(Za;4l1{xqA4wn}` zXIFW}4a@VJE`;3ezUU6bI2eM*KY0lXPp#>O+(>~+*9J$BE2Q_%FI*e~=@AC6xX#-s z;l!Sie%khRYu&fgiW;+wG7^3hC*;pk+~mYt1rh#lCSGRq@ZBX2aT|$Hi(w# zYdf~*c5q?=8VqwPz-&8%`}&qG3YiwT0Tnta)ExVB)5<#{E(?SexPv1(LlGM_*g`*Q zlF=+L%wj|s9V=(B_{fo?{n9F3Kt{G}3mK?JFb-WGj8PS&@SU7Z)m{O?*26cOcCd;& zhf*VttIGT~Rdig3eh`6$J!jI`_mc&Z7!8(kw;ec)`JG~$TVAsIv~d+H9pvI+okGF& zHvsTrSTtw|3Gz3yRu-tsy&=UJv|qc*zlm4EJeU+yR4ELzxRu#hx{9eJdDZ2x=irg@ z){U6YfDC&b4y9d}Xc5S8xG&JD@G`Dfwgg<_uDhjr4Lcpp#7l8!dX%YU zZ*&XW?(9vNp1du2FI0TsJDca$~)OFH8WCJnTg~pe14nH5=tU*RhO@i4PDM1yx?+Uoyx8 zS$w}ln>^ufg4dr`;HTwp_RtbY3p|1?O$rke47;3&n${&eiwTFh0i$` z6?pmTvc2-6(=DYmfD%anW-_m<)>*(=UTkdM{ zbJ93C5VtRY#F>qUKX}*O4MU}I4~Yq_j5F$aXam^*rK}V#8Euj*r{0S39z@z)a%J*yV=xgvBOcKeL>gA#ZG-{C)C>ffDpCOr& z|EQG(aX$KwmQs2yA&l*8Y+^#(3gZh_xks)|{+P9hhM6{Q30L~H^6~A-GNm#t_KAXN z`sja1JK@M@Fy1Vlb(dwZy|Vby!7=qROWChu5;|~E!J#;UQUcbSr*)q8=XI{P=XJQ; zua5D!9(X78@$Po(%Y9vLuR{pDqlSt?2m}urEV@bWvaK>s!HIQEXR`sOorcv-){X!mX}&h2Bu-aCm|2S|WoHx9+C5x|C=+yPK#0&$S1kiv0udG^ zyyws!0JKw0h^c2^ZIy_eqv9XG3;t0U13U~tI5pj4)f@S;f&YBGhs+^^8pD*nwGUFF8=`bF69U({!Ra))<&=Aq=p%XWEYQ z`1hTP3!hh^BgLBI)NvuDk1X+AhvPBkx`iAy%oKE-@Hu01TSZ!i9rM7I&U2;eh{d;N z9fx5!zbsE|F3jHLW`=9u2enOq{$U>Z^b3~vb;c!MSYJ7z6wUkZ^_o%Yj2nt}Sk_2~j zah(Is3DZm}twRWu(kA#SA<%|ocK8>S0ADZBKGBLKh%u|I_R|A>R;#w4x!-(T>^UsY z))uA(WT)nvKfGblTcUksGLO^;0}U$8!kjwvJh~|^R=N=>5-r7_BTa@ zQlGHP!SUQ&FK}R}TQ2hEOXTcq6HcLZ(O)J; z!3ah0KL193RgamgQiowjYonaT+DFitLhIv#0XNuTK~6BOqy6#s;{tejD&fb^Qyste zj{y8{$p0yM>tw=PZVXUKAqYSr3G(Ye1B&=jO@A{^iiO^Sev=H`Tb5^pdFv@bZ)2{M z>)j5CH( z@p_~@54}aqs9V~7PUMJsUh1}Btv9;$7yJP0o$Fgc{*3?B?;JD(EWvxASkz_5SzDyj z@`qF8nsZBYY5UMX_j_VL+_+c3WJ3DO>GYPK^>QxZ5+NVQ#LJ`AgRf#e+ka3C8T7PN z12|PEm-B=<2=6!6@+$YFG|0kvkaq;MQL%|8{`ry$qwd{XEPk5hYz)y@FxoMk(j5$i z@2FV@RuuvlBhA-)4TNL%WD-3ctmE=%IAy!$uL*1hJGK+xlnRWIgb7*!yKcXJm^ zHMC5uiQN2*TOvVUs!<1$4ex7Luypmkpyup!E;kF#vEOolIDKhrP5fiI?mFwE4VZFN zKleiO$NwR0J_~3w7my9-1YwASh%MLDA|Z5U-)C-phIM4*y(f)8&~Df=DTrfGdKIa* zcucJLrU{oQa;asB5|G3G-z&FiWmg5-P^WL9*@GqPwpZ}#$8~8R7v?Esz_Ru}qZwZm zec6JnVL6N7_OZoaByh}A%n=nFf0*2oiMuN?gjDNtb^fvrb(%JU_mFK#Jm*Z2e;M$~ zw!<@|{=b-&k42K}Gf$**XfEi05QOZanmxDR5z`;6QMXjF2i8EPv8mvR%9F*fFp22-0c(?B*{gvmo=Xm|jvV*UriHLFbm z&Ug{ap*sp5qULo_om^Q)94!kP))a=l^xxSZvt2rf5Bvn2lSvveD=mm3-JSI6#50}7 z_QEH6#vsFo3?jqtjDMwCGK~!TCB*PG|1Y8bb6XqXrTSkS`Y)8NCxa&#y2Qc?{P%VS zOzCx#GGjTt`4U^aTEw@36;0!|q#TOD*B2C7v=UHuBpe7la%yj!!c*`o~&x?pyG2CD9 zbOX4b?)ZPXsCPuc>y|<=BiEMy9Ui}DEeX~f@*rrN7=8Bk%lDgiUo)}PO{1v-qrQTw zL#^%uU(}uItvn04!%h(m?`7WQH$-A($N5Ij*rhp$2@oRF09)N8(nV z^2n^?g`M#yRv3A6bzEkNbP{G!2PlR@QU~WJm5p_k&~C}le&x|&OG8IC;!G!`{)^IK z85~Wci#0YsO}#(k!$)_fYQb3i$7#K6PKmQ z?(j4?#hMcOBR-cw(02+mLx1)^(5M!X+eX(JMRwpP3s3ddMs}ax4f(bniG3DKSvt>50G|! zdkazM^1V3=p-dP$j`jV0rgI@=JECg_D7dHm+nXc8pUsItNZ3Zrv#HouXj({Rw=RL-UN56->bt*LZn${;3jo%3KKDo1mHSm= z_26vcvNPvkpudF$R^9iNTFbbQ`4B)&WOHI9WJbE(SsR{<*B6H@0g{6Nx~(! zf8mi?W``RWr9L(4Dyva`p~R%iaBrmjDcQYwK}TTDigkh;O9@>`-o=6yFPX)ER=e_; zmd$#zg1omwo!_(@Lx}mtK?}YXeR5Tn5Xs+RTUVdWjGf zc@G3)?M~W}MCT5$N}j+ExzH98Ma4vd_WE5EoqzP-mF%T)(+dUtrJUn@Bsh?thNJ~< z3iHkw`Dg+JQ`5;*~kyf<5aC*#-%#Un7C|mw(R!q zp*8lUCccWI(lG@XYvuXe9_^LrqU*dPueT0Q;pP*i(l|%VDnHbmV){ah@dsNGQlPM%_wJ0cZay!nbP&V>@z)|0=EXZ_dSI6NTyPb2u5a z{`pA~`K_VN;Tk@WGo$q^=>@e+Yiy7>2cKRycAj4H?-8muU!wCkN(r4C)Bt>SIv^M!T<13PJl24*jcR=JLEP3(Kn{7!`MA5Ta?bivmOp1{W) z!VFc_{|;~lh2!A^Z{m!GJeCb`x&JwfzujgI#M?FWan<*~oN}Je*l>%OiP1D-t$fRL zi<1bG3SpN-ge&IbvW79ah35@XBGoJ`^qX)w+R1;9HD;&z8&_|q9{ z8&w8O3p~Jc#3sQT-ByzjV@X$XK}fU5DSz-)(2ci{Waqp%)VM!U@AZ~e8L}rpsF)`u z4$kWgYau>v@U7qcsU#xOL$!%MoJ-xUVV_9^oVU6#_TKfb_yfXB*CrG8%Gj0SZdlos z1dcgBH!4JxdK{i-3HmA73pw^au$vvGWMtvF?GCXwEUOvUEL0Q(`ZDG>8WB}jcUA+> z39oqXET=}8R5s6_Hd5s&;6Q+Kz5>C!0_%%-@3)s!)7zK@`xy`xEwKNU^y}-u%@JI% z0YD#{@S@X6ApyLLr1{d7`n#PdXo}LeAvw7IBAZ0|qDPyElfhhm=)4+zad7XP{V^6x zS(uvU>Yg%8C(V6oK)i_k*KygO#JD(1ee9!re-Q=VE64T5>8W|Y-Ii2kz^d6y^!gRp zpx3FE?uZcqTeO&+FZTWvOnZ(Yt(1qQPFwhOfYL$G(^E)!ps1-S$gOnVupw&&}7qF4B2k69Y){T5rwVvfS+y{eD(6VEOlAGW)0ahI#EWQR_y1cjgr- z=JLW>^}zkJMgGNkNiF=^a%;8v-iadZaPi*jlUv<`6ORjs5G{T|g3D^jz>#$3Jpwu7 zhV^-Cs}XX`UdK~X^}mt%AR`jvm=`7|pEw!{n&C~qtBdlpfX59lMao|q5G^O_frpEg?L*npgIvd}JZ-@N`tmpVxyAPTm1)v*_?w%LB z{LWOK<=Q=OVOexCwSHlEc#^3YPSzfZZwR8F?g5G2P4yo>vL4Q2xmvdLu@gip$Fdk9 zcro8x3Rq|fs7@l~yZrgnWICLp)4WUhOMf;m8#l{ww<4#!`{P}Q2&xcCeI}pS0d;+Z z{?Rb&nRY6k+RFgHg-?4Zc6w&Wx>Bn5A;1bK!L&i*cR2v(9eX{RfID=;4jjRU8p#!A z<)k5P4I=V*nOyPIm&dHNW2m)_&88iZH4jTDD}yfsgIdgkaMiv2eWaC2_Gdi3P1 z9(L8+0IU?A2NrS`+wsmaUS+GJ#nG!*fZow^G1)JnhXIM;>GV9_%Ap35Y{Cu$*Y55? zY158myJU6h|6mmVnPM267=rfA2$wFvj?a%>M-8fhF*(VWr57(h~GUSnD<`F z$z{bD^$Qj$M2&?ekTn--R)R~JNfju&9>yo>HJQqHhp@@C7@a3E6jb8lt%)MP=??I~ z{HrkUM52TD8qYjk;BdG1`M^o?Y*NUuSfyo-FZ36Ce2$+j<^f6vB#^R zfS$%YQQ*)z77-Um16v%2ZwGkKjn)Z*Oe7ear9EuWo-3=Ra~Gg$5kU$;%3`*vVXEE-NpQmXSq4Mm1^p%qrFTuLmOv# z^+G<|9)2`yeA3rxUCb6=rC3s;CA=sFBZ7Q|w?|;6%hJ$C@IYeXqcYdFfGA@jx;vB00a2mQ|;HGY^`;YuCv$gjHlywlpMGOevUU)?UqDN8}}GpZVg3 zrdaI|q?kRUA+!!DC3<72mEk^>H)p%bzVD>2q?Gp6Ie;dYiH=o@;$}I+qLHaliF<9v z<=L5E7U=wvEN230yvR(9lw79IU=g??*|4^opV9Qy#ivJigy#t?baRFpyS%mUIubZd zA1Sd$yp3+%4V@P}j(-#szkQqsZ}g2#5ELm^`glEsFSsx+Hk>ghkSqY^SY;@8LI~*(Q%a5zm!nq?e^=qp6F%(8J~Q^|jL{(7akik^B*neCN)D zAY9W#U^Qhoivl8I|1uK-oA8{k-tIK}$yZ`eey2(}a^0}G*bxh}FDAaV&$xVbyG!uY z{`A$XcyPzc5YI&L=2~!zkMh*7kZS+tsezCKkiZdZvLqx9Wxc^;2sKtB1v&Zn7k5P|1aU zZvF_PW#b29#f&WkF7~&gY(AOV&xV=T7!51F>^Q7a;;c6EGW4953hv}BrVIE#wwi;U znm%!Gmi$K|7dGR8n|X^=HQ{izcA9S2d;Zf?&BP0+4`p0X1}GgcPG1MM`Ti?56FNq3 zp*kuJO()kxEshnvJGdU5Mhe61Ag^{;8xc8(M~VHe(skP{sIuk~?v~s2$mKHLh)=Kh z@jl;WpxexBc%oyli-&0F4kp0`%P?lrT^WZD4n&st<$VAPI|)!Dg!k0YMy4*${6P?~J&+o3%b%x`T(YxvT<*p0ucdXpt!%z;LNjAl1+-&0I$p<<2f1bcS z=27g;KE=MazR%3x#B8>LYVJAYi|ROg9F|vB9DR2_*$wcUl=Yv}{?PrQ5$rmhH`(>Z z72{#UL2v@dbz|C^k}+3Nf&Bg<0Jef|4Xj{07hS#cBNx_kw=*rka!I8!MICc<>eDkP zw@@|{GOw!dm<>ej0~r$~=6{JdDq-Z=P! zIg|I$_tEc--U?(tB0nmm>$Dg3Nch03@3OA^dTM$mdJxgKg-s-mU2|NauDR!i`J=g4 zH|Lf6NN@B*7tEsZN4K6+ZE{HHeWNlep!DMP1ch4Z8$aeh<|!W1OlExI*hNh`>aAAw z?Tj_vi?$a->8kC~Hu4vZ=_)4&KV0PPZt~ewb(GXzBC+~@4$ZkZTj1iW-hidZKWg4x z2VJ>wW-g_wwG2_tH&zw@GXK)f9b4T8W*EQk>uRcYMNfr0tTl^A zijIOBEqcB$)g9GEC35VQP*WgfT5r!x8Q)QREbtJr)@ZOVR~tH2Vp)pn{iX_vCYDek zY$l#c_fruuF4cS8>WEy>n`vq#(e69plE9m1F|3FHwd6gO)v_C5D|{gBNTiD#nT zC?;XKz#(w;xyfRo?Dp{r<8Kg7_Aernu!)Tg8)oPCLyYi`b`lrmX`)Rxn)g>CUaLMJ z*qGzDb63~9QILp$5!xH8WP5^eq`xl5sTGOxZefV*qwmUjPKioON%8bx6($*ClmV}+ zU~j^GpuGL#rTno2r{}asRNt3?zyfg-!D)d?#I6zZsKUr7r7xERM?hunz%V1(=MfB% z2eW9F$xW|Sn^{6e5axrWW7nt|gOOj3l-a6~8enBIeZP!Yz)iXMN#Pa!(~FycuRS%h z>d3)ae)Nfh~y5_W)jM>3Tv$ydD0LkN#kQF_5A&Ag-V)rP=TDg4kO-z8rnz&~oQLHMXwq&LS$l{GcO z@&yyIJmaKvpTI|!!ANpJbNi%(dbZ1qqkCYY`x2O+oPU2=R0WYuGd!E0H}bv}W<4Qz zgf|;6aJ0D{RXqrMkbC^=+_*5Cn=`PEL}e2 zO5E@7YCrz|##HF3d`K46eS#SO9$QrBtf4SyjRa95yIK1COi)jKnJdLyF@JCPt#^6f zube|R`F2e!_@o{azO|OPn4q@MbcA%evVkUjSoF5t7N6?RQnjsQKmS6>zfFY+7xgzQ zf-Irm+>F$lxJA=oZ$dFPg74bYdM+d-tM-W4+7nIfdFQULwqux9nY0GQ*SHZ2kiSME zWbA7Rc*a@f)!*{;kAz6)x>eAjOBxgFU?dps~UZCm`0^LKa*h{s)kI9Up<|m;Gg3>$&@g7_2t| zRxJRr5xMNz&-SUd^N_f>2Eu%9qi*F~A1Pozeq@4N9E1bP-EO(M?t%<19H3vHCO1T{ zXf`SO{qqw}epMihp@@l8QJ+!UtS3X`DW;{Q=mLxASh-SrN6{7#lSE(Hf0UbYlww2M z)R0HQm=~-iRl?gX=a+79e2=j!oPm!>t{5*|*fKhqBi5s$R_Q)VFCi?`8M{hdLml}U z_3P{=aRz-!DQ%j3(}YAwJnN$5w7)KieWI#AR2Qsday!#@xY6u4HUXGF9pbNBQNQ@A z*2H>cyeR%~;tleBhz-!o9mW3=gO4oGcz-K?pv2xz z6OA!e%@v%T=-EqwfZ$*dgi;73?sY5z9?Ka-%awT8ld#b>E<7iUYl0Oq4#E;jravnG z=kGfSqkd6IF|C@=;=tcRd|DRb^(t0-Cd5lsc_-o{3&LnH=looPs4ZhaGhuxCBNfOna^!?jR|#aXp#stdRll2LgxaCUQM6P{Qox-%?uPRLTR=v)FY0zg-5ZRc z!=KKOlNG>2wGqkS1Yp_dsx21Hr1Mz4!+TOSh4o#lt6InQl*_!>=PRGt`5eHd`~2nY zCFE&HJ?J(V)}HC)nCo?s16#RTS*!-mt9`grzDp{>5*Pms^*m3h?<)5@TQ_lZx|Ww% z0>Gz=)DGA7m1-XJ416|RGkw*(fq2Dl{y$Itn?0%mk> z^&Da~@Gc_VzE|Y9-P@h|+o@G14nr5qhi1OLdb`xw591&G(C+l^I$_j)`}p%MU3V7) zuZlwMV9l%mlSM$!?WSMGt@7Q6+}q`@^Lkk7*s;^ymGP>0N&Qe9r8SWez@lUG+CLj&)SdBc11TT9rD!@N(dD7*|tp+mk3T(mc5j`~-6eY(@` z8^Tg%8&e18S3hOHtFJLm&`im1q)QxW+pC&+<%7>th)2iG&ge5UjC6X{L#L|Hurl=wWY9+ ztj~-dc<7Bqp=}IrHbND-hhR-rZ zRHLO0{?HF0TD^h74tws1<&Uxb*Yv;-AQNkkPwzUAj~yN_su}=}7X%VxaZAfjPtA2% z96=!TfOATT%Xux706yuij%BY4=Z=T7-?7WJ9ppadaNc`Ox6FfoLj2kjGTGswCNRY? zMf^-Fr6cNM6;qJ(t@3r$Tmq_}o)?_PcufpWYi)gTXt5Fq92>fo^NgjU?Nwm$X`&At z86a>a=TxNM`milYla2p|{-q-MAz8kvep&KQRxX8uNOdC?#z4m7k!`gV+_~?l3GyZD zg%YYxV!q9rMoDbd}#K& zbG1Kr;Os1QSRO5T-lPMhUoJ77Eh+Sn0nTHT*nnN{LTECqil(7LUjgTkiFycX32Ll_ z_bFkdnekk#__g)j`ng&&&~B{+;fP{J2k_FGCTWgN5uZ)w#B-6fiFIjC}z=bAiaus{ll2IfC*ksNHl4rrXJv@n~-62YoAv5pcA=M!0w&#KEIRiIcKBv)zY$K8*Ep* zFmPvkbv|_$!8c>A*9(+?b7fbDhuua_I4u)TQzt&Mk!(s_CGVJ|SDvO=ODjjZvbf)hy=4y!KlntVwPep>&TBJEiQrS7f_ zN0*r4mG%*FJXvqTPm;@h$E!Z?5kmLY(NAjL7n!UL)Y-S30ftlVkA2O@86HnpZBL6} zG-Z6%v%II{bTd5Jl%~ZrJqd2?ztQPEi}izK*g4r=9k-TuT}BeMNWhP#&&pQgD1~SJ zANAb5flhwSMzx2xuyh>b8o4`gN=;h<5h^#Da7!+>Mj~fOMzsFK%I_T z&v_s((#F@k&H*JL`0*;Zdby>?m!iAOsL*J)=kcrNW!CC{`Fk>wC5Pk->~qweXESST z^w(9du}KN9MI9~tC}{BHQ>JrxH4M&J44TxS$(s~tY{PEG+xF3?m35##hgyp6R}cGP z;)ifftgpW>^`*{wX`BVoL&$*rB^cW1R7fe~33sN>xzDsLvLNB)yAnugOS-yl&lpB( zN@Js9_~ZQ6C7M=+PAG1C(e^JkAqkv*W$=}tmS%w!w@SaQK1Pt5(Px+te~DV|Y)fp5 zHOL#yNNsWKY#(+#qUo^jTimf5e!)yC?h~>NECJ70n$7A__}rTtfB;e*rb%^K4a*Ib z?>@-=JH>`0nOLC;w%jiIgKsH&cKsm7#`oN6tZKZ6V*@iivS+?TnI~CiWEiTHGRLL4 zd(!Nig0eZ6YTF1*4L{=PCk+>L$%$LlbVl2!Ce5W&ZEZoxaJ4hc%p~= zooCyR&3+b;>_dTp($ve?Nd(fdTmZntxsS(@k9@3Q#2zzNa7K@#^JK|P$g0DNC%iG@ zKS^$%;LULQq@^^^LX7-$RvH~iZ6MoMq!QpS)UPNQLZe7pb$)grBQ{orK_!7~Ay@Z;IBINmVm{aon(^mx|QlWY;QB<#KD; z%1Qt!Q0XYISc&q2UHZ7-H-gmLVfU~4wu~>jM6$%68?j=PO|K=#CbK*hNW*HQ z+!=w4vru&GInjV^s0g7Kb3Q^m!bd5KuBMOi&V?Wh93yKcg0E7aVr4dJxV?+X&I=mL zIKiF?g&ABihTWJjtcdGW+6Otalv&j*Bt^E!FsqPCz63whM)G`3!a7@O6Z$%?=n^bzHSNNh?zGT=~XRFbLCCD*F?lhA2eMy*Zgj;t9bifTs=7p30|i8+An z&->5l!YNXz#sosb#g%EmFYR38u0n=$NGkG7Hfvhn6eKG1w_4=Jdl#dn{bp8;Q7N2; zB>YV7HkM9h&W~>wq=&$?yQP^eO5*>{S-mXeB2kdNu9Y5I$o{7KsxZw0144qG{js=tVhwR_6eW#HFT&F9EGZrm76 zr_IMUR(!RD!dj$`GE_Da)sF4^kh(Cp>H_TP?%i&55c1VBP#ivW!TVDM(xdTpEKXt; zdw={VKBE8y@kPwR>EYp3en&M&*Q(n*@Ns+{`1*(D{^zsnnik#eC#Yl>(0bYf-Un=4 zC%|^ndg|dDPLwve4;Ruvr)Y^7N3)mzW~OTf(m~kvz%)XMUx{b8Lv)AFbi0Pk}4bS~FHw1f~ z5a=PpaaMX*IM-~C+HaZfj1_9B@P2viZTZUImVkQtJ;xI?_gqQj)$~S_+fF}5y-1K5 zoj6fqgbi8WQW1w#f7bN#nlSphB%ox{xogtQ>L|Fx5ipbuS!bMR)O>Qpn{%bg`)2XH8Wz}1e|;Ts2kI*v5*z<-R#u!)KSbGX z0ROG`YEZ9M9Ioz@HgsMLScCe2f(~JxogLM8Q@pnxXFtUtsUBS=%kGmAzsx;DElR*8 z$KJZ&riKUUFd#J}H=a%S*pak3`e~i{+bp`<6 zx&sAF>jwKD<51-Ir13ylbLTf>55WmOP@58pdj+4Nn6z>7L~*N(e?&W`yvL3%DHJK~ z(}mEphj6&tYQ!kUwZ@<&$&9l=m?sCPIqNmqlpdrURnnHoycA(8@Ga-}qii5>dL0(GGTMOADd|R=$*LZ)3!fpw6CT}1H=pU| zqUOu59iSdzUpt?}E3qCi|9i=fy?RzKd01|Z(}&ekr)IK$k*T}BFt9ETrQJ~M@+yb- zBK9&)OTU`EQ(DU%xZ6NH-*aBqsQIxt6Eu=7@q4A9X6mR1770sg@%=E}%2@%`yIXp^ zlCj&(omXWEI69$ht~n6RR9=TGDYyz2jy zqW7i&chx&}_dk~7_Oj%Hs)t-_ja&!pKzvucF-7~d{EWpXo=2(kjeY{lQ?TxxmXcgX zL;oXjDXYG{Y5}8psIc49biw~;)23sL{itxsGbmz=46a1*FJ zNw$s2q1|(jF6JStti#(Y8_RCgXSkF5RH3N`W z*1rFv3gi>~c>R|4ws%X!|2{o;L}+Q7;_sm0RELDHZ21hZN%O0LaH=GUVCaieOVB0e z3s$TJ@cs^cE7teGjwfkv0qlmLsW0j==A#qU-}_M_pT?ZGMWH&EzH+l zsrMBIA~{n1ZpfX!bNg;;Uh1utbeD*APOhpoOFS%b5gM)-*ZRRy($em)!Qi#Pu zTE$M$DebPZgyvMUy*x-wHXiP3dYX)?%fKE>4-$>1FL8I=dk>`R zvq#5HaX*Bf6YOZRdyiKE+BoS3z#qNx#AX4_nG)Bo5R3EOXcK?CyV@0ad#+Rogzqby zZgc7K=|@I$E?vT4b&q$yXWW4KS;X$_>XUbY$8n^Nqh3iuqZ-RW5NCA_2V^eAB1n58}EWoVnJ$%#R+F)HIoP<#F znec6#k*MgCt^{m`A?so|K1mt5VyB)ykmq;lu_cstalRIEZ^1d>`DurAlL7Q#v9usz zhX81EvtLL2!3|R1HV-g-Ro`!HrLPtkuwPyyuJN&*y(`LL^?@uc0Q{VDCe4oEnKpV? z)XL=ZrAb)!qf!UC9z17^Op%md;=JL}tS`TtgvSRN@%PoSPl?}M?4vPbv#$4u z8hN}WT*~>rf|2i)ABRg4Cjp@F3Fp?sUt+yxoT#E4(zTsEP&WeXmc8FT^w77LI;# z2L75xc@d;>)U_+?wbbu4C@1E6e}!Vr=B!mjeyCq~F84YquXiS934|xzJBTNjM4SDF zijZZFv)faMDygSD9fN@}QtvkvuW2OlB*VUi#F=o~%E`fe3mAKOId-1O86!x;KN5?iOcm8Kn>XecH$Gcf{Y!(lBk=v&Qyyqiv0R`FDtm~6? ztM-qRx%b+LOXRxExiiB3hyLC*V93gO$SvSv0D{gruYAxQFo*5C*9<$K-#B&ld33U2 zr_Y(VPl3aCd;;3YTOmdcyFXH4g)L-GoOgY!h7UKKk6w4jfFo;8uayy4M-M<+b?RW2M2~ye~UHs-0!i7$u*S*_@HD zDSz&zOZmx`wp1oo&(ppU!Xj`j)vB!0VCL->>8J4otNb=d*OFtb<0oIeEHhW33w*X+ z&tsMHF7a(_D-&H&kkaJs;%%ZpW7x6SBCPv10{}@GD)%3P!umvD&1pRc!B2cS$2P6& zv>32m2e1B%E!RB!@Mw9w4CVG)>*H+Ph2qK?-}S-Z0`CEp%XjK3%q;*fFf42clDG|DQyM1*9jdmZ@b;Enjjz%*htb$Yz0WZ+S3IL z*k=WWggth4`CoOzGD5ms=boZST9anLZ9brX{SLBs@<58I<@6W?+pKw=^t;OF9`2;k z^;^>z_T%yP1#NaWwk^r^nz#oTaMaxfU$`=!dbn66uW~uO+6=>P;&fMZx~-aQZZQJo zm}h}O4bw&J0r}+J0Fy&QCZl8P`SQsM=zS0jq|{RapJ6<92!JtCI_MrPuU5A|zyQaS z-PJ}JrDkyXF{i&tO?6)m#0`ku}=*KK8kcsS-sYo&0Lfsv7;iT}kpWEfH`66yu` zAihiyU^cfXe{!QVtrhb?3hFoVnsv^FEdi6YFK! z$rQV}Jw;Dzqck^tL(LYKY91OS?2IP1H8Q6uTZ^)LHf4O=AiZuc_S&ip#&)0ODre9B zi=*}mR3I^`W=&n?rTJ!itav%NxM(yu82FYfktRj_xI~et+Pj+~i@SDx8bk^CN)!+W z%P2L5Ftj%o%Br`P4$)gMu7uSWkdNW}A(0eUz?t7l#eST1U1qfx&=9#th*!G(2qICZ zyKZ6Xd9z9rB#|~9N>#*;l8>681pP9c7ahUDQOl69M?GmPX?+oGow$3>I}OQRXw{-$ zZQ3!~%R6$BWE_vsJdm$(If^4}YBY;~ANN{9ABluh6kU&)X;PzQdUt;la)~5Agy_^- zVDz18y-TgKi4xRXsFXpoqTVxODqPf|D$ljQTZNmNWHjs8D4W*jzmR@SJ zf%Ml-p)ai69vU}OMf7OiamDlD%F8in_V1!ZI8tco2FqRqPusKCZ%r>a;Mq}H`CrNFESM8WRz8Z<0^iwessQjHCl*$5$nl$q%lUhyIL(@DDMFUro%FsAxm zePCPkSk=-TWpSq!t1Pd&f?lu)n~!FGO@YyC^6z?*bwxj5SUxm;uUZ8|eNXy|7fg%T z!QcTkxrly_E2k_Zu`?5CyXOfvR*a_2XYul+{9{v%3YtREU)z$syfZrs zlAzK>_fq*5lIS3>DrS4IZjWc7JRZY?yROv<^O3;|A>t?`l;6bp_Vk z;zzqYXCyxu~ZP$ptd_raj}Rn zB*#!C1bolKXRB11q#am{z08{H<|oAgvACEgujY_U$uy)6TJ}De<$!=7$vk0d8O4qj zJn~xoP%Fb3RySK)ZOt$AC( zk0~yg3$)%+7{~HSFUVH`PK5H_M=Y8TuCIq|AIV>elcIO{$9%TTWz4D9LG7@k8b@?h zXD`bM`tgp|{vD?zwT`<&(NcK~dQcgOAXP**#kz8$jV|seEi!S4yM_OM>Vr?XtoZ54 zmXi8_7e86d0^TovQ0GPrnjpAwHfpyNF6PB}c=> zWv)JKVV{)?@kUWzWgjDl!qyChKjx4X&>JiA(T&QzzJIl>gTr9nQbJg`Ca2JNwrNcG zPZs~HtoR!9Tk%h2zIg+C?VI{NLG4TbJvPPVlERO@}dmu z@rIO3d{JKtWyk~8XkcfW9aSpPNYc~{CZW{C2V3A5MsJ5mI`i<3c&106`B;1IH0do2 zcM7>h5#^=!_p(mEW$^wU{(fh=PBHQyh4RTX7Gx0hl=4uGA_{!NhVlfpZ)VU^g>~@S zH*!P#d>dsQFkn@`^Vg<`9rSQ)Jb?|gY1avA@&fDJ(Ql?>DCMcv#UkAi^FsxZ(iYzE zzML9{9^4F5Jtzo%%*fn_vTUXxDCJlMES&v(wy$hQ6s|LyFnRPQa2+4~{cn!&m;RQH zq)d#%?`ngW-H==XiulFGDszz>sF*}yYSqrVLK4)6xxz)Lx>S=lU9!i7(~_Xp9SVmKaes&MJ!=dpu?mq=k<9#5jsKdJfBW2^#5h2D&nQ+n*3c*l)~l0QzrGs#DmGTaQ%1BT8Vr~|xdy*?AKDF$0o z8|PA%dGl|X9zR$F`~7(hnyd%~h(6*=2EMY~y6y$RHF6njzva@s`RhI&$)B6-)&{E( zZh|f9^J#Ac_*q8FN?3U2_rrd%DJ$u=Re%eG*_ggJDm1-P9<9ZBHN2G>6{|jKt0c7M z!4Z_{erkNpn@l1H@tWXY{KNL#F~hTki*F{nlM=8z;KYNU*f$kqnA44lEBC7B*Kk5N zT^jera|7IEw&D+`Lq0~3rt^088c%}UME2(^DUB7L)8t*$NZ&m#4Gv+yCF z*=Ji<_{|x2sKQP92-m95*&iyiPH&$u7jbJz03ov$T27&)jdKd+#U_GDz)AFHbP1x7 z+#84LXukLdZ>m0NF46}*z%V&MBM!--myLslH12HnP;x`1UfS?lxLn3N3v)*;d1TzM z{*(yY*;jXjI4fPAhJq$17yRAjnmugDQ$8H5(QO0bA}Kl*_y4K2Je|i4)s#cEmKwCT zmN^kmDK~HjON}?j zXIE~gJXk*LfEg(ND+xQfqplS*<2vH@OcEZ;a!*|N%umP3l|G9n$-6di zn)bi&2Z???5z(o;xz)Z~#_%_#Jh`b}7X8W(MKM7&^Urk1G!2tb^l&yG&?Mt^9Nj{t zdz7Hj)P$WdFT|#zSp*U{mv28?D%e1#`9m+9EjT#a0elci6C3U7el*Xlq^1jOcv-vu zaKP2`hsU{M_8MV#rw_4EF;2J!Hz-*0_$wsGoM{vn^cnc3gzh{^Mioe%dpto+$#u0Z zjrjm^mEXQuszO{T+yj-ym(zAaHG`p&z40LoK6?a=?xBUknFe1J7~BewHWmnRQtJy-1KM z`tTWJ?3d8{8@CrlXBh^e+^C_Z4wMNcMW_k(iG5ttZ_9~!<(@~?&+nuYZZB#$yi1{; zyXO8fIur7e;yj{X*!TTc$x=?NJ-=Z|qd((mClbnvX_YKjYGEis@=Hl{Swfh5G`_0? zUT@#$Z?d-T4Ee!SC&(&qILb&k3|vM*CrPnFN-FbbKVZ*XZge^aFx(W z)qrE3{EvYA7v5IKe0^=oBo=Isu>OjRs8yEyWPk`hZp)JS`I~L6`+I5AnXF=l9DN;R z=#jGi?`#;Yja@@qtm(&7iX#Evimv`taYRF{#r0Uu!L{0~vBK zsf5BvxpE_Pg_^hva2cNSeoJUBe3o3r#35SfkG@PqW&|EDO3c6#bchY!38F_ztJ$W> zVy_*_#ac&QogAs*=v#{&A!$Z0qWNq<_b5+K*3ApD*zVB}n+bN)mO@`mM{Zjw^aTN> z>iKb}cqFPaNh!N4KHm)m$E>g-jH)j%igyRcs@{oxh)W!%9WW$g{D~+2zm?72tMVWz z#)cF@tejwo%x5jcZv#3m-g?)yT;cI$F~N!kBBrIb{5K^{h`Z08xF5 zcKw*Ar;SLS!m|c}ha8wMwRf!mnmnSWn#gzvFTK9wC#twf(C)IrgIejL1QkQImT!Jw znVm;{X1Z}Ut@t0%MZh%0Eg#YKjeQ4&w8n-^jvMh>p_iJ7VB~}Rv5AJi)N`tl=hs@S z;z=Ws7%s#)nnL07l#(RhHB=Y=%b_%fo=y* zPmYq`T^IJ{e&PP@7u=^h?!X*Q`ojb78r~TAf6E*Wcy{&ym8i;0kSR>V7Ba=&@GV`<85~P-ASY z62T`PywxWS^d6$=%aBj|IL&(bPV?@e)SLWjkJlI>DdPv!poyO&yZwV z+h;vsMn#uWEQf6A_e{=805E)@vb-Yx(T zpQWbIm!LM8D{xQW=54wo71Qi$e+d0I= z3;Yvqh0rGvtdqg{XEbHYWyokaFH8qXq5*6x5sG)Hu`MSmnbAmItDqlbnL-$WO;eOA zQ<7q4jTHzhB$qk*o;g$+wx3td@TXxJA;vrSes=aIlE-^?F)b1-R1<~LTa3#P8q&D- zro}^fhNA26sS(x^^?KHrpR6XFooe(}9z??7ZuNq`(^AC#IZ_?sE=vq|&ai8`%(k5j z!`$1Z?bjXC-q#wYGtbm_@C7e}ZC7L3d~&+R&q)6}!8~bvJKTYe(g$=pMda6L{fKcn z^{_MPS45E=XqA2L%O(&5L`g)|U~dZ)8Ej{AEOhnH&*-QpNpRN$hG&3dETP=;=)$N9 zj$>tM{4W*RBv{bj@Cm+>Jo^qVzt7h2B7alOEwHVR);`;C=Nj^7GO<@jfCaapsAkVc zmV^>t(p@Wg$r9+AcQ7!p;-a+6c!*YX2~3pAylU6E-0u&Tdv}6@9vevAQ7<%Df?R03 zSJVIwlowDp8w_KQz;X0BXrm~pwZa@wErZR@hJeelLd^q1)wE9wutFtP{BAdv(f$8q z858-vft3yA&rc8})u%`^ChPN?IV5qgLyi~Nkk4YwNaq_hzS`LF80_+Q9QgG|+m=|< z9=@DQjFLh24wM{m-TjoabiTYiQClWzGE9xL&aLumpplP!&Gw6=#fJG+wfg_qln`Oy zT;xw>vt}NK@&)ta{k05{ofeu9T5Exr!$ecKJ`1-rU9M zFM{hguw7Kinbm?X1EpvZwsw;~CIxf5J1wZk_9V2k;%ds;i{fl16MO_O4PK~1;D*^;;oZ!g_UC^Zw-jP)#Pb8z(3j4a+9{U5l&gT_i!89J+4~sVH#(Ud%hwVmm@@=+mM=b@%P>WUY#n z$Bi8ag?H$JVeEOO^T3AP#k&*1WnU10zkKTt^4gBX7hq7UD+`LF!8FuxbCfL|5SE>A zU~8qj)ib&7Pc7SPkK81~;LSNjuF!7l5nRZ}%j|QWGg%~{e0j_B+BViebHY;0S}Qi| zm>rXZdES¥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