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..d79bd6c9 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -32,13 +32,12 @@ 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
- - run: cd tests && coverage report
+ - run: coverage report
- name: Make sure pre-commit hooks pass
uses: pre-commit/action@v3.0.1
@@ -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
@@ -156,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
diff --git a/.gitignore b/.gitignore
index cb3111ba..14024c79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -100,6 +100,7 @@ coverage.xml
*,cover
.hypothesis/
.pytest_cache/
+*tmpdir*
# Translations
*.mo
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..1237f21a 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"
@@ -132,3 +135,8 @@ ignore_missing_imports = true
[tool.isort]
profile = "black"
+
+[tool.pytest]
+filterwarnings = [
+ "ignore:'audioop' is deprecated and slated for removal in Python 3.13:DeprecationWarning",
+]
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..37b021a9 100755
--- a/tests/run.py
+++ b/run_tests.py
@@ -19,27 +19,26 @@
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 readalongs.log import LOGGER
+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
LOADER = TestLoader()
diff --git a/tests/test_align_cli.py b/tests/test_align_cli.py
index 349b25bd..836dce69 100755
--- a/tests/test_align_cli.py
+++ b/tests/test_align_cli.py
@@ -9,21 +9,22 @@
import tempfile
from os.path import exists, join
from pathlib import Path
+from typing import Union
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 tests.basic_test_case import BasicTestCase
+from tests.sound_swallower_stub import SoundSwallowerStub
-def write_file(filename: str, file_contents: str) -> str:
+def write_file(filename: Union[str, Path], file_contents: str) -> str:
"""Write file_contents to file filename, and return its name (filename)"""
with open(filename, mode="w", encoding="utf8") as f:
f.write(file_contents)
- return filename
+ return str(filename)
class TestAlignCli(BasicTestCase):
@@ -245,7 +246,7 @@ def test_align_english(self):
"""Validates that the lexicon-based g2p works for English language alignment"""
input_filename = write_file(
- join(self.tempdir, "input"),
+ self.tempdir / "input",
"This is some text that we will run through the English lexicon "
"grapheme to morpheme approach.",
)
@@ -405,7 +406,7 @@ def test_infer_plain_text_or_xml(self):
"""align -i is obsolete, now we infer plain text vs XML; test that!"""
# plain text with guess by contents
- infile1 = write_file(join(self.tempdir, "infile1"), "some plain text")
+ infile1 = write_file(self.tempdir / "infile1", "some plain text")
with SoundSwallowerStub("word:0:1"):
results = self.runner.invoke(
align,
@@ -420,7 +421,7 @@ def test_infer_plain_text_or_xml(self):
self.assertIn("No input language specified for plain text", results.output)
# plain text by extension
- infile2 = write_file(join(self.tempdir, "infile2.txt"), "blah blah",
)
)
@@ -454,7 +455,7 @@ def test_infer_plain_text_or_xml(self):
# XML with guess by contents, but with content error
infile4 = write_file(
- join(self.tempdir, "infile4"),
+ self.tempdir / "infile4",
"blah blah",
)
with SoundSwallowerStub("word:0:1"):
@@ -470,7 +471,7 @@ def test_infer_plain_text_or_xml(self):
self.assertIn("Error parsing XML", results.output)
# XML by file extension
- infile5 = write_file(join(self.tempdir, "infile5.readalong"), "Not XML!")
+ infile5 = write_file(self.tempdir / "infile5.readalong", "Not XML!")
with SoundSwallowerStub("word:0:1"):
results = self.runner.invoke(
align,
diff --git a/tests/test_anchors.py b/tests/test_anchors.py
index be7045c4..843b7449 100755
--- a/tests/test_anchors.py
+++ b/tests/test_anchors.py
@@ -7,10 +7,9 @@
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 tests.basic_test_case import BasicTestCase, silence_c_stderr
class TestAnchors(BasicTestCase):
diff --git a/tests/test_api.py b/tests/test_api.py
index 18353367..52dc96a3 100755
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -4,18 +4,17 @@
Test suite for the API way to call align
"""
-import os
import re
from contextlib import redirect_stderr
from io import StringIO
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 tests.basic_test_case import BasicTestCase, silence_logs
+from tests.sound_swallower_stub import SoundSwallowerStub
class TestAlignApi(BasicTestCase):
@@ -94,7 +93,7 @@ def test_call_make_xml(self):
def test_deprecated_prepare(self):
with self.assertLogs(LOGGER, level="WARNING") as cm:
- api.prepare(self.data_dir / "ej-fra.txt", os.devnull, ("fra",))
+ api.prepare(self.data_dir / "ej-fra.txt", self.tempdir / "foo", ("fra",))
self.assertIn("deprecated", "\n".join(cm.output))
sentences_to_convert = [
diff --git a/tests/test_audio.py b/tests/test_audio.py
index 5ab69359..307387a2 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,
@@ -18,6 +16,7 @@
write_audio_to_file,
)
from readalongs.log import LOGGER
+from tests.basic_test_case import BasicTestCase
class TestAudio(BasicTestCase):
diff --git a/tests/test_dna_text.py b/tests/test_dna_text.py
index e91b64b0..93253900 100755
--- a/tests/test_dna_text.py
+++ b/tests/test_dna_text.py
@@ -6,12 +6,12 @@
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 tests.basic_test_case import BasicTestCase
class TestDNAText(BasicTestCase):
diff --git a/tests/test_force_align.py b/tests/test_force_align.py
index 37b0cb2c..d22d623a 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
@@ -25,6 +24,7 @@
from readalongs.log import LOGGER
from readalongs.portable_tempfile import PortableNamedTemporaryFile
from readalongs.text.util import load_txt, load_xml, save_xml
+from tests.basic_test_case import BasicTestCase, silence_c_stderr
class TestForceAlignment(BasicTestCase):
diff --git a/tests/test_g2p_cli.py b/tests/test_g2p_cli.py
index bf9d800e..544d1da2 100755
--- a/tests/test_g2p_cli.py
+++ b/tests/test_g2p_cli.py
@@ -8,16 +8,16 @@
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
from readalongs.log import LOGGER
from readalongs.text.convert_xml import convert_xml
from readalongs.text.util import parse_xml
+from tests.basic_test_case import BasicTestCase
+from tests.sound_swallower_stub import SoundSwallowerStub
+from tests.test_make_xml_cli import updateFormatVersion, updateStudioVersion
def run_convert_xml(input_string):
diff --git a/tests/test_make_xml_cli.py b/tests/test_make_xml_cli.py
index c14df446..1c4c0fa9 100755
--- a/tests/test_make_xml_cli.py
+++ b/tests/test_make_xml_cli.py
@@ -8,12 +8,11 @@
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 tests.basic_test_case import BasicTestCase
def updateFormatVersion(input):
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 85df66d6..0aa342a0 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
@@ -28,6 +26,8 @@
save_xml,
)
from readalongs.util import JoinerCallbackForClick
+from tests.basic_test_case import BasicTestCase
+from tests.test_dna_utils import segments_from_pairs
class TestMisc(BasicTestCase):
diff --git a/tests/test_package_urls.py b/tests/test_package_urls.py
index 7fd41f22..6247bd14 100755
--- a/tests/test_package_urls.py
+++ b/tests/test_package_urls.py
@@ -3,13 +3,13 @@
from unittest import main
import requests
-from basic_test_case import BasicTestCase, silence_logs
from readalongs.text.make_package import (
FONTS_BUNDLE_URL,
JS_BUNDLE_URL,
fetch_bundle_file,
)
+from tests.basic_test_case import BasicTestCase, silence_logs
class TestPackageURLs(BasicTestCase):
diff --git a/tests/test_silence.py b/tests/test_silence.py
index fb23d81a..a1bcff95 100755
--- a/tests/test_silence.py
+++ b/tests/test_silence.py
@@ -5,11 +5,11 @@
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 tests.basic_test_case import BasicTestCase
class TestSilence(BasicTestCase):
diff --git a/tests/test_smil.py b/tests/test_smil.py
index 4217b7c3..8bd08fa4 100644
--- a/tests/test_smil.py
+++ b/tests/test_smil.py
@@ -7,9 +7,8 @@
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 tests.basic_test_case import BasicTestCase
class TestSmilUtilities(BasicTestCase):
diff --git a/tests/test_tokenize_cli.py b/tests/test_tokenize_cli.py
index 31e20c36..9618dd93 100755
--- a/tests/test_tokenize_cli.py
+++ b/tests/test_tokenize_cli.py
@@ -6,9 +6,8 @@
import os
from unittest import main
-from basic_test_case import BasicTestCase
-
from readalongs.cli import make_xml, tokenize
+from tests.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 c8ba4c20..ec9327cd 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
@@ -20,6 +18,7 @@
from readalongs.text.util import parse_xml
from readalongs.util import get_langs
from readalongs.web_api import OutputFormat, create_grammar, web_api_app
+from tests.basic_test_case import BasicTestCase
class TestWebApi(BasicTestCase):