diff --git a/aimbat/config.py b/aimbat/config.py index f193ca3..a96d925 100644 --- a/aimbat/config.py +++ b/aimbat/config.py @@ -4,6 +4,8 @@ from pydantic_settings import BaseSettings, SettingsConfigDict from pathlib import Path from datetime import timedelta +from pysmo.tools.iccs._defaults import ICCS_DEFAULTS +import numpy as np class Settings(BaseSettings): @@ -28,26 +30,28 @@ class Settings(BaseSettings): """Enable debug logging.""" window_pre: timedelta = Field( - default=timedelta(seconds=-15), + default=ICCS_DEFAULTS.WINDOW_PRE, lt=0, description="Initial relative begin time of window.", ) """Initial relative begin time of window.""" window_post: timedelta = Field( - default=timedelta(seconds=15), + default=ICCS_DEFAULTS.WINDOW_POST, ge=0, description="Initial relative end time of window.", ) """Initial relative end time of window.""" window_padding: timedelta = Field( - default=timedelta(seconds=20), gt=0, description="Padding around time window." + default=ICCS_DEFAULTS.PLOT_PADDING, + gt=0, + description="Padding around time window.", ) """Padding around time window.""" - min_ccnorm: float = Field( - default=0.5, + min_ccnorm: float | np.floating = Field( + default=ICCS_DEFAULTS.MIN_CCNORM, ge=0, le=1, description="Initial minimum cross correlation coefficient.", @@ -76,8 +80,6 @@ class Settings(BaseSettings): ) """Minimum length of truncated UUID string.""" - _blah: int = 4 - settings = Settings() diff --git a/tests/test_data.py b/tests/test_data.py index d268ec7..5aabd35 100644 --- a/tests/test_data.py +++ b/tests/test_data.py @@ -1,8 +1,8 @@ +from aimbat.app import app from pysmo.classes import SAC from sqlalchemy.exc import NoResultFound from sqlmodel import select, Session from pathlib import Path -from importlib import reload from aimbat.lib.io import DataType from aimbat.lib.models import AimbatDataSource import aimbat.lib.data as data @@ -12,9 +12,7 @@ class TestDataBase: - @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_project: Session) -> None: - reload(data) + """Base class for testing the data module.""" class TestDataAdd(TestDataBase): @@ -40,8 +38,6 @@ def test_cli_data_add( sac_file_good: Path, fixture_session_with_project: Session, ) -> None: - from aimbat.app import app - sac_file_good_as_string = str(sac_file_good) app(["data", "add", "--no-progress", sac_file_good_as_string]) @@ -57,7 +53,6 @@ def test_lib_print_data_table_without_active_event( fixture_session_with_data: tuple[Path, Session], capsys: pytest.CaptureFixture, ) -> None: - reload(data) # no event active with pytest.raises(NoResultFound): data.print_data_table(False) @@ -83,8 +78,6 @@ def test_lib_print_data_table_with_active_event( all_events: bool, expected: str, ) -> None: - reload(data) - data.print_data_table(short, all_events) captured = capsys.readouterr() assert expected in captured.out @@ -105,9 +98,6 @@ def test_cli_data_list( cli_args: list[str], expected: str, ) -> None: - reload(data) - from aimbat.app import app - cmd = ["data", "list"] cmd.extend(cli_args) app(cmd) @@ -121,7 +111,6 @@ def test_lib_dump_data( fixture_session_with_data: Session, capsys: pytest.CaptureFixture, ) -> None: - reload(data) data.dump_data_table() captured = capsys.readouterr() loaded_json = json.loads(captured.out) @@ -135,9 +124,6 @@ def test_cli_dump_data( fixture_session_with_data: Session, capsys: pytest.CaptureFixture, ) -> None: - reload(data) - from aimbat.app import app - app(["data", "dump"]) captured = capsys.readouterr() loaded_json = json.loads(captured.out) diff --git a/tests/test_event.py b/tests/test_event.py index 3cef374..1fc53a7 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -1,9 +1,9 @@ +from aimbat.config import settings from aimbat.lib.models import AimbatEvent, AimbatStation, AimbatSeismogram from aimbat.lib.typing import EventParameter from pydantic_core import ValidationError from sqlmodel import Session, select from sqlalchemy.exc import NoResultFound -from importlib import reload from typing import Any from collections.abc import Generator, Sequence import aimbat.lib.event as event @@ -13,10 +13,6 @@ class TestEventBase: - @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_data: Session) -> None: - reload(event) - @pytest.fixture def session( self, fixture_session_with_data: Session @@ -32,11 +28,9 @@ def get_random_event(self, session: Session) -> AimbatEvent: return random.choice(events) def activate_random_event(self, session: Session) -> AimbatEvent: - from aimbat.lib.event import set_active_event - - event = self.get_random_event(session) - set_active_event(session, event) - return event + random_event = self.get_random_event(session) + event.set_active_event(session, random_event) + return random_event class TestDeleteEvent(TestEventBase): @@ -62,7 +56,7 @@ def test_cli_delete_event_by_id(self, session: Session) -> None: is None ) - def test_cli_delete_event_by_id_with_wrong_id(self) -> None: + def test_cli_delete_event_by_id_with_wrong_id(self, session: Session) -> None: from aimbat.app import app from uuid import uuid4 @@ -250,7 +244,7 @@ def test_cli_get_event_parameter( assert "False" in capsys.readouterr().out app(["event", "get", "window_post"]) - assert "15.0s" in capsys.readouterr().out + assert f"{settings.window_post.total_seconds()}s" in capsys.readouterr().out class TestCliEvent(TestEventBase): @@ -274,6 +268,7 @@ def test_cli_set_event_parameter( def test_cli_event_list( self, + session: Session, capsys: pytest.CaptureFixture, ) -> None: from aimbat.app import app diff --git a/tests/test_io.py b/tests/test_io.py index ee6c951..d0861d4 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -7,17 +7,12 @@ from typing import Any from collections.abc import Generator from pathlib import Path -from importlib import reload import aimbat.lib.data as data import numpy as np import pytest class TestSacBase: - @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_project: Session) -> None: - reload(data) - @pytest.fixture def aimbat_seismogram_from_sac( self, diff --git a/tests/test_project.py b/tests/test_project.py index ac2ac63..0ca5d83 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1,6 +1,6 @@ +from aimbat.app import app from pathlib import Path from sqlmodel import Session -from importlib import reload import aimbat.lib.project as project import pytest @@ -35,8 +35,6 @@ def test_lib_create_project_when_one_exists( project.create_project() def test_cli_create_project(self, fixture_session_empty: Session) -> None: - from aimbat.app import app - assert project._project_exists() is False app(["project", "create"]) assert project._project_exists() is True @@ -46,7 +44,6 @@ class TestProjectDelete(TestProjectBase): def test_lib_delete_project_file( self, fixture_session_with_project_file: tuple[Session, Path] ) -> None: - reload(project) assert project._project_exists() is True project.delete_project() @@ -54,15 +51,11 @@ def test_lib_delete_project_file( def test_lib_delete_project(self, fixture_session_with_project: Session) -> None: assert project._project_exists() is True - reload(project) project.delete_project() assert project._project_exists() is False def test_cli_delete_project(self, fixture_session_with_project: Session) -> None: - reload(project) - from aimbat.app import app - assert project._project_exists() is True app(["project", "delete"]) @@ -81,7 +74,6 @@ def test_lib_print_project_info_with_empty_project( fixture_session_with_project: Session, capsys: pytest.CaptureFixture, ) -> None: - reload(project) project.print_project_info() captured = capsys.readouterr() assert "Project Info" in captured.out @@ -90,7 +82,6 @@ def test_lib_print_project_info_with_empty_project( def test_lib_print_project_info_with_active_event( self, fixture_session_with_active_event: Session, capsys: pytest.CaptureFixture ) -> None: - reload(project) project.print_project_info() captured = capsys.readouterr() assert "Project Info" in captured.out @@ -99,9 +90,6 @@ def test_lib_print_project_info_with_active_event( def test_cli_print_project_info_with_active_event( self, fixture_session_with_active_event: Session, capsys: pytest.CaptureFixture ) -> None: - reload(project) - from aimbat.app import app - assert project._project_exists() is True app(["project", "info"]) diff --git a/tests/test_seismogram.py b/tests/test_seismogram.py index 1dad441..a5078ca 100644 --- a/tests/test_seismogram.py +++ b/tests/test_seismogram.py @@ -1,3 +1,4 @@ +from aimbat.app import app from aimbat.lib.typing import SeismogramParameter from aimbat.lib.models import AimbatSeismogram from sqlmodel import Session, select @@ -14,13 +15,10 @@ class TestSeismogramBase: @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_active_event: Session) -> None: - reload(seismogram) - - @pytest.fixture def session( self, fixture_session_with_active_event: Session ) -> Generator[Session, Any, Any]: + reload(seismogram) yield fixture_session_with_active_event @@ -37,8 +35,6 @@ def test_lib_delete_seismogram_by_id(self, session: Session) -> None: ) def test_cli_delete_seismogram_by_id(self, session: Session) -> None: - from aimbat.app import app - aimbat_seismogram = random.choice(list(session.exec(select(AimbatSeismogram)))) id = aimbat_seismogram.id @@ -52,17 +48,14 @@ def test_cli_delete_seismogram_by_id(self, session: Session) -> None: ) def test_cli_delete_seismogram_by_id_with_wrong_id(self) -> None: - from aimbat.app import app - from uuid import uuid4 + import uuid - id = uuid4() + id = uuid.uuid4() with pytest.raises(NoResultFound): app(["seismogram", "delete", str(id)]) def test_cli_delete_seismogram_by_string(self, session: Session) -> None: - from aimbat.app import app - aimbat_seismogram = random.choice(list(session.exec(select(AimbatSeismogram)))) id = aimbat_seismogram.id @@ -125,8 +118,6 @@ def test_lib_get_seismogram_parameter_by_id( def test_cli_get_seismogram_parameter_with_uuid( self, random_seismogram: AimbatSeismogram, capsys: pytest.CaptureFixture ) -> None: - from aimbat.app import app - app( ["seismogram", "get", str(random_seismogram.id), SeismogramParameter.SELECT] ) @@ -136,8 +127,6 @@ def test_cli_get_seismogram_parameter_with_uuid( def test_cli_get_seismogram_parameter_with_string( self, random_seismogram: AimbatSeismogram, capsys: pytest.CaptureFixture ) -> None: - from aimbat.app import app - app( [ "seismogram", @@ -199,8 +188,6 @@ def test_lib_set_seismogram_parameter_by_id( def test_cli_set_seismogram_parameter_with_uuid( self, random_seismogram: AimbatSeismogram, session: Session ) -> None: - from aimbat.app import app - app( [ "seismogram", @@ -221,8 +208,6 @@ def test_cli_set_seismogram_parameter_with_uuid( def test_cli_set_seismogram_parameter_with_string( self, random_seismogram: AimbatSeismogram, session: Session ) -> None: - from aimbat.app import app - app( [ "seismogram", @@ -287,8 +272,6 @@ def test_lib_print_seismogram_table_short_all_events( assert "ID (shortened)" in captured.out def test_cli_print_seismogram_table(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - app(["seismogram", "list"]) captured = capsys.readouterr() @@ -297,10 +280,7 @@ def test_cli_print_seismogram_table(self, capsys: pytest.CaptureFixture) -> None class TestDumpSeismogram(TestSeismogramBase): - def test_lib_dump_data( - self, fixture_session_with_data: Session, capsys: pytest.CaptureFixture - ) -> None: - reload(seismogram) + def test_lib_dump_data(self, capsys: pytest.CaptureFixture) -> None: seismogram.dump_seismogram_table() captured = capsys.readouterr() loaded_json = json.loads(captured.out) @@ -309,12 +289,7 @@ def test_lib_dump_data( for i in loaded_json: _ = AimbatSeismogram(**i) - def test_cli_dump_data( - self, fixture_session_with_data: Session, capsys: pytest.CaptureFixture - ) -> None: - reload(seismogram) - from aimbat.app import app - + def test_cli_dump_data(self, capsys: pytest.CaptureFixture) -> None: app(["seismogram", "dump"]) captured = capsys.readouterr() loaded_json = json.loads(captured.out) @@ -336,6 +311,4 @@ def test_lib_plotseis_qt( _ = seismogram.plot_seismograms(use_qt=True) def test_cli_plotseis_mpl(self) -> None: - from aimbat.app import app - app(["seismogram", "plot"]) diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index 859b6ed..1ab8fd3 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -1,3 +1,4 @@ +from aimbat.app import app from sqlmodel import Session from importlib import reload from typing import Any @@ -11,13 +12,10 @@ class TestSnapshotBase: @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_active_event: Session) -> None: - reload(snapshot) - - @pytest.fixture def session( self, fixture_session_with_active_event: Session ) -> Generator[Session, Any, Any]: + reload(snapshot) yield fixture_session_with_active_event @@ -143,8 +141,6 @@ def test_snapshot_table_short_all_events( class TestCliSnapshotUsage(TestSnapshotBase): def test_usage(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - app("snapshot") captured = capsys.readouterr() assert "Usage" in captured.out @@ -152,8 +148,6 @@ def test_usage(self, capsys: pytest.CaptureFixture) -> None: class TestCliSnapshotCreate(TestSnapshotBase): def test_create_snapshot(self, session: Session) -> None: - from aimbat.app import app - app(["snapshot", "create", RANDOM_COMMENT]) all_snapshots = snapshot.get_snapshots(session) @@ -170,8 +164,6 @@ def create_snapshots(self, session: Session) -> Generator[None, Any, Any]: yield def test_delete_snapshot_with_uuid(self, session: Session) -> None: - from aimbat.app import app - all_snapshots = snapshot.get_snapshots(session) assert len(all_snapshots) == 1 snapshot_id = all_snapshots[0].id @@ -182,8 +174,6 @@ def test_delete_snapshot_with_uuid(self, session: Session) -> None: assert len(all_snapshots) == 0 def test_delete_snapshot_with_string(self, session: Session) -> None: - from aimbat.app import app - all_snapshots = snapshot.get_snapshots(session) assert len(all_snapshots) == 1 snapshot_id = str(all_snapshots[0].id)[:8] @@ -194,8 +184,6 @@ def test_delete_snapshot_with_string(self, session: Session) -> None: assert len(all_snapshots) == 0 def test_rollback_to_snapshot_with_uuid(self, session: Session) -> None: - from aimbat.app import app - all_snapshots = snapshot.get_snapshots(session) assert len(all_snapshots) == 1 snapshot_id = all_snapshots[0].id @@ -204,8 +192,6 @@ def test_rollback_to_snapshot_with_uuid(self, session: Session) -> None: session.flush() def test_rollback_to_snapshot_with_string(self, session: Session) -> None: - from aimbat.app import app - all_snapshots = snapshot.get_snapshots(session) assert len(all_snapshots) == 1 snapshot_id = str(all_snapshots[0].id)[:8] @@ -223,8 +209,6 @@ def create_snapshots(self, session: Session) -> Generator[None, Any, Any]: yield def test_snapshot_table_no_format(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - app(["snapshot", "list"]) captured = capsys.readouterr() diff --git a/tests/test_station.py b/tests/test_station.py index 6f07980..f89832c 100644 --- a/tests/test_station.py +++ b/tests/test_station.py @@ -1,4 +1,5 @@ from aimbat.lib.models import AimbatStation +from aimbat.app import app from sqlmodel import Session, select from sqlalchemy.exc import NoResultFound from importlib import reload @@ -12,13 +13,10 @@ class TestStationBase: @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_active_event: Session) -> None: - reload(station) - - @pytest.fixture def session( self, fixture_session_with_active_event: Session ) -> Generator[Session, Any, Any]: + reload(station) yield fixture_session_with_active_event @@ -35,8 +33,6 @@ def test_lib_delete_station_by_id(self, session: Session) -> None: ) def test_cli_delete_station_by_id(self, session: Session) -> None: - from aimbat.app import app - seismogram = random.choice(list(session.exec(select(AimbatStation)))) id = seismogram.id @@ -50,17 +46,14 @@ def test_cli_delete_station_by_id(self, session: Session) -> None: ) def test_cli_delete_station_by_id_with_wrong_id(self) -> None: - from aimbat.app import app - from uuid import uuid4 + import uuid - id = uuid4() + id = uuid.uuid4() with pytest.raises(NoResultFound): app(["station", "delete", str(id)]) def test_cli_delete_station_by_string(self, session: Session) -> None: - from aimbat.app import app - station = random.choice(list(session.exec(select(AimbatStation)))) id = station.id @@ -91,25 +84,18 @@ def test_sac_data(self, capsys: pytest.CaptureFixture) -> None: class TestCliStation(TestStationBase): def test_usage(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - app(["station"]) assert "Usage" in capsys.readouterr().out - def test_station_list(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - + def test_station_list( + self, session: Session, capsys: pytest.CaptureFixture + ) -> None: app(["station", "list", "--all"]) assert "# Seismograms" in capsys.readouterr().out class TestDumpStation(TestStationBase): - def test_lib_dump_data( - self, - fixture_session_with_data: Session, - capsys: pytest.CaptureFixture, - ) -> None: - reload(station) + def test_lib_dump_data(self, capsys: pytest.CaptureFixture) -> None: station.dump_station_table() captured = capsys.readouterr() loaded_json = json.loads(captured.out) @@ -118,14 +104,7 @@ def test_lib_dump_data( for i in loaded_json: _ = AimbatStation(**i) - def test_cli_dump_data( - self, - fixture_session_with_data: Session, - capsys: pytest.CaptureFixture, - ) -> None: - reload(station) - from aimbat.app import app - + def test_cli_dump_data(self, capsys: pytest.CaptureFixture) -> None: app(["station", "dump"]) captured = capsys.readouterr() loaded_json = json.loads(captured.out) diff --git a/tests/test_utils.py b/tests/test_utils.py index 5e5fe10..02758e6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,5 @@ from aimbat.config import Settings +from aimbat.app import app from pysmo.classes import SAC from datetime import datetime, timezone from importlib import reload @@ -14,11 +15,6 @@ class TestUtilsBase: - @pytest.fixture(autouse=True) - def reload_modules(self, fixture_session_with_active_event: Session) -> None: - reload(checkdata) - reload(sampledata) - @pytest.fixture def session( self, fixture_session_with_active_event: Session @@ -28,11 +24,14 @@ def session( @pytest.fixture(autouse=True) def download_dir( self, + session: Session, tmp_path_factory: pytest.TempPathFactory, patch_settings: Settings, ) -> Generator[Path, Any, Any]: tmp_dir = tmp_path_factory.mktemp("download_dir") patch_settings.sampledata_dir = tmp_dir + reload(checkdata) + reload(sampledata) yield tmp_dir @@ -87,8 +86,6 @@ def test_check_seismogram_no_begin_time(self, sac_instance_good: SAC) -> None: assert "No seismogram data" in issues[0] def test_cli_usage(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - app(["utils", "checkdata", "--help"]) assert "Usage" in capsys.readouterr().out @@ -96,7 +93,6 @@ def test_cli_checkdata( self, tmp_path_factory: pytest.TempPathFactory, capsys: pytest.CaptureFixture ) -> None: """Test AIMBAT cli with checkdata subcommand.""" - from aimbat.app import app testfile = str(tmp_path_factory.mktemp("checkdata")) + "/test.sac" @@ -146,14 +142,10 @@ def test_lib_delete_sampledata(self, download_dir: Path) -> None: assert download_dir.exists() is False def test_cli_usage(self, capsys: pytest.CaptureFixture) -> None: - from aimbat.app import app - app(["utils", "sampledata"]) assert "Usage" in capsys.readouterr().out def test_cli_download_sampledata(self, download_dir: Path) -> None: - from aimbat.app import app - assert len(os.listdir((download_dir))) == 0 app(["utils", "sampledata", "download"]) assert len(os.listdir((download_dir))) > 0 @@ -166,8 +158,6 @@ def test_cli_download_sampledata(self, download_dir: Path) -> None: app(["utils", "sampledata", "download", "--force"]) def test_cli__delete_sampledata(self, download_dir: Path) -> None: - from aimbat.app import app - assert len(os.listdir((download_dir))) == 0 app(["utils", "sampledata", "download"]) assert len(os.listdir((download_dir))) > 0 diff --git a/uv.lock b/uv.lock index 5673266..49cfee1 100644 --- a/uv.lock +++ b/uv.lock @@ -605,27 +605,27 @@ wheels = [ [[package]] name = "fonttools" -version = "4.60.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/d9/4eabd956fe123651a1f0efe29d9758b3837b5ae9a98934bdb571117033bb/fonttools-4.60.0.tar.gz", hash = "sha256:8f5927f049091a0ca74d35cce7f78e8f7775c83a6901a8fbe899babcc297146a", size = 3553671, upload-time = "2025-09-17T11:34:01.504Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/9b/706ebf84b55ab03439c1f3a94d6915123c0d96099f4238b254fdacffe03a/fonttools-4.60.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8c68928a438d60dfde90e2f09aa7f848ed201176ca6652341744ceec4215859f", size = 2831953, upload-time = "2025-09-17T11:32:29.39Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/782f485be450846e4f3aecff1f10e42af414fc6e19d235c70020f64278e1/fonttools-4.60.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b7133821249097cffabf0624eafd37f5a3358d5ce814febe9db688e3673e724e", size = 2351716, upload-time = "2025-09-17T11:32:31.46Z" }, - { url = "https://files.pythonhosted.org/packages/39/77/ad8d2a6ecc19716eb488c8cf118de10f7802e14bdf61d136d7b52358d6b1/fonttools-4.60.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3638905d3d77ac8791127ce181f7cb434f37e4204d8b2e31b8f1e154320b41f", size = 4922729, upload-time = "2025-09-17T11:32:33.659Z" }, - { url = "https://files.pythonhosted.org/packages/6b/48/aa543037c6e7788e1bc36b3f858ac70a59d32d0f45915263d0b330a35140/fonttools-4.60.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7968a26ef010ae89aabbb2f8e9dec1e2709a2541bb8620790451ee8aeb4f6fbf", size = 4967188, upload-time = "2025-09-17T11:32:35.74Z" }, - { url = "https://files.pythonhosted.org/packages/ac/58/e407d2028adc6387947eff8f2940b31f4ed40b9a83c2c7bbc8b9255126e2/fonttools-4.60.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ef01ca7847c356b0fe026b7b92304bc31dc60a4218689ee0acc66652c1a36b2", size = 4910043, upload-time = "2025-09-17T11:32:38.054Z" }, - { url = "https://files.pythonhosted.org/packages/16/ef/e78519b3c296ef757a21b792fc6a785aa2ef9a2efb098083d8ed5f6ee2ba/fonttools-4.60.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f3482d7ed7867edfcf785f77c1dffc876c4b2ddac19539c075712ff2a0703cf5", size = 5061980, upload-time = "2025-09-17T11:32:40.457Z" }, - { url = "https://files.pythonhosted.org/packages/00/4c/ad72444d1e3ef704ee90af8d5abf198016a39908d322bf41235562fb01a0/fonttools-4.60.0-cp312-cp312-win32.whl", hash = "sha256:8c937c4fe8addff575a984c9519433391180bf52cf35895524a07b520f376067", size = 2217750, upload-time = "2025-09-17T11:32:42.586Z" }, - { url = "https://files.pythonhosted.org/packages/46/55/3e8ac21963e130242f5a9ea2ebc57f5726d704bf4dcca89088b5b637b2d3/fonttools-4.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:99b06d5d6f29f32e312adaed0367112f5ff2d300ea24363d377ec917daf9e8c5", size = 2266025, upload-time = "2025-09-17T11:32:44.8Z" }, - { url = "https://files.pythonhosted.org/packages/b4/6b/d090cd54abe88192fe3010f573508b2592cf1d1f98b14bcb799a8ad20525/fonttools-4.60.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:97100ba820936cdb5148b634e0884f0088699c7e2f1302ae7bba3747c7a19fb3", size = 2824791, upload-time = "2025-09-17T11:32:47.002Z" }, - { url = "https://files.pythonhosted.org/packages/97/8c/7ccb5a27aac9a535623fe04935fb9f469a4f8a1253991af9fbac2fe88c17/fonttools-4.60.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:03fccf84f377f83e99a5328a9ebe6b41e16fcf64a1450c352b6aa7e0deedbc01", size = 2347081, upload-time = "2025-09-17T11:32:49.204Z" }, - { url = "https://files.pythonhosted.org/packages/f8/1a/c14f0bb20b4cb7849dc0519f0ab0da74318d52236dc23168530569958599/fonttools-4.60.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3ef06671f862cd7da78ab105fbf8dce9da3634a8f91b3a64ed5c29c0ac6a9a8", size = 4902095, upload-time = "2025-09-17T11:32:51.848Z" }, - { url = "https://files.pythonhosted.org/packages/c9/a0/c7c91f07c40de5399cbaec7d25e04c9afac6c8f80036a98c125efdb5fe1a/fonttools-4.60.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f2195faf96594c238462c420c7eff97d1aa51de595434f806ec3952df428616", size = 4959137, upload-time = "2025-09-17T11:32:54.185Z" }, - { url = "https://files.pythonhosted.org/packages/38/d2/169e49498df9f2c721763aa39b0bf3d08cb762864ebc8a8ddb99f5ba7ec8/fonttools-4.60.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3887008865fa4f56cff58a1878f1300ba81a4e34f76daf9b47234698493072ee", size = 4900467, upload-time = "2025-09-17T11:32:56.664Z" }, - { url = "https://files.pythonhosted.org/packages/cc/9c/bfb56b89c3eab8bcb739c7fd1e8a43285c8dd833e1e1d18d4f54f2f641af/fonttools-4.60.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5567bd130378f21231d3856d8f0571dcdfcd77e47832978c26dabe572d456daa", size = 5043508, upload-time = "2025-09-17T11:32:58.944Z" }, - { url = "https://files.pythonhosted.org/packages/77/30/2b511c7eb99faee1fd9a0b42e984fb91275da3d681da650af4edf409d0fd/fonttools-4.60.0-cp313-cp313-win32.whl", hash = "sha256:699d0b521ec0b188ac11f2c14ccf6a926367795818ddf2bd00a273e9a052dd20", size = 2216037, upload-time = "2025-09-17T11:33:01.192Z" }, - { url = "https://files.pythonhosted.org/packages/3d/73/a2cc5ee4faeb0302cc81942c27f3b516801bf489fdc422a1b20090fff695/fonttools-4.60.0-cp313-cp313-win_amd64.whl", hash = "sha256:24296163268e7c800009711ce5c0e9997be8882c0bd546696c82ef45966163a6", size = 2265190, upload-time = "2025-09-17T11:33:03.935Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a4/247d3e54eb5ed59e94e09866cfc4f9567e274fbf310ba390711851f63b3b/fonttools-4.60.0-py3-none-any.whl", hash = "sha256:496d26e4d14dcccdd6ada2e937e4d174d3138e3d73f5c9b6ec6eb2fd1dab4f66", size = 1142186, upload-time = "2025-09-17T11:33:59.287Z" }, +version = "4.60.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/42/97a13e47a1e51a5a7142475bbcf5107fe3a68fc34aef331c897d5fb98ad0/fonttools-4.60.1.tar.gz", hash = "sha256:ef00af0439ebfee806b25f24c8f92109157ff3fac5731dc7867957812e87b8d9", size = 3559823, upload-time = "2025-09-29T21:13:27.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/f7/a10b101b7a6f8836a5adb47f2791f2075d044a6ca123f35985c42edc82d8/fonttools-4.60.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b0c6d57ab00dae9529f3faf187f2254ea0aa1e04215cf2f1a8ec277c96661bc", size = 2832953, upload-time = "2025-09-29T21:11:39.616Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fe/7bd094b59c926acf2304d2151354ddbeb74b94812f3dc943c231db09cb41/fonttools-4.60.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:839565cbf14645952d933853e8ade66a463684ed6ed6c9345d0faf1f0e868877", size = 2352706, upload-time = "2025-09-29T21:11:41.826Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ca/4bb48a26ed95a1e7eba175535fe5805887682140ee0a0d10a88e1de84208/fonttools-4.60.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8177ec9676ea6e1793c8a084a90b65a9f778771998eb919d05db6d4b1c0b114c", size = 4923716, upload-time = "2025-09-29T21:11:43.893Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9f/2cb82999f686c1d1ddf06f6ae1a9117a880adbec113611cc9d22b2fdd465/fonttools-4.60.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:996a4d1834524adbb423385d5a629b868ef9d774670856c63c9a0408a3063401", size = 4968175, upload-time = "2025-09-29T21:11:46.439Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/be569699e37d166b78e6218f2cde8c550204f2505038cdd83b42edc469b9/fonttools-4.60.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a46b2f450bc79e06ef3b6394f0c68660529ed51692606ad7f953fc2e448bc903", size = 4911031, upload-time = "2025-09-29T21:11:48.977Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9f/89411cc116effaec5260ad519162f64f9c150e5522a27cbb05eb62d0c05b/fonttools-4.60.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ec722ee589e89a89f5b7574f5c45604030aa6ae24cb2c751e2707193b466fed", size = 5062966, upload-time = "2025-09-29T21:11:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/62/a1/f888221934b5731d46cb9991c7a71f30cb1f97c0ef5fcf37f8da8fce6c8e/fonttools-4.60.1-cp312-cp312-win32.whl", hash = "sha256:b2cf105cee600d2de04ca3cfa1f74f1127f8455b71dbad02b9da6ec266e116d6", size = 2218750, upload-time = "2025-09-29T21:11:56.601Z" }, + { url = "https://files.pythonhosted.org/packages/88/8f/a55b5550cd33cd1028601df41acd057d4be20efa5c958f417b0c0613924d/fonttools-4.60.1-cp312-cp312-win_amd64.whl", hash = "sha256:992775c9fbe2cf794786fa0ffca7f09f564ba3499b8fe9f2f80bd7197db60383", size = 2267026, upload-time = "2025-09-29T21:11:58.852Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5b/cdd2c612277b7ac7ec8c0c9bc41812c43dc7b2d5f2b0897e15fdf5a1f915/fonttools-4.60.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f68576bb4bbf6060c7ab047b1574a1ebe5c50a17de62830079967b211059ebb", size = 2825777, upload-time = "2025-09-29T21:12:01.22Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8a/de9cc0540f542963ba5e8f3a1f6ad48fa211badc3177783b9d5cadf79b5d/fonttools-4.60.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:eedacb5c5d22b7097482fa834bda0dafa3d914a4e829ec83cdea2a01f8c813c4", size = 2348080, upload-time = "2025-09-29T21:12:03.785Z" }, + { url = "https://files.pythonhosted.org/packages/2d/8b/371ab3cec97ee3fe1126b3406b7abd60c8fec8975fd79a3c75cdea0c3d83/fonttools-4.60.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b33a7884fabd72bdf5f910d0cf46be50dce86a0362a65cfc746a4168c67eb96c", size = 4903082, upload-time = "2025-09-29T21:12:06.382Z" }, + { url = "https://files.pythonhosted.org/packages/04/05/06b1455e4bc653fcb2117ac3ef5fa3a8a14919b93c60742d04440605d058/fonttools-4.60.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2409d5fb7b55fd70f715e6d34e7a6e4f7511b8ad29a49d6df225ee76da76dd77", size = 4960125, upload-time = "2025-09-29T21:12:09.314Z" }, + { url = "https://files.pythonhosted.org/packages/8e/37/f3b840fcb2666f6cb97038793606bdd83488dca2d0b0fc542ccc20afa668/fonttools-4.60.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8651e0d4b3bdeda6602b85fdc2abbefc1b41e573ecb37b6779c4ca50753a199", size = 4901454, upload-time = "2025-09-29T21:12:11.931Z" }, + { url = "https://files.pythonhosted.org/packages/fd/9e/eb76f77e82f8d4a46420aadff12cec6237751b0fb9ef1de373186dcffb5f/fonttools-4.60.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:145daa14bf24824b677b9357c5e44fd8895c2a8f53596e1b9ea3496081dc692c", size = 5044495, upload-time = "2025-09-29T21:12:15.241Z" }, + { url = "https://files.pythonhosted.org/packages/f8/b3/cede8f8235d42ff7ae891bae8d619d02c8ac9fd0cfc450c5927a6200c70d/fonttools-4.60.1-cp313-cp313-win32.whl", hash = "sha256:2299df884c11162617a66b7c316957d74a18e3758c0274762d2cc87df7bc0272", size = 2217028, upload-time = "2025-09-29T21:12:17.96Z" }, + { url = "https://files.pythonhosted.org/packages/75/4d/b022c1577807ce8b31ffe055306ec13a866f2337ecee96e75b24b9b753ea/fonttools-4.60.1-cp313-cp313-win_amd64.whl", hash = "sha256:a3db56f153bd4c5c2b619ab02c5db5192e222150ce5a1bc10f16164714bc39ac", size = 2266200, upload-time = "2025-09-29T21:12:20.14Z" }, + { url = "https://files.pythonhosted.org/packages/c7/93/0dd45cd283c32dea1545151d8c3637b4b8c53cdb3a625aeb2885b184d74d/fonttools-4.60.1-py3-none-any.whl", hash = "sha256:906306ac7afe2156fcf0042173d6ebbb05416af70f6b370967b47f8f00103bbb", size = 1143175, upload-time = "2025-09-29T21:13:24.134Z" }, ] [[package]]