From e3303c80942106677f2b3239534949b301d950ee Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Tue, 23 Dec 2025 16:02:52 -0500 Subject: [PATCH 1/4] refactor(tests): hatch-ify and correct imports in tests Should fix: #281 --- README.md | 28 +++++++++++++++++--------- pyproject.toml | 3 +++ tests/run.py => run_tests.py | 38 ++++++++++++++++++------------------ tests/test_align_cli.py | 4 ++-- tests/test_anchors.py | 2 +- tests/test_api.py | 4 ++-- tests/test_audio.py | 2 +- tests/test_dna_text.py | 2 +- tests/test_force_align.py | 2 +- tests/test_g2p_cli.py | 6 +++--- tests/test_make_xml_cli.py | 2 +- tests/test_misc.py | 4 ++-- tests/test_package_urls.py | 2 +- tests/test_silence.py | 2 +- tests/test_smil.py | 2 +- tests/test_tokenize_cli.py | 2 +- tests/test_web_api.py | 2 +- 17 files changed, 60 insertions(+), 47 deletions(-) rename tests/run.py => run_tests.py (81%) diff --git a/README.md b/README.md index 1283f836..a717b40a 100644 --- a/README.md +++ b/README.md @@ -132,25 +132,35 @@ To install the latest published version on PyPI: pip install readalongs -To install the current development version, clone the repo and pip install it -locally: +To install the current development version, clone the repo and pip +install it locally, either with [hatch](https://hatch.pypa.io) (this +will open a new shell with the package installed inside it): -```sh -$ git clone https://github.com/ReadAlongs/Studio.git -$ cd Studio -$ pip install -e . -``` + git clone https://github.com/ReadAlongs/Studio.git + cd Studio + hatch shell + +or directly with `pip` (you should be using a [virtual +environment](https://docs.python.org/3/library/venv.html) though!): + + git clone https://github.com/ReadAlongs/Studio.git + cd Studio + pip install -e . ### Verifying your installation Run `readalongs -h` to confirm that installation was successful. If you installed the current development version with Git, you can also run the -full test suite (requires installing the dev dependencies): +full test suite, either with `hatch`: + + hatch test + +or test the installed version (requires installing dev dependencies): pip install 'readalongs[dev]' # if you installed from PyPI, or pip install -e '.[dev]' # if you installed from a local clone - python tests/run.py dev + python run_tests.py dev And you can download our [open samples on GitHub](https://github.com/ReadAlongs/OpenSamples) to run your first alignments. diff --git a/pyproject.toml b/pyproject.toml index 426d8316..d5429071 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,6 +113,9 @@ include = [ [tool.hatch.env] requires = [ "hatch-pip-compile" ] +[tool.hatch.envs.hatch-test] +features = [ "dev" ] + [tool.hatch.envs.prod] features = [ "heroku" ] type = "pip-compile" diff --git a/tests/run.py b/run_tests.py similarity index 81% rename from tests/run.py rename to run_tests.py index 701fb668..577aefd0 100755 --- a/tests/run.py +++ b/run_tests.py @@ -19,25 +19,25 @@ import sys from unittest import TestLoader, TestSuite, TextTestRunner -from test_align_cli import TestAlignCli -from test_anchors import TestAnchors -from test_api import TestAlignApi -from test_audio import TestAudio -from test_config import TestConfig -from test_dna_text import TestDNAText -from test_dna_utils import TestDNAUtils -from test_dtd import TestDTD -from test_force_align import TestForceAlignment, TestXHTML -from test_g2p_cli import TestG2pCli -from test_make_xml_cli import TestMakeXMLCli -from test_misc import TestMisc -from test_package_urls import TestPackageURLs -from test_silence import TestSilence -from test_smil import TestSmilUtilities -from test_temp_file import TestTempFile -from test_tokenize_cli import TestTokenizeCli -from test_tokenize_xml import TestTokenizer -from test_web_api import TestWebApi +from tests.test_align_cli import TestAlignCli +from tests.test_anchors import TestAnchors +from tests.test_api import TestAlignApi +from tests.test_audio import TestAudio +from tests.test_config import TestConfig +from tests.test_dna_text import TestDNAText +from tests.test_dna_utils import TestDNAUtils +from tests.test_dtd import TestDTD +from tests.test_force_align import TestForceAlignment, TestXHTML +from tests.test_g2p_cli import TestG2pCli +from tests.test_make_xml_cli import TestMakeXMLCli +from tests.test_misc import TestMisc +from tests.test_package_urls import TestPackageURLs +from tests.test_silence import TestSilence +from tests.test_smil import TestSmilUtilities +from tests.test_temp_file import TestTempFile +from tests.test_tokenize_cli import TestTokenizeCli +from tests.test_tokenize_xml import TestTokenizer +from tests.test_web_api import TestWebApi from readalongs.log import LOGGER diff --git a/tests/test_align_cli.py b/tests/test_align_cli.py index 349b25bd..be783751 100755 --- a/tests/test_align_cli.py +++ b/tests/test_align_cli.py @@ -11,9 +11,9 @@ from pathlib import Path from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from lxml.html import fromstring -from sound_swallower_stub import SoundSwallowerStub +from .sound_swallower_stub import SoundSwallowerStub from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.cli import align, langs diff --git a/tests/test_anchors.py b/tests/test_anchors.py index be7045c4..90a9071f 100755 --- a/tests/test_anchors.py +++ b/tests/test_anchors.py @@ -7,7 +7,7 @@ from io import StringIO from unittest import main -from basic_test_case import BasicTestCase, silence_c_stderr +from .basic_test_case import BasicTestCase, silence_c_stderr from readalongs.align import align_audio from readalongs.log import LOGGER diff --git a/tests/test_api.py b/tests/test_api.py index 18353367..b8172642 100755 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -11,8 +11,8 @@ from unittest import main import click -from basic_test_case import BasicTestCase, silence_logs -from sound_swallower_stub import SoundSwallowerStub +from .basic_test_case import BasicTestCase, silence_logs +from .sound_swallower_stub import SoundSwallowerStub from readalongs import api from readalongs.log import LOGGER diff --git a/tests/test_audio.py b/tests/test_audio.py index 5ab69359..1534c29b 100755 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -7,7 +7,7 @@ from subprocess import run from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from readalongs.audio_utils import ( extract_section, diff --git a/tests/test_dna_text.py b/tests/test_dna_text.py index e91b64b0..f8e5598a 100755 --- a/tests/test_dna_text.py +++ b/tests/test_dna_text.py @@ -6,7 +6,7 @@ from io import StringIO from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from lxml import etree from readalongs.text import tokenize_xml diff --git a/tests/test_force_align.py b/tests/test_force_align.py index 37b0cb2c..0bc2439e 100755 --- a/tests/test_force_align.py +++ b/tests/test_force_align.py @@ -12,7 +12,7 @@ from io import StringIO from tempfile import TemporaryDirectory -from basic_test_case import BasicTestCase, silence_c_stderr +from .basic_test_case import BasicTestCase, silence_c_stderr from lxml import etree from soundswallower import get_model_path diff --git a/tests/test_g2p_cli.py b/tests/test_g2p_cli.py index bf9d800e..c81f89cc 100755 --- a/tests/test_g2p_cli.py +++ b/tests/test_g2p_cli.py @@ -8,10 +8,10 @@ from io import StringIO from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from lxml import etree -from sound_swallower_stub import SoundSwallowerStub -from test_make_xml_cli import updateFormatVersion, updateStudioVersion +from .sound_swallower_stub import SoundSwallowerStub +from .test_make_xml_cli import updateFormatVersion, updateStudioVersion from readalongs.align import align_audio from readalongs.cli import align, g2p, make_xml, tokenize diff --git a/tests/test_make_xml_cli.py b/tests/test_make_xml_cli.py index c14df446..bab9a724 100755 --- a/tests/test_make_xml_cli.py +++ b/tests/test_make_xml_cli.py @@ -8,7 +8,7 @@ from shutil import copyfile from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase # from readalongs.log import LOGGER from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION diff --git a/tests/test_misc.py b/tests/test_misc.py index 85df66d6..1b6264a7 100755 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -8,10 +8,10 @@ from unittest import main import click -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from lxml import etree from pep440 import is_canonical -from test_dna_utils import segments_from_pairs +from .test_dna_utils import segments_from_pairs from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.align import split_silences diff --git a/tests/test_package_urls.py b/tests/test_package_urls.py index 7fd41f22..31ae597e 100755 --- a/tests/test_package_urls.py +++ b/tests/test_package_urls.py @@ -3,7 +3,7 @@ from unittest import main import requests -from basic_test_case import BasicTestCase, silence_logs +from .basic_test_case import BasicTestCase, silence_logs from readalongs.text.make_package import ( FONTS_BUNDLE_URL, diff --git a/tests/test_silence.py b/tests/test_silence.py index fb23d81a..0dd24c45 100755 --- a/tests/test_silence.py +++ b/tests/test_silence.py @@ -5,7 +5,7 @@ import os from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from pydub import AudioSegment from readalongs.cli import align diff --git a/tests/test_smil.py b/tests/test_smil.py index 4217b7c3..c93ae0f6 100644 --- a/tests/test_smil.py +++ b/tests/test_smil.py @@ -7,7 +7,7 @@ from textwrap import dedent from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from readalongs.text.make_smil import make_smil, parse_smil diff --git a/tests/test_tokenize_cli.py b/tests/test_tokenize_cli.py index 31e20c36..1396758d 100755 --- a/tests/test_tokenize_cli.py +++ b/tests/test_tokenize_cli.py @@ -6,7 +6,7 @@ import os from unittest import main -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from readalongs.cli import make_xml, tokenize diff --git a/tests/test_web_api.py b/tests/test_web_api.py index c8ba4c20..c98ec075 100755 --- a/tests/test_web_api.py +++ b/tests/test_web_api.py @@ -10,7 +10,7 @@ from unittest import main from unittest.mock import patch -from basic_test_case import BasicTestCase +from .basic_test_case import BasicTestCase from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.log import LOGGER From e400c52a20ea9c79c591a4f83ff3b4ba6ca536ec Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Tue, 23 Dec 2025 16:10:58 -0500 Subject: [PATCH 2/4] fix(ci): fix tests in ci --- .github/workflows/matrix-tests.yml | 2 +- .github/workflows/tests.yml | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/matrix-tests.yml b/.github/workflows/matrix-tests.yml index cdd982c0..a0747040 100644 --- a/.github/workflows/matrix-tests.yml +++ b/.github/workflows/matrix-tests.yml @@ -29,4 +29,4 @@ jobs: run: | pip install -e .[all] - name: Run tests - run: cd tests && python run.py prod + run: python run_tests.py prod diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cbab18bf..ffaaa9be 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,9 +32,8 @@ jobs: - name: Run tests run: | - cd tests - coverage run --parallel-mode run.py prod - DEVELOPMENT=1 coverage run --parallel-mode test_web_api.py + coverage run --parallel-mode run_tests.py prod + DEVELOPMENT=1 coverage run --parallel-mode -m tests.test_web_api coverage combine coverage xml @@ -109,7 +108,7 @@ jobs: pip install -e .[all] - name: Run tests on Windows - run: cd tests && python run.py prod + run: python run_tests.py prod - name: Make sure the CLI outputs utf8 on Windows # Note: we're checking something CLI specific, from a prompt, so we don't want to run From 75e543f84744263ac13244e431fcfd05256554e0 Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Tue, 23 Dec 2025 16:14:02 -0500 Subject: [PATCH 3/4] fix(ci): fix ci some more --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ffaaa9be..d79bd6c9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,7 +37,7 @@ jobs: coverage combine coverage xml - - run: cd tests && coverage report + - run: coverage report - name: Make sure pre-commit hooks pass uses: pre-commit/action@v3.0.1 @@ -155,4 +155,4 @@ jobs: run: pip install httpx - name: unit test the web API - run: python tests/test_web_api.py + run: python -m tests.test_web_api From 7729a0943c45303f972eabafc37ab673fa42f126 Mon Sep 17 00:00:00 2001 From: David Huggins-Daines Date: Tue, 23 Dec 2025 16:20:19 -0500 Subject: [PATCH 4/4] fix: correct pre-commit issues --- run_tests.py | 3 +-- tests/test_align_cli.py | 9 ++++++--- tests/test_anchors.py | 4 ++-- tests/test_api.py | 5 +++-- tests/test_audio.py | 4 ++-- tests/test_dna_text.py | 3 ++- tests/test_force_align.py | 3 ++- tests/test_g2p_cli.py | 7 ++++--- tests/test_make_xml_cli.py | 4 ++-- tests/test_misc.py | 5 +++-- tests/test_package_urls.py | 3 ++- tests/test_silence.py | 3 ++- tests/test_smil.py | 4 ++-- tests/test_tokenize_cli.py | 4 ++-- tests/test_web_api.py | 4 ++-- 15 files changed, 37 insertions(+), 28 deletions(-) diff --git a/run_tests.py b/run_tests.py index 577aefd0..37b021a9 100755 --- a/run_tests.py +++ b/run_tests.py @@ -19,6 +19,7 @@ import sys from unittest import TestLoader, TestSuite, TextTestRunner +from readalongs.log import LOGGER from tests.test_align_cli import TestAlignCli from tests.test_anchors import TestAnchors from tests.test_api import TestAlignApi @@ -39,8 +40,6 @@ from tests.test_tokenize_xml import TestTokenizer from tests.test_web_api import TestWebApi -from readalongs.log import LOGGER - LOADER = TestLoader() e2e_tests = [ diff --git a/tests/test_align_cli.py b/tests/test_align_cli.py index be783751..f655e97a 100755 --- a/tests/test_align_cli.py +++ b/tests/test_align_cli.py @@ -11,13 +11,14 @@ from pathlib import Path from unittest import main -from .basic_test_case import BasicTestCase from lxml.html import fromstring -from .sound_swallower_stub import SoundSwallowerStub from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.cli import align, langs +from .basic_test_case import BasicTestCase +from .sound_swallower_stub import SoundSwallowerStub + def write_file(filename: str, file_contents: str) -> str: """Write file_contents to file filename, and return its name (filename)""" @@ -617,7 +618,9 @@ def slurp_text(filename, encoding): with open(filename, "r", encoding=encoding) as f: return f.read() - base_file = write_file(self.tempdir / "add-bom-input.txt", "Random Text été") + base_file = write_file( + str(self.tempdir / "add-bom-input.txt"), "Random Text été" + ) bom_file = self.add_bom(base_file) self.assertEqual( slurp_text(base_file, "utf-8"), slurp_text(bom_file, "utf-8-sig") diff --git a/tests/test_anchors.py b/tests/test_anchors.py index 90a9071f..dd076ee5 100755 --- a/tests/test_anchors.py +++ b/tests/test_anchors.py @@ -7,11 +7,11 @@ from io import StringIO from unittest import main -from .basic_test_case import BasicTestCase, silence_c_stderr - from readalongs.align import align_audio from readalongs.log import LOGGER +from .basic_test_case import BasicTestCase, silence_c_stderr + class TestAnchors(BasicTestCase): """Unit testing for the anchors functionality in readalongs align""" diff --git a/tests/test_api.py b/tests/test_api.py index b8172642..93a5961d 100755 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -11,12 +11,13 @@ from unittest import main import click -from .basic_test_case import BasicTestCase, silence_logs -from .sound_swallower_stub import SoundSwallowerStub from readalongs import api from readalongs.log import LOGGER +from .basic_test_case import BasicTestCase, silence_logs +from .sound_swallower_stub import SoundSwallowerStub + class TestAlignApi(BasicTestCase): """Test suite for the API way to call align()""" diff --git a/tests/test_audio.py b/tests/test_audio.py index 1534c29b..71331c4e 100755 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -7,8 +7,6 @@ from subprocess import run from unittest import main -from .basic_test_case import BasicTestCase - from readalongs.audio_utils import ( extract_section, join_section, @@ -19,6 +17,8 @@ ) from readalongs.log import LOGGER +from .basic_test_case import BasicTestCase + class TestAudio(BasicTestCase): """Test suite for various audio contents handling methods""" diff --git a/tests/test_dna_text.py b/tests/test_dna_text.py index f8e5598a..c178f34d 100755 --- a/tests/test_dna_text.py +++ b/tests/test_dna_text.py @@ -6,13 +6,14 @@ from io import StringIO from unittest import main -from .basic_test_case import BasicTestCase from lxml import etree from readalongs.text import tokenize_xml from readalongs.text.add_ids_to_xml import add_ids from readalongs.text.util import parse_xml +from .basic_test_case import BasicTestCase + class TestDNAText(BasicTestCase): """Test handling of DNA text in tokenization""" diff --git a/tests/test_force_align.py b/tests/test_force_align.py index 0bc2439e..55f8f374 100755 --- a/tests/test_force_align.py +++ b/tests/test_force_align.py @@ -12,7 +12,6 @@ from io import StringIO from tempfile import TemporaryDirectory -from .basic_test_case import BasicTestCase, silence_c_stderr from lxml import etree from soundswallower import get_model_path @@ -26,6 +25,8 @@ from readalongs.portable_tempfile import PortableNamedTemporaryFile from readalongs.text.util import load_txt, load_xml, save_xml +from .basic_test_case import BasicTestCase, silence_c_stderr + class TestForceAlignment(BasicTestCase): """Unit testing suite for forced-alignment with SoundSwallower""" diff --git a/tests/test_g2p_cli.py b/tests/test_g2p_cli.py index c81f89cc..27948f20 100755 --- a/tests/test_g2p_cli.py +++ b/tests/test_g2p_cli.py @@ -8,10 +8,7 @@ from io import StringIO from unittest import main -from .basic_test_case import BasicTestCase from lxml import etree -from .sound_swallower_stub import SoundSwallowerStub -from .test_make_xml_cli import updateFormatVersion, updateStudioVersion from readalongs.align import align_audio from readalongs.cli import align, g2p, make_xml, tokenize @@ -19,6 +16,10 @@ from readalongs.text.convert_xml import convert_xml from readalongs.text.util import parse_xml +from .basic_test_case import BasicTestCase +from .sound_swallower_stub import SoundSwallowerStub +from .test_make_xml_cli import updateFormatVersion, updateStudioVersion + def run_convert_xml(input_string): """wrap convert_xml to make unit testing easier""" diff --git a/tests/test_make_xml_cli.py b/tests/test_make_xml_cli.py index bab9a724..dbfce406 100755 --- a/tests/test_make_xml_cli.py +++ b/tests/test_make_xml_cli.py @@ -8,13 +8,13 @@ from shutil import copyfile from unittest import main -from .basic_test_case import BasicTestCase - # from readalongs.log import LOGGER from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.align_utils import create_input_ras, create_ras_from_text from readalongs.cli import align, make_xml +from .basic_test_case import BasicTestCase + def updateFormatVersion(input): return input.replace("{{format_version}}", READALONG_FILE_FORMAT_VERSION) diff --git a/tests/test_misc.py b/tests/test_misc.py index 1b6264a7..f9061fd9 100755 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -8,10 +8,8 @@ from unittest import main import click -from .basic_test_case import BasicTestCase from lxml import etree from pep440 import is_canonical -from .test_dna_utils import segments_from_pairs from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.align import split_silences @@ -29,6 +27,9 @@ ) from readalongs.util import JoinerCallbackForClick +from .basic_test_case import BasicTestCase +from .test_dna_utils import segments_from_pairs + class TestMisc(BasicTestCase): """Testing miscellaneous stuff""" diff --git a/tests/test_package_urls.py b/tests/test_package_urls.py index 31ae597e..6cd53e2a 100755 --- a/tests/test_package_urls.py +++ b/tests/test_package_urls.py @@ -3,7 +3,6 @@ from unittest import main import requests -from .basic_test_case import BasicTestCase, silence_logs from readalongs.text.make_package import ( FONTS_BUNDLE_URL, @@ -11,6 +10,8 @@ fetch_bundle_file, ) +from .basic_test_case import BasicTestCase, silence_logs + class TestPackageURLs(BasicTestCase): def test_urls(self): diff --git a/tests/test_silence.py b/tests/test_silence.py index 0dd24c45..3d86ec60 100755 --- a/tests/test_silence.py +++ b/tests/test_silence.py @@ -5,12 +5,13 @@ import os from unittest import main -from .basic_test_case import BasicTestCase from pydub import AudioSegment from readalongs.cli import align from readalongs.text.util import load_xml +from .basic_test_case import BasicTestCase + class TestSilence(BasicTestCase): """Test suite for inserting silences into a readalong""" diff --git a/tests/test_smil.py b/tests/test_smil.py index c93ae0f6..7ee079fb 100644 --- a/tests/test_smil.py +++ b/tests/test_smil.py @@ -7,10 +7,10 @@ from textwrap import dedent from unittest import main -from .basic_test_case import BasicTestCase - from readalongs.text.make_smil import make_smil, parse_smil +from .basic_test_case import BasicTestCase + class TestSmilUtilities(BasicTestCase): """Unit test suite for the smil writing and parsing utilities""" diff --git a/tests/test_tokenize_cli.py b/tests/test_tokenize_cli.py index 1396758d..06ae0fa7 100755 --- a/tests/test_tokenize_cli.py +++ b/tests/test_tokenize_cli.py @@ -6,10 +6,10 @@ import os from unittest import main -from .basic_test_case import BasicTestCase - from readalongs.cli import make_xml, tokenize +from .basic_test_case import BasicTestCase + # from readalongs.log import LOGGER diff --git a/tests/test_web_api.py b/tests/test_web_api.py index c98ec075..8287826d 100755 --- a/tests/test_web_api.py +++ b/tests/test_web_api.py @@ -10,8 +10,6 @@ from unittest import main from unittest.mock import patch -from .basic_test_case import BasicTestCase - from readalongs._version import READALONG_FILE_FORMAT_VERSION, VERSION from readalongs.log import LOGGER from readalongs.text.add_ids_to_xml import add_ids @@ -21,6 +19,8 @@ from readalongs.util import get_langs from readalongs.web_api import OutputFormat, create_grammar, web_api_app +from .basic_test_case import BasicTestCase + class TestWebApi(BasicTestCase): _API_CLIENT = None