diff options
60 files changed, 3424 insertions, 1369 deletions
@@ -26,5 +26,8 @@ test poetry.toml /language-packs # The module docs are regenerated using sphinx-apidoc -doc/developer/module/ +doc/developer/module/* +# However, we manually document hittekaart_py: +!doc/developer/module/modules.rst +!doc/developer/module/hittekaart_py.rst /node_modules diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9263e91..20307c4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" default: - image: python:bullseye + image: python:bookworm # Pip's cache doesn't store the python packages # https://pip.pypa.io/en/stable/topics/caching/ # @@ -17,16 +17,15 @@ default: before_script: - python --version # For debugging + - dpkg -s libsqlite3-0 - pip install tox test: + parallel: + matrix: + - DB: ["sqlite", "postgres"] script: - - pip install poetry && pip install "playwright=="$(poetry show playwright | grep version | cut -f 2 -d ":" | tr -d " ") - - playwright install firefox - - playwright install-deps - - apt install -y redis-server - - redis-server >/dev/null 2>&1 & - - tox -e python -- --browser firefox + - ci/run_tests.sh $DB # test-pypy: # image: pypy:3 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a49f7b6..1172f02 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,35 @@ Changelog Unreleased ---------- +Fixed +^^^^^ + +- Backup GPX files are now included in the storage breakdown. + +0.11.0 - 2025-06-18 +------------------- + +Added +^^^^^ + +- The admin overview with storage statistics and some other information. +- Track preview images in the browse/profile views. +- The ability to replace a track's GPX file with a new one. + +Changed +^^^^^^^ + +- Fietsboek now requires at least Python 3.11. +- There is now a proper 404 (Not Found) page. +- There is now a proper 403 (Forbidden) page. +- The heat map is now preselected on the profile page. +- The browse page now paginates its results. + +Fixed +^^^^^ + +- The alignment of image/comment counts on the home view has been fixed. + 0.10.0 - 2025-02-05 ------------------- diff --git a/asset-sources/theme.scss b/asset-sources/theme.scss index 7f89bf6..ba05782 100644 --- a/asset-sources/theme.scss +++ b/asset-sources/theme.scss @@ -280,10 +280,63 @@ strong { width: 25%; } +.browse-track-card { + display: grid; + grid-template: 'preview data' / 300px auto; + + @media (max-width: 768px) { + grid-template: + 'preview' + 'data'; + } + + &.card-body { + padding: 0px; + } + + .browse-track-preview { + grid-area: preview; + + @media (max-width: 768px) { + text-align: center; + } + + img { + width: 300px; + height: 300px; + } + } + + .browse-track-data { + grid-area: data; + padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); + } +} + .chart-title { text-align: center; } +/* Admin view layout: We have an extra sidebar for the navigation */ +#adminContainer { + display: grid; + grid-template-areas: "sidebar main"; + grid-template-columns: 1fr 5fr; + gap: 1rem; +} + +#adminNavigation { + grid-area: sidebar; +} + +#adminContent { + grid-area: main; +} + +.admin-stat { + font-size: 120%; +} + .list-group.list-group-root { padding: 0; overflow: hidden; diff --git a/ci/run_tests.sh b/ci/run_tests.sh new file mode 100755 index 0000000..200f794 --- /dev/null +++ b/ci/run_tests.sh @@ -0,0 +1,48 @@ +#!/bin/bash +set -euxo pipefail + +DB=$1 + +PPATH="/usr/lib/postgresql/15/bin/" + +setup_postgres() { + apt update + apt install -y postgresql postgresql-client sudo + echo -n "postgres" >/tmp/pw + mkdir /tmp/postgres-db + chown postgres:postgres /tmp/postgres-db + sudo -u postgres "$PPATH/initdb" --pwfile /tmp/pw -U postgres /tmp/postgres-db + sudo -u postgres "$PPATH/postgres" -D /tmp/postgres-db >/dev/null 2>&1 & +} + +setup_redis() { + apt install -y redis-server + redis-server >/dev/null 2>&1 & +} + +setup_playwright() { + pip install poetry + pip install "playwright=="$(poetry show playwright | grep version | cut -f 2 -d ":" | tr -d " ") + playwright install firefox + playwright install-deps +} + +case "$DB" in + "sqlite") + ;; + + "postgres") + setup_postgres + sed -i 's|^sqlalchemy.url = .*$|sqlalchemy.url = postgresql://postgres:postgres@localhost/postgres|' testing.ini + ;; + + *) + echo "Unknown database: $DB" + exit 1 + ;; +esac + +pip install tox +setup_playwright +setup_redis +tox -e python -- --browser firefox diff --git a/doc/administration/installation.rst b/doc/administration/installation.rst index b207784..13e03b5 100644 --- a/doc/administration/installation.rst +++ b/doc/administration/installation.rst @@ -6,14 +6,17 @@ This document will outline the installation process of Fietsboek, step-by-step. Requirements ------------ -Fietsboek has the following requirements (apart from the Python modules, which -will be installed by ``pip``): +Fietsboek has the following requirements: -* Python 3.10 or later -* A `redis <https://redis.com/>`__ server, used for caching and temporary data -* (Optionally) an SQL database server like `PostgreSQL - <https://www.postgresql.org/>`__ or `MariaDB <https://mariadb.org/>`__ (if - SQLite is not enough) +* A Linux system +* Python 3.11 or later +* A `redis <https://redis.com/>`__ server +* (Optionally) an SQL database server: + + * `PostgreSQL <https://www.postgresql.org/>`__ + +Other systems (such as BSD as operating system, or MariaDB as SQL server) might +work, but are not tested. In addition, if you run on a different interpreter than CPython, you might need a working Rust toolchain (``rustc`` and ``cargo``) installed. This is because @@ -97,6 +100,18 @@ parser to process the GPX files: .. _issue #7: https://gitlab.com/dunj3/fietsboek/-/issues/7 +Optional: Install Database Drivers +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you decide to use a database other than SQLite, you must install the +required drivers: + +**PostgreSQL**: + +.. code:: bash + + .venv/bin/pip install psycopg2 + Configuring Fietsboek --------------------- @@ -119,7 +134,7 @@ other update tasks. You can use it to set up the initial database schema: instead of a specific version, you must also run ``.venv/bin/alembic -c production.ini upgrade head``. - See :doc:`../developer/updater` ("Furhter notes") for more information. + See :doc:`../developer/updater` ("Further notes") for more information. Adding an Administrator User ---------------------------- diff --git a/doc/developer/module/hittekaart_py.rst b/doc/developer/module/hittekaart_py.rst new file mode 100644 index 0000000..052c2b0 --- /dev/null +++ b/doc/developer/module/hittekaart_py.rst @@ -0,0 +1,149 @@ +hittekaart_py package +===================== + +.. module:: hittekaart_py + + The ``hittekaart_py`` module provides a Python interface for hittekaart__ + heatmap generation. Unlike previous modules, this is a binding using + PyO3__, and not a subprocess wrapper. + +.. __: https://gitlab.com/dunj3/hittekaart +.. __: https://crates.io/crates/pyo3 + +Example +------- + +.. code-block:: python + + from hittekaart_py import ( + Track, HeatmapRenderer, Settings, Storage, generate + ) + + settings = Settings(threads=3) + tracks = [ + Track.from_file(b"Documents/track.gpx", None), + Track.from_coordinates([(45.0, 47.0)]), + ] + storage = Storage.Sqlite(b"/tmp/tiles.sqlite") + generate(settings, tracks, HeatmapRenderer(), storage) + +Input and output +---------------- + +.. class:: Track + + An in-memory representation of a track. + + .. staticmethod:: from_file(path, compression) + + Loads a track from the given file. + + :param path: Path to the file. + :type path: bytes + :param compression: Decompression algorithm to use when reading the + file. Can be :obj:`None`, ``"gzip"`` or ``"brotli"``. + :type compression: str | None + :return: The created track. + :rtype: Track + + .. staticmethod:: from_coordinates(coordinates) + + Directly represents the given coordinates as a track. + + :param coordinates: The coordinates as a list of longitude-latitude + pairs. + :type coordinates: list[tuple[float, float]] + :return: The created track. + :rtype: Track + + +.. class:: Storage + + Represents the output storage. + + .. staticmethod:: Folder(path) + + Output the tiles to the given folder. This will create a subdirectory + for every zoom level, then a directory for the x coordinate, then a + file ``y.png``. + + Note that this will create many small files, which can waste space. + + :param path: Path to the folder. + :type path: bytes + :return: The created storage. + :rtype: Storage + + .. staticmethod:: Sqlite(path) + + Output the tiles to the given SQLite database. + + :param path: Path to the database. + :type path: bytes + :return: The created storage. + :rtype: Storage + +Renderers +--------- + +.. class:: HeatmapRenderer() + + The renderer that generates a heatmap. + +.. class:: MarktileRenderer() + + The renderer that will only mark visited tiles. + +.. class:: TilehuntRenderer(zoom) + + The renderer that will mark visisted tiles at a fixed zoom level. + + :param zoom: The zoom level. + :type zoom: int + +Tile generation +--------------- + +.. class:: Settings(min_zoom=1, max_zoom=19, threads=0) + + Settings that apply to all renderers. + + .. attribute:: min_zoom + :type: int + + Smalles zoom level to generate tiles for. + + .. attribute:: max_zoom + :type: int + + Largest zoom level to generate tiles for. + + .. attribute:: threads + :type: int + + Number of threads to use for tile generation. + + Setting this to 0 will automatically determine the number of available + cores and use that many threads. + +.. function:: generate(settings, items, renderer, storage) + + Generates the tiles using the given renderer, and saves them to the given + storage. + + :param settings: The settings. + :type settings: Settings + :param items: The tracks to render. + :type items: ~typing.Iterable[Track] + :param renderer: The renderer to use. + :type renderer: HeatmapRenderer | MarktileRenderer | TilehuntRenderer + :param storage: The storage to output to. + :type storage: Storage + +Errors +------ + +.. exception:: HitteError + + Catch-all error for underlying hittekaart errors. See the string + description for the error cause. diff --git a/doc/developer/module/modules.rst b/doc/developer/module/modules.rst new file mode 100644 index 0000000..9585a5f --- /dev/null +++ b/doc/developer/module/modules.rst @@ -0,0 +1,8 @@ +fietsboek +========= + +.. toctree:: + :maxdepth: 1 + + fietsboek + hittekaart_py diff --git a/fietsboek/__init__.py b/fietsboek/__init__.py index 3d6c125..9cd58ed 100644 --- a/fietsboek/__init__.py +++ b/fietsboek/__init__.py @@ -20,6 +20,7 @@ from pathlib import Path from typing import Callable, Optional import redis +import sqlalchemy from pyramid.config import Configurator from pyramid.csrf import CookieCSRFStoragePolicy from pyramid.httpexceptions import HTTPServiceUnavailable @@ -119,6 +120,22 @@ def check_update_state(config_uri: str): LOGGER.warning("Could not determine version state of the data - check `fietsupdate status`") +def check_db_engine(sqlalchemy_uri: str): + """Checks whether we "support" the given SQL engine. + + :param sqlalchemy_uri: The configured SQLAlchemy URL. + """ + engine = sqlalchemy.create_engine(sqlalchemy_uri) + match engine.name: + case "sqlite": + pass + case _: + LOGGER.warning( + "The configured SQL backend is not well tested in combination with fietsboek. " + "Use it at your own risk." + ) + + def main(global_config, **settings): """This function returns a Pyramid WSGI application.""" # Avoid a circular import by not importing at the top level @@ -132,6 +149,8 @@ def main(global_config, **settings): parsed_config = mod_config.parse(settings) settings["jinja2.newstyle"] = True + check_db_engine(parsed_config.sqlalchemy_url) + def data_manager(request): return DataManager(Path(request.config.data_dir)) @@ -174,7 +193,7 @@ def main(global_config, **settings): config.add_request_method(redis_, name="redis", reify=True) config.add_request_method(config_, name="config", reify=True) - config.registry.registerUtility(TileRequester()) + config.registry.registerUtility(TileRequester(redis.from_url(parsed_config.redis_url))) jinja2_env = config.get_jinja2_environment() jinja2_env.filters["format_decimal"] = mod_jinja2.filter_format_decimal diff --git a/fietsboek/actions.py b/fietsboek/actions.py index a20ca2e..3f14308 100644 --- a/fietsboek/actions.py +++ b/fietsboek/actions.py @@ -7,6 +7,7 @@ the test functions. """ import datetime +import io import logging import re from typing import Optional @@ -18,12 +19,14 @@ from pyramid.request import Request from sqlalchemy import select from sqlalchemy.orm.session import Session -from . import email, models +from . import email, models, trackmap from . import transformers as mod_transformers from . import util +from .config import TileLayerConfig from .data import DataManager, TrackDataDir from .models.track import TrackType, Visibility from .models.user import TokenType +from .views.tileproxy import TileRequester LOGGER = logging.getLogger(__name__) @@ -31,6 +34,8 @@ LOGGER = logging.getLogger(__name__) def add_track( dbsession: Session, data_manager: DataManager, + tile_requester: TileRequester, + layer: TileLayerConfig, owner: models.User, title: str, date: datetime.datetime, @@ -105,6 +110,12 @@ def add_track( track.ensure_cache(gpx) dbsession.add(track.cache) + LOGGER.debug("Building preview image for %s", track.id) + preview_image = trackmap.render(gpx, layer, tile_requester) + image_io = io.BytesIO() + preview_image.save(image_io, "PNG") + manager.set_preview(image_io.getvalue()) + manager.engrave_metadata( title=track.title, description=track.description, diff --git a/fietsboek/alembic/versions/20220808_d085998b49ca.py b/fietsboek/alembic/versions/20220808_d085998b49ca.py index d6353d2..2c5b71d 100644 --- a/fietsboek/alembic/versions/20220808_d085998b49ca.py +++ b/fietsboek/alembic/versions/20220808_d085998b49ca.py @@ -14,9 +14,18 @@ down_revision = '091ce24409fe' branch_labels = None depends_on = None +is_postgres = op.get_bind().dialect.name == "postgresql" + def upgrade(): - op.add_column('tracks', sa.Column('type', sa.Enum('ORGANIC', 'SYNTHETIC', name='tracktype'), nullable=True)) + if is_postgres: + tracktype = sa.dialects.postgresql.ENUM("ORGANIC", "SYNTHETIC", name="tracktype") + tracktype.create(op.get_bind()) + op.add_column("tracks", sa.Column("type", tracktype, nullable=True)) + else: + op.add_column('tracks', sa.Column('type', sa.Enum('ORGANIC', 'SYNTHETIC', name='tracktype'), nullable=True)) op.execute("UPDATE tracks SET type='ORGANIC';") def downgrade(): op.drop_column('tracks', 'type') + if is_postgres: + op.execute("DROP TYPE tracktype;") diff --git a/fietsboek/alembic/versions/20230203_3149aa2d0114.py b/fietsboek/alembic/versions/20230203_3149aa2d0114.py index eb9ef78..ced8639 100644 --- a/fietsboek/alembic/versions/20230203_3149aa2d0114.py +++ b/fietsboek/alembic/versions/20230203_3149aa2d0114.py @@ -16,7 +16,7 @@ depends_on = None def upgrade(): op.add_column('tracks', sa.Column('transformers', sa.JSON(), nullable=True)) - op.execute('UPDATE tracks SET transformers="[]";') + op.execute("UPDATE tracks SET transformers='[]';") def downgrade(): op.drop_column('tracks', 'transformers') diff --git a/fietsboek/alembic/versions/20250607_2ebe1bf66430.py b/fietsboek/alembic/versions/20250607_2ebe1bf66430.py new file mode 100644 index 0000000..d7c811e --- /dev/null +++ b/fietsboek/alembic/versions/20250607_2ebe1bf66430.py @@ -0,0 +1,46 @@ +"""switch transfomers from JSON to TEXT + +Revision ID: 2ebe1bf66430 +Revises: 4566843039d6 +Create Date: 2025-06-07 23:24:33.182649 + +""" +import logging + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '2ebe1bf66430' +down_revision = '4566843039d6' +branch_labels = None +depends_on = None + +is_sqlite = op.get_bind().dialect.name == "sqlite" + +def upgrade(): + if is_sqlite: + op.add_column('tracks', sa.Column('transformers_text', sa.Text, nullable=True)) + op.execute('UPDATE tracks SET transformers_text=transformers;') + try: + op.drop_column('tracks', 'transformers') + except sa.exc.OperationalError as exc: + logging.getLogger(__name__).warning( + "Your SQLite version does not support dropping a column. " + "We're setting the content to NULL instead: %s", + exc, + ) + op.execute("UPDATE tracks SET transformers = NULL;") + op.alter_column("tracks", "transformers", new_column_name="transformers_old_delete_this_column") + op.alter_column('tracks', 'transformers_text', new_column_name='transformers') + else: + op.alter_column('tracks', 'transformers', type_=sa.Text) + +def downgrade(): + if is_sqlite: + op.add_column('tracks', sa.Column('transfomers_json', sa.JSON, nullable=True)) + op.execute('UPDATE tracks SET transformers_json=transformers;') + op.drop_column('tracks', 'transformers') + op.alter_column('tracks', 'transformers_json', new_column_name='transformers') + else: + op.alter_column('tracks', 'transformers', type_=sa.JSON) diff --git a/fietsboek/config.py b/fietsboek/config.py index af946b4..beea820 100644 --- a/fietsboek/config.py +++ b/fietsboek/config.py @@ -19,6 +19,7 @@ import re import typing import urllib.parse from enum import Enum +from itertools import chain import pydantic from pydantic import ( @@ -277,6 +278,27 @@ class Config(BaseModel): hasher.update(what_for.encode("utf-8")) return hasher.hexdigest() + def public_tile_layers(self) -> list[TileLayerConfig]: + """Returns all tile layer configs that are public. + + :return: A list of public :class:`TileLayerConfig`s. + """ + # pylint: disable=import-outside-toplevel,cyclic-import + from .views.tileproxy import DEFAULT_TILE_LAYERS, extract_tile_layers + + return [ + source + for source in chain( + ( + default_layer + for default_layer in DEFAULT_TILE_LAYERS + if default_layer.layer_id in self.default_tile_layers + ), + extract_tile_layers(self), + ) + if source.access == LayerAccess.PUBLIC + ] + def parse(config: dict) -> Config: """Parses the configuration into a :class:`Config`. diff --git a/fietsboek/convert.py b/fietsboek/convert.py index 3c8208b..d3bfb22 100644 --- a/fietsboek/convert.py +++ b/fietsboek/convert.py @@ -49,4 +49,20 @@ def from_fit(data: bytes) -> GPX: return gpx -__all__ = ["from_fit"] +def smart_convert(data: bytes) -> bytes: + """Tries to be smart in converting the input bytes. + + This function automatically applies the correct conversion if possible. + + Note that this function is not guaranteed to return valid GPX bytes. In the worst case, + invalid bytes are simply passed through. + + :param data: The input bytes. + :return: The converted content. + """ + if len(data) > 11 and data[9:12] == b"FIT": + return from_fit(data).to_xml().encode("utf-8") + return data + + +__all__ = ["from_fit", "smart_convert"] diff --git a/fietsboek/data.py b/fietsboek/data.py index a7e9b19..9d5a133 100644 --- a/fietsboek/data.py +++ b/fietsboek/data.py @@ -9,7 +9,6 @@ the database itself. This module makes access to such data objects easier. # pylint: disable=deprecated-argument import datetime import logging -import os import random import shutil import string @@ -130,6 +129,27 @@ class DataManager: raise FileNotFoundError(f"The path {path} is not a directory") from None return UserDataDir(user_id, path) + def size(self) -> int: + """Returns the size of all data. + + :return: The size of all data in bytes. + """ + return util.recursive_size(self.data_dir) + + def list_tracks(self) -> list[int]: + """Returns a list of all tracks. + + :return: A list of all track IDs. + """ + return [int(track.name) for track in self._track_data_dir(".").iterdir()] + + def list_users(self) -> list[int]: + """Returns a list of all users. + + :return: A list of all user IDs. + """ + return [int(user.name) for user in self._user_data_dir(".").iterdir()] + class TrackDataDir: """Manager for a single track's data. @@ -191,6 +211,12 @@ class TrackDataDir: elif action == "delete_image": path, data = rest path.write_bytes(data) + elif action == "set_preview": + old_data = rest + if old_data is None: + self.preview_path().unlink() + else: + self.preview_path().write_bytes(old_data) self.journal = None @@ -218,6 +244,9 @@ class TrackDataDir: elif action == "delete_image": # Again, nothing to do here, we simply discard the in-memory image data pass + elif action == "set_preview": + # Still nothing to do here + pass self.journal = None @@ -252,10 +281,7 @@ class TrackDataDir: :return: The size of bytes that this track consumes. """ - size = 0 - for root, _, files in os.walk(self.path): - size += sum(os.path.getsize(os.path.join(root, fname)) for fname in files) - return size + return util.recursive_size(self.path) def gpx_path(self) -> Path: """Returns the path of the GPX file. @@ -406,6 +432,26 @@ class TrackDataDir: path.unlink() + def preview_path(self) -> Path: + """Gets the path to the "preview image". + + :return: The path to the preview image. + """ + return self.path / "preview.png" + + def set_preview(self, data: bytes): + """Sets the preview image to the given data. + + :param data: The data of the preview image. + """ + if self.journal is not None: + try: + previous_preview = self.preview_path().read_bytes() + except FileNotFoundError: + previous_preview = None + self.journal.append(("set_preview", previous_preview)) + self.preview_path().write_bytes(data) + class UserDataDir: """Manager for a single user's data.""" diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.mo b/fietsboek/locale/de/LC_MESSAGES/messages.mo Binary files differindex 9de152c..6526f6f 100644 --- a/fietsboek/locale/de/LC_MESSAGES/messages.mo +++ b/fietsboek/locale/de/LC_MESSAGES/messages.mo diff --git a/fietsboek/locale/de/LC_MESSAGES/messages.po b/fietsboek/locale/de/LC_MESSAGES/messages.po index ccb46a3..f306a46 100644 --- a/fietsboek/locale/de/LC_MESSAGES/messages.po +++ b/fietsboek/locale/de/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-01-30 21:50+0100\n" +"POT-Creation-Date: 2025-06-12 22:39+0200\n" "PO-Revision-Date: 2022-07-02 17:35+0200\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language: de\n" @@ -16,87 +16,185 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.17.0\n" -#: fietsboek/actions.py:267 +#: fietsboek/actions.py:278 msgid "email.verify_mail.subject" msgstr "Fietsboek Konto Bestätigung" -#: fietsboek/actions.py:270 +#: fietsboek/actions.py:281 msgid "email.verify.text" msgstr "" "Um Dein Fietsboek-Konto zu bestätigen, nutze diesen Link: {}\n" "\n" "Falls Du kein Konto angelegt hast, ignoriere diese E-Mail." -#: fietsboek/util.py:333 +#: fietsboek/util.py:334 msgid "password_constraint.mismatch" msgstr "Passwörter stimmen nicht überein" -#: fietsboek/util.py:335 +#: fietsboek/util.py:336 msgid "password_constraint.length" msgstr "Passwort zu kurz" -#: fietsboek/models/track.py:603 +#: fietsboek/models/track.py:622 msgid "tooltip.table.length" msgstr "Länge" -#: fietsboek/models/track.py:604 +#: fietsboek/models/track.py:623 msgid "tooltip.table.people" msgstr "# Personen" -#: fietsboek/models/track.py:605 +#: fietsboek/models/track.py:624 msgid "tooltip.table.uphill" msgstr "Bergauf" -#: fietsboek/models/track.py:606 +#: fietsboek/models/track.py:625 msgid "tooltip.table.downhill" msgstr "Bergab" -#: fietsboek/models/track.py:607 fietsboek/templates/home.jinja2:7 +#: fietsboek/models/track.py:626 fietsboek/templates/home.jinja2:7 msgid "tooltip.table.moving_time" msgstr "Fahrzeit" -#: fietsboek/models/track.py:608 fietsboek/templates/home.jinja2:8 +#: fietsboek/models/track.py:627 fietsboek/templates/home.jinja2:8 msgid "tooltip.table.stopped_time" msgstr "Haltezeit" -#: fietsboek/models/track.py:610 +#: fietsboek/models/track.py:629 msgid "tooltip.table.max_speed" msgstr "Maximalgeschwindigkeit" -#: fietsboek/models/track.py:614 +#: fietsboek/models/track.py:633 msgid "tooltip.table.avg_speed" msgstr "Durchschnittsgeschwindigkeit" +#: fietsboek/templates/403.jinja2:5 +msgid "403.title" +msgstr "Zugang verboten" + +#: fietsboek/templates/403.jinja2:9 +msgid "403.no_access" +msgstr "Du hast keinen Zugang zu dieser Ressource." + +#: fietsboek/templates/403.jinja2:12 +msgid "403.try_log_in" +msgstr "" +"Falls Du Zugang haben solltest, stelle sicher, dass du korrekt angemeldet" +" bist." + +#: fietsboek/templates/404.jinja2:5 +msgid "404.title" +msgstr "Sackgasse" + +#: fietsboek/templates/404.jinja2:9 +msgid "404.path_not_found" +msgstr "Der gesuchte Weg wurde nicht gefunden." + +#: fietsboek/templates/404.jinja2:12 +msgid "404.choose_different" +msgstr "Bitte such einen anderen Weg." + #: fietsboek/templates/admin.jinja2:5 msgid "page.admin.title" msgstr "Administration" -#: fietsboek/templates/admin.jinja2:7 +#: fietsboek/templates/admin.jinja2:10 +msgid "page.admin.nav.overview" +msgstr "Übersicht" + +#: fietsboek/templates/admin.jinja2:11 +msgid "page.admin.nav.badges" +msgstr "Wappen" + +#: fietsboek/templates/admin_badges.jinja2:5 msgid "page.admin.badges" msgstr "Wappen" -#: fietsboek/templates/admin.jinja2:23 +#: fietsboek/templates/admin_badges.jinja2:21 msgid "page.admin.badge.edit" msgstr "Bearbeiten" -#: fietsboek/templates/admin.jinja2:29 +#: fietsboek/templates/admin_badges.jinja2:22 msgid "page.admin.badge.delete_badge" msgstr "Löschen" -#: fietsboek/templates/admin.jinja2:37 +#: fietsboek/templates/admin_badges.jinja2:35 msgid "page.admin.badges.badge_title" msgstr "Titel" -#: fietsboek/templates/admin.jinja2:41 +#: fietsboek/templates/admin_badges.jinja2:39 msgid "page.admin.badges.badge_image" msgstr "Bild" -#: fietsboek/templates/admin.jinja2:45 +#: fietsboek/templates/admin_badges.jinja2:43 msgid "page.admin.badges.add_badge" msgstr "Hinzufügen" +#: fietsboek/templates/admin_overview.jinja2:5 +msgid "admin.overview.instance_has" +msgstr "Diese Instanz hat" + +#: fietsboek/templates/admin_overview.jinja2:9 +msgid "admin.overview.stat.user" +msgid_plural "admin.overview.stat.users" +msgstr[0] "%(num)d Nutzer:in" +msgstr[1] "%(num)d Nutzer:innen" + +#: fietsboek/templates/admin_overview.jinja2:13 +msgid "admin.overview.stat.track" +msgid_plural "admin.overview.stat.tracks" +msgstr[0] "%(num)d Strecke" +msgstr[1] "%(num)d Strecken" + +#: fietsboek/templates/admin_overview.jinja2:17 +msgid "admin.overview.stats.mib" +msgstr "MiB an Daten" + +#: fietsboek/templates/admin_overview.jinja2:24 +msgid "admin.overview.system_overview" +msgstr "Systemübersicht" + +#: fietsboek/templates/admin_overview.jinja2:28 +msgid "admin.overview.fietsboek_version" +msgstr "Fietsboek-Version" + +#: fietsboek/templates/admin_overview.jinja2:32 +msgid "admin.overview.python_version" +msgstr "Python-Version" + +#: fietsboek/templates/admin_overview.jinja2:36 +msgid "admin.overview.kernel_version" +msgstr "Kernel-Version" + +#: fietsboek/templates/admin_overview.jinja2:40 +msgid "admin.overview.distro_version" +msgstr "Distribution" + +#: fietsboek/templates/admin_overview.jinja2:44 +msgid "admin.overview.last_cronjob" +msgstr "Letzter Cronjob" + +#: fietsboek/templates/admin_overview.jinja2:55 +msgid "admin.overview.storage_graph.label.gpx" +msgstr "GPX" + +#: fietsboek/templates/admin_overview.jinja2:56 +msgid "admin.overview.storage_graph.label.images" +msgstr "Bilder" + +#: fietsboek/templates/admin_overview.jinja2:57 +msgid "admin.overview.storage_graph.label.previews" +msgstr "Vorschaubilder" + +#: fietsboek/templates/admin_overview.jinja2:58 +msgid "admin.overview.storage_graph.label.user_maps" +msgstr "Nutzerkarten" + +#: fietsboek/templates/admin_overview.jinja2:84 +msgid "admin.overview.storage_graph.title" +msgstr "Speicherübersicht" + #: fietsboek/templates/browse.jinja2:4 msgid "page.browse.title" msgstr "Stöbern" @@ -193,73 +291,81 @@ msgstr "Dies ist eine Aufnahme einer Strecke" msgid "page.browse.synthetic_tooltip" msgstr "Dies ist eine geplante Strecke" -#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:103 -#: fietsboek/templates/profile.jinja2:15 +#: fietsboek/templates/browse.jinja2:162 fietsboek/templates/details.jinja2:103 +#: fietsboek/templates/profile_overview.jinja2:20 msgid "page.details.date" msgstr "Datum" -#: fietsboek/templates/browse.jinja2:160 fietsboek/templates/details.jinja2:117 -#: fietsboek/templates/profile.jinja2:17 +#: fietsboek/templates/browse.jinja2:164 fietsboek/templates/details.jinja2:117 +#: fietsboek/templates/profile_overview.jinja2:22 msgid "page.details.length" msgstr "Länge" -#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:108 -#: fietsboek/templates/profile.jinja2:21 +#: fietsboek/templates/browse.jinja2:169 fietsboek/templates/details.jinja2:108 +#: fietsboek/templates/profile_overview.jinja2:26 msgid "page.details.start_time" msgstr "Startzeit" -#: fietsboek/templates/browse.jinja2:167 fietsboek/templates/details.jinja2:112 -#: fietsboek/templates/profile.jinja2:23 +#: fietsboek/templates/browse.jinja2:171 fietsboek/templates/details.jinja2:112 +#: fietsboek/templates/profile_overview.jinja2:28 msgid "page.details.end_time" msgstr "Endzeit" -#: fietsboek/templates/browse.jinja2:172 fietsboek/templates/details.jinja2:121 -#: fietsboek/templates/profile.jinja2:27 +#: fietsboek/templates/browse.jinja2:176 fietsboek/templates/details.jinja2:121 +#: fietsboek/templates/profile_overview.jinja2:32 msgid "page.details.uphill" msgstr "Bergauf" -#: fietsboek/templates/browse.jinja2:174 fietsboek/templates/details.jinja2:125 -#: fietsboek/templates/profile.jinja2:29 +#: fietsboek/templates/browse.jinja2:178 fietsboek/templates/details.jinja2:125 +#: fietsboek/templates/profile_overview.jinja2:34 msgid "page.details.downhill" msgstr "Bergab" -#: fietsboek/templates/browse.jinja2:179 fietsboek/templates/details.jinja2:130 -#: fietsboek/templates/profile.jinja2:33 +#: fietsboek/templates/browse.jinja2:183 fietsboek/templates/details.jinja2:130 +#: fietsboek/templates/profile_overview.jinja2:38 msgid "page.details.moving_time" msgstr "Fahrzeit" -#: fietsboek/templates/browse.jinja2:181 fietsboek/templates/details.jinja2:134 -#: fietsboek/templates/profile.jinja2:35 +#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:134 +#: fietsboek/templates/profile_overview.jinja2:40 msgid "page.details.stopped_time" msgstr "Haltezeit" -#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:138 -#: fietsboek/templates/profile.jinja2:39 +#: fietsboek/templates/browse.jinja2:189 fietsboek/templates/details.jinja2:138 +#: fietsboek/templates/profile_overview.jinja2:44 msgid "page.details.max_speed" msgstr "maximale Geschwindigkeit" -#: fietsboek/templates/browse.jinja2:187 fietsboek/templates/details.jinja2:142 -#: fietsboek/templates/profile.jinja2:41 +#: fietsboek/templates/browse.jinja2:191 fietsboek/templates/details.jinja2:142 +#: fietsboek/templates/profile_overview.jinja2:46 msgid "page.details.avg_speed" msgstr "durchschnittliche Geschwindigkeit" -#: fietsboek/templates/browse.jinja2:192 +#: fietsboek/templates/browse.jinja2:196 msgid "page.browse.card.comments" msgstr "Kommentare" -#: fietsboek/templates/browse.jinja2:194 +#: fietsboek/templates/browse.jinja2:198 msgid "page.browse.card.images" msgstr "Bilder" -#: fietsboek/templates/browse.jinja2:211 +#: fietsboek/templates/browse.jinja2:216 msgid "page.browse.download_multiple" msgstr "ausgewählte Herunterladen" -#: fietsboek/templates/browse.jinja2:213 +#: fietsboek/templates/browse.jinja2:222 fietsboek/templates/browse.jinja2:226 +msgid "pagination.previous" +msgstr "Vorherige" + +#: fietsboek/templates/browse.jinja2:231 fietsboek/templates/browse.jinja2:235 +msgid "pagination.next" +msgstr "Nächste" + +#: fietsboek/templates/browse.jinja2:242 msgid "page.browse.no_results" msgstr "Es wurden keine Strecken gefunden, die den Filtern entsprechen." -#: fietsboek/templates/browse.jinja2:215 +#: fietsboek/templates/browse.jinja2:244 msgid "page.browse.no_tracks" msgstr "" "Es wurden keine Strecken gefunden, auf die Du Zugriff hast. Versuche, " @@ -396,11 +502,15 @@ msgstr "Absenden" msgid "page.edit.title" msgstr "Strecke Bearbeiten" -#: fietsboek/templates/edit.jinja2:16 +#: fietsboek/templates/edit.jinja2:14 +msgid "page.edit.form.new_track" +msgstr "Neue Streckendatei auswählen" + +#: fietsboek/templates/edit.jinja2:20 msgid "page.edit.form.submit" msgstr "Speichern" -#: fietsboek/templates/edit.jinja2:17 +#: fietsboek/templates/edit.jinja2:21 msgid "page.edit.form.cancel" msgstr "Abbrechen" @@ -544,13 +654,13 @@ msgstr "" "Links, um sie fortzusetzen:" #: fietsboek/templates/home.jinja2:44 fietsboek/templates/home.jinja2:53 -#: fietsboek/templates/home.jinja2:97 +#: fietsboek/templates/home.jinja2:99 msgid "page.home.summary.track" msgid_plural "page.home.summary.tracks" msgstr[0] "%(num)d Strecke" msgstr[1] "%(num)d Strecken" -#: fietsboek/templates/home.jinja2:97 +#: fietsboek/templates/home.jinja2:99 msgid "page.home.total" msgstr "Gesamt" @@ -654,91 +764,91 @@ msgstr "Passwörter stimmen nicht überein" msgid "page.password_reset.reset" msgstr "Zurücksetzen" -#: fietsboek/templates/profile.jinja2:64 +#: fietsboek/templates/profile.jinja2:10 msgid "page.profile.tabbar.overview" msgstr "Übersicht" -#: fietsboek/templates/profile.jinja2:69 +#: fietsboek/templates/profile.jinja2:15 msgid "page.profile.tabbar.graphs" msgstr "Diagramme" -#: fietsboek/templates/profile.jinja2:74 +#: fietsboek/templates/profile.jinja2:20 msgid "page.profile.tabbar.calendar" msgstr "Kalender" -#: fietsboek/templates/profile.jinja2:88 +#: fietsboek/templates/profile_calendar.jinja2:9 +msgid "page.profile.calendar.previous" +msgstr "Vorheriger Monat" + +#: fietsboek/templates/profile_calendar.jinja2:11 +msgid "page.profile.calendar.next" +msgstr "Nächster Monat" + +#: fietsboek/templates/profile_graphs.jinja2:6 +msgid "page.profile.graph.km_per_month" +msgstr "Kilometer pro Monat" + +#: fietsboek/templates/profile_overview.jinja2:71 msgid "page.profile.length" msgstr "Länge" -#: fietsboek/templates/profile.jinja2:92 +#: fietsboek/templates/profile_overview.jinja2:75 msgid "page.profile.avg_length" msgstr "durchschnittliche Länge" -#: fietsboek/templates/profile.jinja2:96 +#: fietsboek/templates/profile_overview.jinja2:79 msgid "page.profile.uphill" msgstr "Bergauf" -#: fietsboek/templates/profile.jinja2:100 +#: fietsboek/templates/profile_overview.jinja2:83 msgid "page.profile.downhill" msgstr "Bergab" -#: fietsboek/templates/profile.jinja2:104 +#: fietsboek/templates/profile_overview.jinja2:87 msgid "page.profile.moving_time" msgstr "Fahrzeit" -#: fietsboek/templates/profile.jinja2:108 +#: fietsboek/templates/profile_overview.jinja2:91 msgid "page.profile.stopped_time" msgstr "Haltezeit" -#: fietsboek/templates/profile.jinja2:112 +#: fietsboek/templates/profile_overview.jinja2:95 msgid "page.profile.avg_duration" msgstr "durchschnittliche Dauer" -#: fietsboek/templates/profile.jinja2:116 +#: fietsboek/templates/profile_overview.jinja2:99 msgid "page.profile.max_speed" msgstr "maximale Geschwindigkeit" -#: fietsboek/templates/profile.jinja2:120 +#: fietsboek/templates/profile_overview.jinja2:103 msgid "page.profile.avg_speed" msgstr "durchschnittliche Geschwindigkeit" -#: fietsboek/templates/profile.jinja2:124 +#: fietsboek/templates/profile_overview.jinja2:107 msgid "page.profile.number_of_tracks" msgstr "Anzahl der Strecken" -#: fietsboek/templates/profile.jinja2:130 +#: fietsboek/templates/profile_overview.jinja2:113 msgid "page.profile.longest_distance_track" msgstr "Weiteste Strecke" -#: fietsboek/templates/profile.jinja2:135 +#: fietsboek/templates/profile_overview.jinja2:118 msgid "page.profile.shortest_distance_track" msgstr "Kürzeste Strecke" -#: fietsboek/templates/profile.jinja2:140 +#: fietsboek/templates/profile_overview.jinja2:123 msgid "page.profile.longest_duration_track" msgstr "Am Längsten Dauernde Strecke" -#: fietsboek/templates/profile.jinja2:145 +#: fietsboek/templates/profile_overview.jinja2:128 msgid "page.profile.shortest_duration_track" msgstr "Am Kürzesten Dauernde Strecke" -#: fietsboek/templates/profile.jinja2:152 -msgid "page.profile.graph.km_per_month" -msgstr "Kilometer pro Monat" - -#: fietsboek/templates/profile.jinja2:161 -msgid "page.profile.calendar.previous" -msgstr "Vorheriger Monat" - -#: fietsboek/templates/profile.jinja2:163 -msgid "page.profile.calendar.next" -msgstr "Nächster Monat" - -#: fietsboek/templates/profile.jinja2:218 +#: fietsboek/templates/profile_overview.jinja2:149 msgid "page.profile.heatmap" msgstr "Heatmap" -#: fietsboek/templates/profile.jinja2:223 +#: fietsboek/templates/profile_overview.jinja2:157 msgid "page.profile.tilehunt" msgstr "Kacheljäger" @@ -888,15 +998,15 @@ msgstr "Ungültige E-Mail-Adresse" msgid "flash.a_confirmation_link_has_been_sent" msgstr "Ein Bestätigungslink wurde versandt" -#: fietsboek/views/admin.py:49 +#: fietsboek/views/admin.py:156 msgid "flash.badge_added" msgstr "Wappen hinzugefügt" -#: fietsboek/views/admin.py:73 +#: fietsboek/views/admin.py:180 msgid "flash.badge_modified" msgstr "Wappen bearbeitet" -#: fietsboek/views/admin.py:93 +#: fietsboek/views/admin.py:200 msgid "flash.badge_deleted" msgstr "Wappen gelöscht" @@ -956,23 +1066,23 @@ msgstr "E-Mail-Adresse bestätigt" msgid "flash.password_updated" msgstr "Passwort aktualisiert" -#: fietsboek/views/detail.py:162 +#: fietsboek/views/detail.py:166 msgid "flash.track_deleted" msgstr "Strecke gelöscht" -#: fietsboek/views/upload.py:53 -msgid "flash.no_file_selected" -msgstr "Keine Datei ausgewählt" - -#: fietsboek/views/upload.py:66 +#: fietsboek/views/edit.py:100 fietsboek/views/upload.py:66 msgid "flash.invalid_file" msgstr "Ungültige GPX-Datei gesendet" -#: fietsboek/views/upload.py:192 +#: fietsboek/views/upload.py:54 +msgid "flash.no_file_selected" +msgstr "Keine Datei ausgewählt" + +#: fietsboek/views/upload.py:194 msgid "flash.upload_success" msgstr "Hochladen erfolgreich" -#: fietsboek/views/upload.py:211 +#: fietsboek/views/upload.py:213 msgid "flash.upload_cancelled" msgstr "Hochladen abgebrochen" diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.mo b/fietsboek/locale/en/LC_MESSAGES/messages.mo Binary files differindex 5f8edc6..18f473c 100644 --- a/fietsboek/locale/en/LC_MESSAGES/messages.mo +++ b/fietsboek/locale/en/LC_MESSAGES/messages.mo diff --git a/fietsboek/locale/en/LC_MESSAGES/messages.po b/fietsboek/locale/en/LC_MESSAGES/messages.po index 981d134..89e183d 100644 --- a/fietsboek/locale/en/LC_MESSAGES/messages.po +++ b/fietsboek/locale/en/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-01-30 21:50+0100\n" +"POT-Creation-Date: 2025-06-12 22:39+0200\n" "PO-Revision-Date: 2023-04-03 20:42+0200\n" "Last-Translator: \n" "Language: en\n" @@ -16,87 +16,185 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.17.0\n" -#: fietsboek/actions.py:267 +#: fietsboek/actions.py:278 msgid "email.verify_mail.subject" msgstr "Fietsboek Account Verification" -#: fietsboek/actions.py:270 +#: fietsboek/actions.py:281 msgid "email.verify.text" msgstr "" "To verify your Fietsboek account, please use this link: {}\n" "\n" "If you did not create an account, ignore this email." -#: fietsboek/util.py:333 +#: fietsboek/util.py:334 msgid "password_constraint.mismatch" msgstr "Passwords don't match" -#: fietsboek/util.py:335 +#: fietsboek/util.py:336 msgid "password_constraint.length" msgstr "Password not long enough" -#: fietsboek/models/track.py:603 +#: fietsboek/models/track.py:622 msgid "tooltip.table.length" msgstr "Length" -#: fietsboek/models/track.py:604 +#: fietsboek/models/track.py:623 msgid "tooltip.table.people" msgstr "# People" -#: fietsboek/models/track.py:605 +#: fietsboek/models/track.py:624 msgid "tooltip.table.uphill" msgstr "Uphill" -#: fietsboek/models/track.py:606 +#: fietsboek/models/track.py:625 msgid "tooltip.table.downhill" msgstr "Downhill" -#: fietsboek/models/track.py:607 fietsboek/templates/home.jinja2:7 +#: fietsboek/models/track.py:626 fietsboek/templates/home.jinja2:7 msgid "tooltip.table.moving_time" msgstr "Moving Time" -#: fietsboek/models/track.py:608 fietsboek/templates/home.jinja2:8 +#: fietsboek/models/track.py:627 fietsboek/templates/home.jinja2:8 msgid "tooltip.table.stopped_time" msgstr "Stopped Time" -#: fietsboek/models/track.py:610 +#: fietsboek/models/track.py:629 msgid "tooltip.table.max_speed" msgstr "Max Speed" -#: fietsboek/models/track.py:614 +#: fietsboek/models/track.py:633 msgid "tooltip.table.avg_speed" msgstr "Average Speed" +#: fietsboek/templates/403.jinja2:5 +msgid "403.title" +msgstr "No entry" + +#: fietsboek/templates/403.jinja2:9 +msgid "403.no_access" +msgstr "You are not allowed to access this resource." + +#: fietsboek/templates/403.jinja2:12 +msgid "403.try_log_in" +msgstr "" +"If you should have access, make sure you are logged in with the right " +"credentials." + +#: fietsboek/templates/404.jinja2:5 +msgid "404.title" +msgstr "Dead end" + +#: fietsboek/templates/404.jinja2:9 +msgid "404.path_not_found" +msgstr "The path you have chosen was not found." + +#: fietsboek/templates/404.jinja2:12 +msgid "404.choose_different" +msgstr "Please choose a different path." + #: fietsboek/templates/admin.jinja2:5 msgid "page.admin.title" msgstr "Administration" -#: fietsboek/templates/admin.jinja2:7 +#: fietsboek/templates/admin.jinja2:10 +msgid "page.admin.nav.overview" +msgstr "Overview" + +#: fietsboek/templates/admin.jinja2:11 +msgid "page.admin.nav.badges" +msgstr "Badges" + +#: fietsboek/templates/admin_badges.jinja2:5 msgid "page.admin.badges" msgstr "Badges" -#: fietsboek/templates/admin.jinja2:23 +#: fietsboek/templates/admin_badges.jinja2:21 msgid "page.admin.badge.edit" msgstr "Edit" -#: fietsboek/templates/admin.jinja2:29 +#: fietsboek/templates/admin_badges.jinja2:22 msgid "page.admin.badge.delete_badge" msgstr "Delete badge" -#: fietsboek/templates/admin.jinja2:37 +#: fietsboek/templates/admin_badges.jinja2:35 msgid "page.admin.badges.badge_title" msgstr "Badge Title" -#: fietsboek/templates/admin.jinja2:41 +#: fietsboek/templates/admin_badges.jinja2:39 msgid "page.admin.badges.badge_image" msgstr "Badge Image" -#: fietsboek/templates/admin.jinja2:45 +#: fietsboek/templates/admin_badges.jinja2:43 msgid "page.admin.badges.add_badge" msgstr "Add Badge" +#: fietsboek/templates/admin_overview.jinja2:5 +msgid "admin.overview.instance_has" +msgstr "This instance has" + +#: fietsboek/templates/admin_overview.jinja2:9 +msgid "admin.overview.stat.user" +msgid_plural "admin.overview.stat.users" +msgstr[0] "%(num)d user" +msgstr[1] "%(num)d users" + +#: fietsboek/templates/admin_overview.jinja2:13 +msgid "admin.overview.stat.track" +msgid_plural "admin.overview.stat.tracks" +msgstr[0] "%(num)d track" +msgstr[1] "%(num)d tracks" + +#: fietsboek/templates/admin_overview.jinja2:17 +msgid "admin.overview.stats.mib" +msgstr "MiB of data" + +#: fietsboek/templates/admin_overview.jinja2:24 +msgid "admin.overview.system_overview" +msgstr "System information" + +#: fietsboek/templates/admin_overview.jinja2:28 +msgid "admin.overview.fietsboek_version" +msgstr "Fietsboek version" + +#: fietsboek/templates/admin_overview.jinja2:32 +msgid "admin.overview.python_version" +msgstr "Python version" + +#: fietsboek/templates/admin_overview.jinja2:36 +msgid "admin.overview.kernel_version" +msgstr "Linux version" + +#: fietsboek/templates/admin_overview.jinja2:40 +msgid "admin.overview.distro_version" +msgstr "Distribution" + +#: fietsboek/templates/admin_overview.jinja2:44 +msgid "admin.overview.last_cronjob" +msgstr "Last cronjob" + +#: fietsboek/templates/admin_overview.jinja2:55 +msgid "admin.overview.storage_graph.label.gpx" +msgstr "GPX" + +#: fietsboek/templates/admin_overview.jinja2:56 +msgid "admin.overview.storage_graph.label.images" +msgstr "Images" + +#: fietsboek/templates/admin_overview.jinja2:57 +msgid "admin.overview.storage_graph.label.previews" +msgstr "Preview images" + +#: fietsboek/templates/admin_overview.jinja2:58 +msgid "admin.overview.storage_graph.label.user_maps" +msgstr "User maps" + +#: fietsboek/templates/admin_overview.jinja2:84 +msgid "admin.overview.storage_graph.title" +msgstr "Storage breakdown" + #: fietsboek/templates/browse.jinja2:4 msgid "page.browse.title" msgstr "Browse" @@ -193,73 +291,81 @@ msgstr "This is a recording of a track" msgid "page.browse.synthetic_tooltip" msgstr "This is a pre-planned track" -#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:103 -#: fietsboek/templates/profile.jinja2:15 +#: fietsboek/templates/browse.jinja2:162 fietsboek/templates/details.jinja2:103 +#: fietsboek/templates/profile_overview.jinja2:20 msgid "page.details.date" msgstr "Date" -#: fietsboek/templates/browse.jinja2:160 fietsboek/templates/details.jinja2:117 -#: fietsboek/templates/profile.jinja2:17 +#: fietsboek/templates/browse.jinja2:164 fietsboek/templates/details.jinja2:117 +#: fietsboek/templates/profile_overview.jinja2:22 msgid "page.details.length" msgstr "Length" -#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:108 -#: fietsboek/templates/profile.jinja2:21 +#: fietsboek/templates/browse.jinja2:169 fietsboek/templates/details.jinja2:108 +#: fietsboek/templates/profile_overview.jinja2:26 msgid "page.details.start_time" msgstr "Record Start" -#: fietsboek/templates/browse.jinja2:167 fietsboek/templates/details.jinja2:112 -#: fietsboek/templates/profile.jinja2:23 +#: fietsboek/templates/browse.jinja2:171 fietsboek/templates/details.jinja2:112 +#: fietsboek/templates/profile_overview.jinja2:28 msgid "page.details.end_time" msgstr "Record End" -#: fietsboek/templates/browse.jinja2:172 fietsboek/templates/details.jinja2:121 -#: fietsboek/templates/profile.jinja2:27 +#: fietsboek/templates/browse.jinja2:176 fietsboek/templates/details.jinja2:121 +#: fietsboek/templates/profile_overview.jinja2:32 msgid "page.details.uphill" msgstr "Uphill" -#: fietsboek/templates/browse.jinja2:174 fietsboek/templates/details.jinja2:125 -#: fietsboek/templates/profile.jinja2:29 +#: fietsboek/templates/browse.jinja2:178 fietsboek/templates/details.jinja2:125 +#: fietsboek/templates/profile_overview.jinja2:34 msgid "page.details.downhill" msgstr "Downhill" -#: fietsboek/templates/browse.jinja2:179 fietsboek/templates/details.jinja2:130 -#: fietsboek/templates/profile.jinja2:33 +#: fietsboek/templates/browse.jinja2:183 fietsboek/templates/details.jinja2:130 +#: fietsboek/templates/profile_overview.jinja2:38 msgid "page.details.moving_time" msgstr "Moving Time" -#: fietsboek/templates/browse.jinja2:181 fietsboek/templates/details.jinja2:134 -#: fietsboek/templates/profile.jinja2:35 +#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:134 +#: fietsboek/templates/profile_overview.jinja2:40 msgid "page.details.stopped_time" msgstr "Stopped Time" -#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:138 -#: fietsboek/templates/profile.jinja2:39 +#: fietsboek/templates/browse.jinja2:189 fietsboek/templates/details.jinja2:138 +#: fietsboek/templates/profile_overview.jinja2:44 msgid "page.details.max_speed" msgstr "Max Speed" -#: fietsboek/templates/browse.jinja2:187 fietsboek/templates/details.jinja2:142 -#: fietsboek/templates/profile.jinja2:41 +#: fietsboek/templates/browse.jinja2:191 fietsboek/templates/details.jinja2:142 +#: fietsboek/templates/profile_overview.jinja2:46 msgid "page.details.avg_speed" msgstr "Average Speed" -#: fietsboek/templates/browse.jinja2:192 +#: fietsboek/templates/browse.jinja2:196 msgid "page.browse.card.comments" msgstr "Comments" -#: fietsboek/templates/browse.jinja2:194 +#: fietsboek/templates/browse.jinja2:198 msgid "page.browse.card.images" msgstr "Images" -#: fietsboek/templates/browse.jinja2:211 +#: fietsboek/templates/browse.jinja2:216 msgid "page.browse.download_multiple" msgstr "Download selected" -#: fietsboek/templates/browse.jinja2:213 +#: fietsboek/templates/browse.jinja2:222 fietsboek/templates/browse.jinja2:226 +msgid "pagination.previous" +msgstr "Previous" + +#: fietsboek/templates/browse.jinja2:231 fietsboek/templates/browse.jinja2:235 +msgid "pagination.next" +msgstr "Next" + +#: fietsboek/templates/browse.jinja2:242 msgid "page.browse.no_results" msgstr "No results matching the filters were found." -#: fietsboek/templates/browse.jinja2:215 +#: fietsboek/templates/browse.jinja2:244 msgid "page.browse.no_tracks" msgstr "You currently do not have access to any tracks. Try logging in." @@ -392,11 +498,15 @@ msgstr "Submit" msgid "page.edit.title" msgstr "Edit Track" -#: fietsboek/templates/edit.jinja2:16 +#: fietsboek/templates/edit.jinja2:14 +msgid "page.edit.form.new_track" +msgstr "New file for this track" + +#: fietsboek/templates/edit.jinja2:20 msgid "page.edit.form.submit" msgstr "Save" -#: fietsboek/templates/edit.jinja2:17 +#: fietsboek/templates/edit.jinja2:21 msgid "page.edit.form.cancel" msgstr "Cancel" @@ -538,13 +648,13 @@ msgid "page.home.unfinished_uploads" msgstr "You have unfinished uploads. Click on the links below to resume them:" #: fietsboek/templates/home.jinja2:44 fietsboek/templates/home.jinja2:53 -#: fietsboek/templates/home.jinja2:97 +#: fietsboek/templates/home.jinja2:99 msgid "page.home.summary.track" msgid_plural "page.home.summary.tracks" msgstr[0] "%(num)d track" msgstr[1] "%(num)d tracks" -#: fietsboek/templates/home.jinja2:97 +#: fietsboek/templates/home.jinja2:99 msgid "page.home.total" msgstr "Total" @@ -648,91 +758,91 @@ msgstr "Passwords must match" msgid "page.password_reset.reset" msgstr "Reset" -#: fietsboek/templates/profile.jinja2:64 +#: fietsboek/templates/profile.jinja2:10 msgid "page.profile.tabbar.overview" msgstr "Overview" -#: fietsboek/templates/profile.jinja2:69 +#: fietsboek/templates/profile.jinja2:15 msgid "page.profile.tabbar.graphs" msgstr "Graphs" -#: fietsboek/templates/profile.jinja2:74 +#: fietsboek/templates/profile.jinja2:20 msgid "page.profile.tabbar.calendar" msgstr "Calendar" -#: fietsboek/templates/profile.jinja2:88 +#: fietsboek/templates/profile_calendar.jinja2:9 +msgid "page.profile.calendar.previous" +msgstr "Previous month" + +#: fietsboek/templates/profile_calendar.jinja2:11 +msgid "page.profile.calendar.next" +msgstr "Next month" + +#: fietsboek/templates/profile_graphs.jinja2:6 +msgid "page.profile.graph.km_per_month" +msgstr "Kilometers per month" + +#: fietsboek/templates/profile_overview.jinja2:71 msgid "page.profile.length" msgstr "Length" -#: fietsboek/templates/profile.jinja2:92 +#: fietsboek/templates/profile_overview.jinja2:75 msgid "page.profile.avg_length" msgstr "Average Length" -#: fietsboek/templates/profile.jinja2:96 +#: fietsboek/templates/profile_overview.jinja2:79 msgid "page.profile.uphill" msgstr "Uphill" -#: fietsboek/templates/profile.jinja2:100 +#: fietsboek/templates/profile_overview.jinja2:83 msgid "page.profile.downhill" msgstr "Downhill" -#: fietsboek/templates/profile.jinja2:104 +#: fietsboek/templates/profile_overview.jinja2:87 msgid "page.profile.moving_time" msgstr "Moving Time" -#: fietsboek/templates/profile.jinja2:108 +#: fietsboek/templates/profile_overview.jinja2:91 msgid "page.profile.stopped_time" msgstr "Stopped Time" -#: fietsboek/templates/profile.jinja2:112 +#: fietsboek/templates/profile_overview.jinja2:95 msgid "page.profile.avg_duration" msgstr "Average Duration" -#: fietsboek/templates/profile.jinja2:116 +#: fietsboek/templates/profile_overview.jinja2:99 msgid "page.profile.max_speed" msgstr "Max Speed" -#: fietsboek/templates/profile.jinja2:120 +#: fietsboek/templates/profile_overview.jinja2:103 msgid "page.profile.avg_speed" msgstr "Average Speed" -#: fietsboek/templates/profile.jinja2:124 +#: fietsboek/templates/profile_overview.jinja2:107 msgid "page.profile.number_of_tracks" msgstr "Number of tracks" -#: fietsboek/templates/profile.jinja2:130 +#: fietsboek/templates/profile_overview.jinja2:113 msgid "page.profile.longest_distance_track" msgstr "Longest Track" -#: fietsboek/templates/profile.jinja2:135 +#: fietsboek/templates/profile_overview.jinja2:118 msgid "page.profile.shortest_distance_track" msgstr "Shortest Track" -#: fietsboek/templates/profile.jinja2:140 +#: fietsboek/templates/profile_overview.jinja2:123 msgid "page.profile.longest_duration_track" msgstr "Most Time-Consuming Track" -#: fietsboek/templates/profile.jinja2:145 +#: fietsboek/templates/profile_overview.jinja2:128 msgid "page.profile.shortest_duration_track" msgstr "Quickest Track" -#: fietsboek/templates/profile.jinja2:152 -msgid "page.profile.graph.km_per_month" -msgstr "Kilometers per month" - -#: fietsboek/templates/profile.jinja2:161 -msgid "page.profile.calendar.previous" -msgstr "Previous month" - -#: fietsboek/templates/profile.jinja2:163 -msgid "page.profile.calendar.next" -msgstr "Next month" - -#: fietsboek/templates/profile.jinja2:218 +#: fietsboek/templates/profile_overview.jinja2:149 msgid "page.profile.heatmap" msgstr "Heat Map" -#: fietsboek/templates/profile.jinja2:223 +#: fietsboek/templates/profile_overview.jinja2:157 msgid "page.profile.tilehunt" msgstr "Tilehunt" @@ -878,15 +988,15 @@ msgstr "Invalid email" msgid "flash.a_confirmation_link_has_been_sent" msgstr "A confirmation link has been sent" -#: fietsboek/views/admin.py:49 +#: fietsboek/views/admin.py:156 msgid "flash.badge_added" msgstr "Badge has been added" -#: fietsboek/views/admin.py:73 +#: fietsboek/views/admin.py:180 msgid "flash.badge_modified" msgstr "Badge has been modified" -#: fietsboek/views/admin.py:93 +#: fietsboek/views/admin.py:200 msgid "flash.badge_deleted" msgstr "Badge has been deleted" @@ -945,23 +1055,23 @@ msgstr "Your email address has been verified" msgid "flash.password_updated" msgstr "Password has been updated" -#: fietsboek/views/detail.py:162 +#: fietsboek/views/detail.py:166 msgid "flash.track_deleted" msgstr "Track has been deleted" -#: fietsboek/views/upload.py:53 -msgid "flash.no_file_selected" -msgstr "No file selected" - -#: fietsboek/views/upload.py:66 +#: fietsboek/views/edit.py:100 fietsboek/views/upload.py:66 msgid "flash.invalid_file" msgstr "Invalid GPX file selected" -#: fietsboek/views/upload.py:192 +#: fietsboek/views/upload.py:54 +msgid "flash.no_file_selected" +msgstr "No file selected" + +#: fietsboek/views/upload.py:194 msgid "flash.upload_success" msgstr "Upload successful" -#: fietsboek/views/upload.py:211 +#: fietsboek/views/upload.py:213 msgid "flash.upload_cancelled" msgstr "Upload cancelled" diff --git a/fietsboek/locale/fietslog.pot b/fietsboek/locale/fietslog.pot index 60c77a5..6383760 100644 --- a/fietsboek/locale/fietslog.pot +++ b/fietsboek/locale/fietslog.pot @@ -8,91 +8,187 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2025-01-30 21:50+0100\n" +"POT-Creation-Date: 2025-06-12 22:39+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" -"Generated-By: Babel 2.15.0\n" +"Generated-By: Babel 2.17.0\n" -#: fietsboek/actions.py:267 +#: fietsboek/actions.py:278 msgid "email.verify_mail.subject" msgstr "" -#: fietsboek/actions.py:270 +#: fietsboek/actions.py:281 msgid "email.verify.text" msgstr "" -#: fietsboek/util.py:333 +#: fietsboek/util.py:334 msgid "password_constraint.mismatch" msgstr "" -#: fietsboek/util.py:335 +#: fietsboek/util.py:336 msgid "password_constraint.length" msgstr "" -#: fietsboek/models/track.py:603 +#: fietsboek/models/track.py:622 msgid "tooltip.table.length" msgstr "" -#: fietsboek/models/track.py:604 +#: fietsboek/models/track.py:623 msgid "tooltip.table.people" msgstr "" -#: fietsboek/models/track.py:605 +#: fietsboek/models/track.py:624 msgid "tooltip.table.uphill" msgstr "" -#: fietsboek/models/track.py:606 +#: fietsboek/models/track.py:625 msgid "tooltip.table.downhill" msgstr "" -#: fietsboek/models/track.py:607 fietsboek/templates/home.jinja2:7 +#: fietsboek/models/track.py:626 fietsboek/templates/home.jinja2:7 msgid "tooltip.table.moving_time" msgstr "" -#: fietsboek/models/track.py:608 fietsboek/templates/home.jinja2:8 +#: fietsboek/models/track.py:627 fietsboek/templates/home.jinja2:8 msgid "tooltip.table.stopped_time" msgstr "" -#: fietsboek/models/track.py:610 +#: fietsboek/models/track.py:629 msgid "tooltip.table.max_speed" msgstr "" -#: fietsboek/models/track.py:614 +#: fietsboek/models/track.py:633 msgid "tooltip.table.avg_speed" msgstr "" +#: fietsboek/templates/403.jinja2:5 +msgid "403.title" +msgstr "" + +#: fietsboek/templates/403.jinja2:9 +msgid "403.no_access" +msgstr "" + +#: fietsboek/templates/403.jinja2:12 +msgid "403.try_log_in" +msgstr "" + +#: fietsboek/templates/404.jinja2:5 +msgid "404.title" +msgstr "" + +#: fietsboek/templates/404.jinja2:9 +msgid "404.path_not_found" +msgstr "" + +#: fietsboek/templates/404.jinja2:12 +msgid "404.choose_different" +msgstr "" + #: fietsboek/templates/admin.jinja2:5 msgid "page.admin.title" msgstr "" -#: fietsboek/templates/admin.jinja2:7 +#: fietsboek/templates/admin.jinja2:10 +msgid "page.admin.nav.overview" +msgstr "" + +#: fietsboek/templates/admin.jinja2:11 +msgid "page.admin.nav.badges" +msgstr "" + +#: fietsboek/templates/admin_badges.jinja2:5 msgid "page.admin.badges" msgstr "" -#: fietsboek/templates/admin.jinja2:23 +#: fietsboek/templates/admin_badges.jinja2:21 msgid "page.admin.badge.edit" msgstr "" -#: fietsboek/templates/admin.jinja2:29 +#: fietsboek/templates/admin_badges.jinja2:22 msgid "page.admin.badge.delete_badge" msgstr "" -#: fietsboek/templates/admin.jinja2:37 +#: fietsboek/templates/admin_badges.jinja2:35 msgid "page.admin.badges.badge_title" msgstr "" -#: fietsboek/templates/admin.jinja2:41 +#: fietsboek/templates/admin_badges.jinja2:39 msgid "page.admin.badges.badge_image" msgstr "" -#: fietsboek/templates/admin.jinja2:45 +#: fietsboek/templates/admin_badges.jinja2:43 msgid "page.admin.badges.add_badge" msgstr "" +#: fietsboek/templates/admin_overview.jinja2:5 +msgid "admin.overview.instance_has" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:9 +msgid "admin.overview.stat.user" +msgid_plural "admin.overview.stat.users" +msgstr[0] "" +msgstr[1] "" + +#: fietsboek/templates/admin_overview.jinja2:13 +msgid "admin.overview.stat.track" +msgid_plural "admin.overview.stat.tracks" +msgstr[0] "" +msgstr[1] "" + +#: fietsboek/templates/admin_overview.jinja2:17 +msgid "admin.overview.stats.mib" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:24 +msgid "admin.overview.system_overview" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:28 +msgid "admin.overview.fietsboek_version" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:32 +msgid "admin.overview.python_version" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:36 +msgid "admin.overview.kernel_version" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:40 +msgid "admin.overview.distro_version" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:44 +msgid "admin.overview.last_cronjob" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:55 +msgid "admin.overview.storage_graph.label.gpx" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:56 +msgid "admin.overview.storage_graph.label.images" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:57 +msgid "admin.overview.storage_graph.label.previews" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:58 +msgid "admin.overview.storage_graph.label.user_maps" +msgstr "" + +#: fietsboek/templates/admin_overview.jinja2:84 +msgid "admin.overview.storage_graph.title" +msgstr "" + #: fietsboek/templates/browse.jinja2:4 msgid "page.browse.title" msgstr "" @@ -189,73 +285,81 @@ msgstr "" msgid "page.browse.synthetic_tooltip" msgstr "" -#: fietsboek/templates/browse.jinja2:158 fietsboek/templates/details.jinja2:103 -#: fietsboek/templates/profile.jinja2:15 +#: fietsboek/templates/browse.jinja2:162 fietsboek/templates/details.jinja2:103 +#: fietsboek/templates/profile_overview.jinja2:20 msgid "page.details.date" msgstr "" -#: fietsboek/templates/browse.jinja2:160 fietsboek/templates/details.jinja2:117 -#: fietsboek/templates/profile.jinja2:17 +#: fietsboek/templates/browse.jinja2:164 fietsboek/templates/details.jinja2:117 +#: fietsboek/templates/profile_overview.jinja2:22 msgid "page.details.length" msgstr "" -#: fietsboek/templates/browse.jinja2:165 fietsboek/templates/details.jinja2:108 -#: fietsboek/templates/profile.jinja2:21 +#: fietsboek/templates/browse.jinja2:169 fietsboek/templates/details.jinja2:108 +#: fietsboek/templates/profile_overview.jinja2:26 msgid "page.details.start_time" msgstr "" -#: fietsboek/templates/browse.jinja2:167 fietsboek/templates/details.jinja2:112 -#: fietsboek/templates/profile.jinja2:23 +#: fietsboek/templates/browse.jinja2:171 fietsboek/templates/details.jinja2:112 +#: fietsboek/templates/profile_overview.jinja2:28 msgid "page.details.end_time" msgstr "" -#: fietsboek/templates/browse.jinja2:172 fietsboek/templates/details.jinja2:121 -#: fietsboek/templates/profile.jinja2:27 +#: fietsboek/templates/browse.jinja2:176 fietsboek/templates/details.jinja2:121 +#: fietsboek/templates/profile_overview.jinja2:32 msgid "page.details.uphill" msgstr "" -#: fietsboek/templates/browse.jinja2:174 fietsboek/templates/details.jinja2:125 -#: fietsboek/templates/profile.jinja2:29 +#: fietsboek/templates/browse.jinja2:178 fietsboek/templates/details.jinja2:125 +#: fietsboek/templates/profile_overview.jinja2:34 msgid "page.details.downhill" msgstr "" -#: fietsboek/templates/browse.jinja2:179 fietsboek/templates/details.jinja2:130 -#: fietsboek/templates/profile.jinja2:33 +#: fietsboek/templates/browse.jinja2:183 fietsboek/templates/details.jinja2:130 +#: fietsboek/templates/profile_overview.jinja2:38 msgid "page.details.moving_time" msgstr "" -#: fietsboek/templates/browse.jinja2:181 fietsboek/templates/details.jinja2:134 -#: fietsboek/templates/profile.jinja2:35 +#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:134 +#: fietsboek/templates/profile_overview.jinja2:40 msgid "page.details.stopped_time" msgstr "" -#: fietsboek/templates/browse.jinja2:185 fietsboek/templates/details.jinja2:138 -#: fietsboek/templates/profile.jinja2:39 +#: fietsboek/templates/browse.jinja2:189 fietsboek/templates/details.jinja2:138 +#: fietsboek/templates/profile_overview.jinja2:44 msgid "page.details.max_speed" msgstr "" -#: fietsboek/templates/browse.jinja2:187 fietsboek/templates/details.jinja2:142 -#: fietsboek/templates/profile.jinja2:41 +#: fietsboek/templates/browse.jinja2:191 fietsboek/templates/details.jinja2:142 +#: fietsboek/templates/profile_overview.jinja2:46 msgid "page.details.avg_speed" msgstr "" -#: fietsboek/templates/browse.jinja2:192 +#: fietsboek/templates/browse.jinja2:196 msgid "page.browse.card.comments" msgstr "" -#: fietsboek/templates/browse.jinja2:194 +#: fietsboek/templates/browse.jinja2:198 msgid "page.browse.card.images" msgstr "" -#: fietsboek/templates/browse.jinja2:211 +#: fietsboek/templates/browse.jinja2:216 msgid "page.browse.download_multiple" msgstr "" -#: fietsboek/templates/browse.jinja2:213 +#: fietsboek/templates/browse.jinja2:222 fietsboek/templates/browse.jinja2:226 +msgid "pagination.previous" +msgstr "" + +#: fietsboek/templates/browse.jinja2:231 fietsboek/templates/browse.jinja2:235 +msgid "pagination.next" +msgstr "" + +#: fietsboek/templates/browse.jinja2:242 msgid "page.browse.no_results" msgstr "" -#: fietsboek/templates/browse.jinja2:215 +#: fietsboek/templates/browse.jinja2:244 msgid "page.browse.no_tracks" msgstr "" @@ -388,11 +492,15 @@ msgstr "" msgid "page.edit.title" msgstr "" -#: fietsboek/templates/edit.jinja2:16 +#: fietsboek/templates/edit.jinja2:14 +msgid "page.edit.form.new_track" +msgstr "" + +#: fietsboek/templates/edit.jinja2:20 msgid "page.edit.form.submit" msgstr "" -#: fietsboek/templates/edit.jinja2:17 +#: fietsboek/templates/edit.jinja2:21 msgid "page.edit.form.cancel" msgstr "" @@ -532,13 +640,13 @@ msgid "page.home.unfinished_uploads" msgstr "" #: fietsboek/templates/home.jinja2:44 fietsboek/templates/home.jinja2:53 -#: fietsboek/templates/home.jinja2:97 +#: fietsboek/templates/home.jinja2:99 msgid "page.home.summary.track" msgid_plural "page.home.summary.tracks" msgstr[0] "" msgstr[1] "" -#: fietsboek/templates/home.jinja2:97 +#: fietsboek/templates/home.jinja2:99 msgid "page.home.total" msgstr "" @@ -642,91 +750,91 @@ msgstr "" msgid "page.password_reset.reset" msgstr "" -#: fietsboek/templates/profile.jinja2:64 +#: fietsboek/templates/profile.jinja2:10 msgid "page.profile.tabbar.overview" msgstr "" -#: fietsboek/templates/profile.jinja2:69 +#: fietsboek/templates/profile.jinja2:15 msgid "page.profile.tabbar.graphs" msgstr "" -#: fietsboek/templates/profile.jinja2:74 +#: fietsboek/templates/profile.jinja2:20 msgid "page.profile.tabbar.calendar" msgstr "" -#: fietsboek/templates/profile.jinja2:88 +#: fietsboek/templates/profile_calendar.jinja2:9 +msgid "page.profile.calendar.previous" +msgstr "" + +#: fietsboek/templates/profile_calendar.jinja2:11 +msgid "page.profile.calendar.next" +msgstr "" + +#: fietsboek/templates/profile_graphs.jinja2:6 +msgid "page.profile.graph.km_per_month" +msgstr "" + +#: fietsboek/templates/profile_overview.jinja2:71 msgid "page.profile.length" msgstr "" -#: fietsboek/templates/profile.jinja2:92 +#: fietsboek/templates/profile_overview.jinja2:75 msgid "page.profile.avg_length" msgstr "" -#: fietsboek/templates/profile.jinja2:96 +#: fietsboek/templates/profile_overview.jinja2:79 msgid "page.profile.uphill" msgstr "" -#: fietsboek/templates/profile.jinja2:100 +#: fietsboek/templates/profile_overview.jinja2:83 msgid "page.profile.downhill" msgstr "" -#: fietsboek/templates/profile.jinja2:104 +#: fietsboek/templates/profile_overview.jinja2:87 msgid "page.profile.moving_time" msgstr "" -#: fietsboek/templates/profile.jinja2:108 +#: fietsboek/templates/profile_overview.jinja2:91 msgid "page.profile.stopped_time" msgstr "" -#: fietsboek/templates/profile.jinja2:112 +#: fietsboek/templates/profile_overview.jinja2:95 msgid "page.profile.avg_duration" msgstr "" -#: fietsboek/templates/profile.jinja2:116 +#: fietsboek/templates/profile_overview.jinja2:99 msgid "page.profile.max_speed" msgstr "" -#: fietsboek/templates/profile.jinja2:120 +#: fietsboek/templates/profile_overview.jinja2:103 msgid "page.profile.avg_speed" msgstr "" -#: fietsboek/templates/profile.jinja2:124 +#: fietsboek/templates/profile_overview.jinja2:107 msgid "page.profile.number_of_tracks" msgstr "" -#: fietsboek/templates/profile.jinja2:130 +#: fietsboek/templates/profile_overview.jinja2:113 msgid "page.profile.longest_distance_track" msgstr "" -#: fietsboek/templates/profile.jinja2:135 +#: fietsboek/templates/profile_overview.jinja2:118 msgid "page.profile.shortest_distance_track" msgstr "" -#: fietsboek/templates/profile.jinja2:140 +#: fietsboek/templates/profile_overview.jinja2:123 msgid "page.profile.longest_duration_track" msgstr "" -#: fietsboek/templates/profile.jinja2:145 +#: fietsboek/templates/profile_overview.jinja2:128 msgid "page.profile.shortest_duration_track" msgstr "" -#: fietsboek/templates/profile.jinja2:152 -msgid "page.profile.graph.km_per_month" -msgstr "" - -#: fietsboek/templates/profile.jinja2:161 -msgid "page.profile.calendar.previous" -msgstr "" - -#: fietsboek/templates/profile.jinja2:163 -msgid "page.profile.calendar.next" -msgstr "" - -#: fietsboek/templates/profile.jinja2:218 +#: fietsboek/templates/profile_overview.jinja2:149 msgid "page.profile.heatmap" msgstr "" -#: fietsboek/templates/profile.jinja2:223 +#: fietsboek/templates/profile_overview.jinja2:157 msgid "page.profile.tilehunt" msgstr "" @@ -866,15 +974,15 @@ msgstr "" msgid "flash.a_confirmation_link_has_been_sent" msgstr "" -#: fietsboek/views/admin.py:49 +#: fietsboek/views/admin.py:156 msgid "flash.badge_added" msgstr "" -#: fietsboek/views/admin.py:73 +#: fietsboek/views/admin.py:180 msgid "flash.badge_modified" msgstr "" -#: fietsboek/views/admin.py:93 +#: fietsboek/views/admin.py:200 msgid "flash.badge_deleted" msgstr "" @@ -930,23 +1038,23 @@ msgstr "" msgid "flash.password_updated" msgstr "" -#: fietsboek/views/detail.py:162 +#: fietsboek/views/detail.py:166 msgid "flash.track_deleted" msgstr "" -#: fietsboek/views/upload.py:53 -msgid "flash.no_file_selected" +#: fietsboek/views/edit.py:100 fietsboek/views/upload.py:66 +msgid "flash.invalid_file" msgstr "" -#: fietsboek/views/upload.py:66 -msgid "flash.invalid_file" +#: fietsboek/views/upload.py:54 +msgid "flash.no_file_selected" msgstr "" -#: fietsboek/views/upload.py:192 +#: fietsboek/views/upload.py:194 msgid "flash.upload_success" msgstr "" -#: fietsboek/views/upload.py:211 +#: fietsboek/views/upload.py:213 msgid "flash.upload_cancelled" msgstr "" diff --git a/fietsboek/models/track.py b/fietsboek/models/track.py index 0737982..0921437 100644 --- a/fietsboek/models/track.py +++ b/fietsboek/models/track.py @@ -15,11 +15,13 @@ meta information. import datetime import enum import gzip +import json import logging from itertools import chain from typing import TYPE_CHECKING, Optional, Union import gpxpy +import sqlalchemy.types from babel.numbers import format_decimal from markupsafe import Markup from pyramid.authorization import ( @@ -34,7 +36,6 @@ from pyramid.httpexceptions import HTTPNotFound from pyramid.i18n import Localizer from pyramid.i18n import TranslationString as _ from sqlalchemy import ( - JSON, Column, DateTime, Enum, @@ -58,6 +59,24 @@ if TYPE_CHECKING: LOGGER = logging.getLogger(__name__) +class JsonText(sqlalchemy.types.TypeDecorator): + """Saves objects serialized as JSON but keeps the column as a Text.""" + + # This is straight from the SQLAlchemy documentation, so the non-overriden + # methods should be fine. + # pylint: disable=too-many-ancestors,abstract-method + + impl = sqlalchemy.types.Text + + cache_ok = True + + def process_bind_param(self, value, dialect): + return json.dumps(value) + + def process_result_value(self, value, dialect): + return json.loads(value) + + class Tag(Base): """A tag is a single keyword associated with a track. @@ -213,7 +232,7 @@ class Track(Base): visibility = Column(Enum(Visibility)) link_secret = Column(Text) type = Column(Enum(TrackType)) - transformers = Column(JSON) + transformers = Column(JsonText) owner: Mapped["models.User"] = relationship("User", back_populates="tracks") cache: Mapped[Optional["TrackCache"]] = relationship( diff --git a/fietsboek/routes.py b/fietsboek/routes.py index d5caef8..4286e92 100644 --- a/fietsboek/routes.py +++ b/fietsboek/routes.py @@ -46,13 +46,19 @@ def includeme(config): config.add_route( "image", "/track/{track_id}/images/{image_name}", factory="fietsboek.models.Track.factory" ) + config.add_route( + "track-map", + "/track/{track_id}/preview", + factory="fietsboek.models.Track.factory", + ) config.add_route("badge", "/badge/{badge_id}", factory="fietsboek.models.Badge.factory") - config.add_route("admin", "/admin") - config.add_route("admin-badge-add", "/admin/add-badge") - config.add_route("admin-badge-edit", "/admin/edit-badge") - config.add_route("admin-badge-delete", "/admin/delete-badge") + config.add_route("admin", "/admin/") + config.add_route("admin-badge", "/admin/badges/") + config.add_route("admin-badge-add", "/admin/badges/add") + config.add_route("admin-badge-edit", "/admin/badges/edit") + config.add_route("admin-badge-delete", "/admin/badges/delete") config.add_route("user-data", "/me") config.add_route("add-friend", "/me/send-friend-request") diff --git a/fietsboek/scripts/fietscron.py b/fietsboek/scripts/fietscron.py index 7687e12..a3f3f54 100644 --- a/fietsboek/scripts/fietscron.py +++ b/fietsboek/scripts/fietscron.py @@ -6,6 +6,7 @@ import logging.config from pathlib import Path import click +import gpxpy import pyramid.paster import redis as mod_redis from redis import Redis @@ -14,10 +15,11 @@ from sqlalchemy.engine import Engine from sqlalchemy.orm import Session from .. import config as mod_config -from .. import hittekaart, models +from .. import hittekaart, models, trackmap from ..config import Config from ..data import DataManager from ..models.user import TOKEN_LIFETIME +from ..views.tileproxy import TileRequester from . import config_option LOGGER = logging.getLogger(__name__) @@ -33,6 +35,7 @@ def cli(config): \b * Deletes pending uploads that are older than 24 hours. * Rebuilds the cache for missing tracks. + * Builds preview images for tracks. * (optional) Runs ``hittekaart`` to generate heatmaps """ logging.config.fileConfig(config) @@ -47,16 +50,20 @@ def cli(config): return engine = create_engine(config.sqlalchemy_url) + redis = mod_redis.from_url(config.redis_url) LOGGER.debug("Starting maintenance tasks") remove_old_uploads(engine) remove_old_tokens(engine) rebuild_cache(engine, data_manager) + build_previews(engine, data_manager, redis, config) + redis = mod_redis.from_url(config.redis_url) if config.hittekaart_autogenerate: - redis = mod_redis.from_url(config.redis_url) run_hittekaart(engine, data_manager, redis, config) + redis.set("last-cronjob", datetime.datetime.now(datetime.UTC).timestamp()) + def remove_old_uploads(engine: Engine): """Removes old uploads from the database.""" @@ -95,6 +102,34 @@ def rebuild_cache(engine: Engine, data_manager: DataManager): session.commit() +def build_previews( + engine: Engine, + data_manager: DataManager, + redis: Redis, + config: mod_config.Config, +): + """Builds track preview images if they are missing.""" + LOGGER.info("Building track preview images") + session = Session(engine) + tile_requester = TileRequester(redis) + layer = config.public_tile_layers()[0] + tracks = select(models.Track) + for track in session.execute(tracks).scalars(): + if track.id is None: + continue + + track_dir = data_manager.open(track.id) + if track_dir.preview_path().exists(): + continue + + LOGGER.debug("Building preview for %s", track.id) + gpx = gpxpy.parse(track_dir.decompress_gpx()) + preview = trackmap.render(gpx, layer, tile_requester) + with track_dir.lock(): + with open(track_dir.preview_path(), "wb") as preview_file: + preview.save(preview_file, "PNG") + + def run_hittekaart(engine: Engine, data_manager: DataManager, redis: Redis, config: Config): """Run outstanding hittekaart requests.""" # The logic here is as follows: diff --git a/fietsboek/static/DeadEnd.svg b/fietsboek/static/DeadEnd.svg new file mode 100644 index 0000000..b65f171 --- /dev/null +++ b/fietsboek/static/DeadEnd.svg @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 421.0267 630.72443" + height="630.72443" + width="421.0267" + xml:space="preserve" + id="svg5733" + version="1.1"><metadata + id="metadata5739"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs + id="defs5737" /><g + transform="matrix(1.3333333,0,0,-1.3333333,0,630.72445)" + id="g5741"><g + id="g5743"><path + id="path5745" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + d="M 21.594,0.375 H 294.59 c 20.906,0 20.805,17.051 20.805,17.051 v 438.41 c 0,0 0.136,16.828 -17.977,16.828 H 18.945 c 0,0 -18.531,0.309 -18.57,-18.507 V 19.11 C 0.375,0.328 21.594,0.375 21.594,0.375 Z" /><path + id="path5747" + style="fill:#154889;fill-opacity:1;fill-rule:evenodd;stroke:none" + d="m 18.422,8.723 h 279.019 c 0,0 10.153,0.027 10.153,10.27 V 454.86 c 0,0 -0.008,10.058 -11.235,10.058 H 19.504 c 0,0 -11.152,0 -11.152,-10.054 V 18.352 C 8.391,8.621 18.422,8.723 18.422,8.723 Z" /><path + id="path5749" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" + d="m 18.945,473.043 c -0.007,0 -4.711,0.078 -9.433,-2.238 C 4.781,468.485 0.02,463.688 0,454.161 V 19.11 C 0,9.582 5.441,4.731 10.832,2.356 16.223,-0.019 21.594,0 21.594,0 H 294.59 c 10.555,0 15.918,4.348 18.562,8.707 2.645,4.356 2.618,8.715 2.618,8.715 v 438.414 c 0,0.004 0.035,4.293 -2.243,8.594 -2.277,4.297 -6.937,8.613 -16.109,8.613 z m 0,-0.379 h 278.473 c 18.113,0 17.977,-16.828 17.977,-16.828 V 17.422 c 0,0 0.101,-17.047 -20.805,-17.047 H 21.594 c 0,0 -21.219,-0.047 -21.219,18.731 v 435.051 c 0.039,18.816 18.57,18.507 18.57,18.507 z" /><path + id="path5751" + style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" + d="M 53.969,418.61 V 265.614 h 96.164 v -34.368 h -66 V 181.43 H 137.16 V 6.571 h 41.59 l -0.066,174.859 h 53.027 l -0.066,49.816 h -66 v 34.368 h 96.156 V 418.61 Z" /><path + id="path5753" + style="fill:#cc0000;fill-opacity:1;fill-rule:evenodd;stroke:none" + d="M 88.68,226.7 H 227.098 V 186.442 H 88.68 Z" /><path + id="path5755" + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" + d="m 165.359,390.379 c 0,-5.152 -4.175,-9.332 -9.332,-9.332 -5.152,0 -9.328,4.18 -9.328,9.332 0,5.153 4.176,9.332 9.328,9.332 5.157,0 9.332,-4.179 9.332,-9.332 z" /><path + id="path5757" + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" + d="m 128.367,336.723 c 25.278,39.441 22.879,40.543 32.328,40.824 5.5,-0.164 5.664,-1.664 14.996,-8.996 11.5,-8.449 10.664,-8.551 10.5,-17.832 -0.05,-11.164 0.715,-10.328 -8.168,-14.496 v 18.832 l -7.832,6.996 v -26.496 l -18.66,0.168 -0.168,20.996 c -15.332,-23.66 -14.332,-22.328 -22.996,-19.996 z" /><path + id="path5759" + style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" + d="m 151.863,332.891 h 18.164 c 0,-4.168 1.332,-3.832 10.996,-21.996 10.165,-18.715 10.332,-17.445 4.5,-28.16 l -25.16,45.156 c -23.832,-46.105 -20.832,-44.707 -33.996,-43.824 l 13.164,24.66 c 11.282,20.383 11.883,20.449 12.332,24.164 z" /></g></g></svg>
\ No newline at end of file diff --git a/fietsboek/static/NoEntry.svg b/fietsboek/static/NoEntry.svg new file mode 100644 index 0000000..1cc18a4 --- /dev/null +++ b/fietsboek/static/NoEntry.svg @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="600.99628" height="600.99628"> + <defs> + <path id="a" d="M23.809 456.512h.051v-.047h-.051v.047z"/> + <path id="b" d="M23.809 456.465v.047l.05-.047h-.05zm.05 0-.05.047v-.047h.05z"/> + <path id="c" d="M23.859 456.465h-.05.023v.047l.027-.047z"/> + <path id="d" d="M23.809 456.465v.047-.047l.05.047-.05-.047z"/> + <path id="e" d="M23.859 456.512v-.047h-.05l.05.047z"/> + </defs> + <g transform="matrix(1.25 0 0 -1.25 0 600.99628)"> + <path fill="#fff" d="M480.398 240.399c0-132.551-107.449-240-240-240-132.55 0-240 107.449-240 240 0 132.55 107.45 240 240 240 132.551 0 240-107.45 240-240z"/> + <path fill="#c1121c" d="M240.402 472.402c-127.75 0-232-104.25-232-232s104.25-232 232-232c127.746 0 232 104.25 232 232s-104.254 232-232 232zm-208-196h416v-72h-416v72z"/> + <path d="M240.398 480.797C107.633 480.797 0 373.164 0 240.399 0 107.633 107.633 0 240.398 0c132.766 0 240.399 107.633 240.399 240.399 0 132.765-107.633 240.398-240.399 240.398zm0-.398c132.551 0 240-107.45 240-240 0-132.551-107.449-240-240-240-132.55 0-240 107.449-240 240 0 132.55 107.45 240 240 240zM23.832 456.512v-.024h-.023l.023.024z"/> + <path d="M23.809 456.512h.023v-.023h-.023v.023z"/> + <path d="M23.809 456.488v.024l.023-.024h-.023zm.023 0-.023.024v-.024h.023z"/> + <path d="M23.859 456.488h-.05.023v.024l.027-.024z"/> + <path d="M23.809 456.488v.024-.024l.023.024-.023-.024z"/> + <path d="M23.809 456.512h.023v-.023h-.023v.023zm.05 0v-.047h-.05l.05.047z"/> + <use xlink:href="#a"/> + <use xlink:href="#b"/> + <use xlink:href="#c"/> + <use xlink:href="#d"/> + <use xlink:href="#a"/> + <use xlink:href="#e"/> + <use xlink:href="#a"/> + <use xlink:href="#b"/> + <use xlink:href="#c"/> + <use xlink:href="#d"/> + <use xlink:href="#a"/> + <use xlink:href="#e"/> + <use xlink:href="#a"/> + <use xlink:href="#b"/> + <use xlink:href="#c"/> + <use xlink:href="#d"/> + <use xlink:href="#a"/> + </g> +</svg>
\ No newline at end of file diff --git a/fietsboek/static/theme.css b/fietsboek/static/theme.css index 2298b49..65e6881 100644 --- a/fietsboek/static/theme.css +++ b/fietsboek/static/theme.css @@ -268,10 +268,59 @@ strong { width: 25%; } +.browse-track-card { + display: grid; + grid-template: "preview data"/300px auto; +} +@media (max-width: 768px) { + .browse-track-card { + grid-template: "preview" "data"; + } +} +.browse-track-card.card-body { + padding: 0px; +} +.browse-track-card .browse-track-preview { + grid-area: preview; +} +@media (max-width: 768px) { + .browse-track-card .browse-track-preview { + text-align: center; + } +} +.browse-track-card .browse-track-preview img { + width: 300px; + height: 300px; +} +.browse-track-card .browse-track-data { + grid-area: data; + padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); +} + .chart-title { text-align: center; } +/* Admin view layout: We have an extra sidebar for the navigation */ +#adminContainer { + display: grid; + grid-template-areas: "sidebar main"; + grid-template-columns: 1fr 5fr; + gap: 1rem; +} + +#adminNavigation { + grid-area: sidebar; +} + +#adminContent { + grid-area: main; +} + +.admin-stat { + font-size: 120%; +} + .list-group.list-group-root { padding: 0; overflow: hidden; diff --git a/fietsboek/static/theme.css.map b/fietsboek/static/theme.css.map index 08ac64f..3ddb9bc 100644 --- a/fietsboek/static/theme.css.map +++ b/fietsboek/static/theme.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../../asset-sources/theme.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EAqCE;EACA;EACA;EACA;EACA;EACA;;AAzCA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAWJ;EACI;;;AAGJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;;AAEF;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;EACA;;AACA;EACE;;AAIJ;EACE;EACA;;;AAIJ;EACE;;;AAGF;AACA;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"theme.css"}
\ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../../asset-sources/theme.scss"],"names":[],"mappings":"AAAA;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;AAEA;EACE;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EAqCE;EACA;EACA;EACA;EACA;EACA;;AAzCA;EACE;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAWJ;EACI;;;AAGJ;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;EACA;;AAGF;EACE;;;AAIJ;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;;AAIJ;EACE;;AAEF;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;;AACA;EACE;;AAGJ;EACE;EACA;;AACA;EACE;;AAIJ;EACE;EACA;;;AAIJ;EACE;;;AAGF;AACA;EACE;;;AAGF;EACE;EACA;;AAEA;EAJF;IAKI,eACE;;;AAIJ;EACE;;AAGF;EACE;;AAEA;EAHF;IAII;;;AAGF;EACE;EACA;;AAIJ;EACE;EACA;;;AAIJ;EACE;;;AAGF;AACA;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE","file":"theme.css"}
\ No newline at end of file diff --git a/fietsboek/templates/403.jinja2 b/fietsboek/templates/403.jinja2 new file mode 100644 index 0000000..9f4478e --- /dev/null +++ b/fietsboek/templates/403.jinja2 @@ -0,0 +1,16 @@ +{% extends "layout.jinja2" %} + +{% block content %} +<div class="container"> + <h1>{{ _("403.title") }}</h1> + <div style="text-align: center;"> + <img src="{{ request.static_url('fietsboek:static/NoEntry.svg') }}" style="width: min(100%, 300px); margin: auto;"> + <p> + {{ _("403.no_access") }} + </p> + <p> + {{ _("403.try_log_in") }} + </p> + </div> +</div> +{% endblock content %} diff --git a/fietsboek/templates/404.jinja2 b/fietsboek/templates/404.jinja2 index aaf1241..9c7cc72 100644 --- a/fietsboek/templates/404.jinja2 +++ b/fietsboek/templates/404.jinja2 @@ -1,8 +1,16 @@ {% extends "layout.jinja2" %} {% block content %} -<div class="content"> - <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1> - <p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p> +<div class="container"> + <h1>{{ _("404.title") }}</h1> + <div style="text-align: center;"> + <img src="{{ request.static_url('fietsboek:static/DeadEnd.svg') }}" style="width: min(100%, 300px); margin: auto;"> + <p> + {{ _("404.path_not_found") }} + </p> + <p> + {{ _("404.choose_different") }} + </p> + </div> </div> {% endblock content %} diff --git a/fietsboek/templates/admin.jinja2 b/fietsboek/templates/admin.jinja2 index 3201d8d..e05e9f0 100644 --- a/fietsboek/templates/admin.jinja2 +++ b/fietsboek/templates/admin.jinja2 @@ -4,45 +4,18 @@ <div class="container"> <h1>{{ _("page.admin.title") }}</h1> - <h2>{{ _("page.admin.badges") }}</h2> + <div id="adminContainer"> + <aside id="adminNavigation"> + <nav class="nav nav-pills nav-fill flex-column"> + <a class="nav-link{% if admin_index == 0 %} active text-bg-dark{% endif %}" href="{{ request.route_url('admin') }}">{{ _("page.admin.nav.overview") }}</a> + <a class="nav-link{% if admin_index == 1 %} active text-bg-dark{% endif %}" href="{{ request.route_url('admin-badge') }}">{{ _("page.admin.nav.badges") }}</a> + </nav> + </aside> - <div class="list-group"> - {% for badge in badges %} - <span href="#" class="list-group-item list-group-item-action d-flex admin-badge-list"> - {{ util.render_badge(badge) }} - <form method="POST" enctype="multipart/form-data" action="{{ request.route_path('admin-badge-edit') }}"> - <input type="hidden" name="badge-edit-id" value="{{ badge.id }}"> - <div class="mb-3"> - <input type="text" class="form-control" name="badge-title" value="{{ badge.title }}"> - </div> - <div class="mb-3"> - <input class="form-control" type="file" name="badge-image"> - </div> - {{ util.hidden_csrf_input() }} - <div class="mb-3"> - <button class="btn btn-primary">{{ _("page.admin.badge.edit") }}</button> - </div> - </form> - <form method="POST" action="{{ request.route_path('admin-badge-delete') }}"> - <input type="hidden" name="badge-delete-id" value="{{ badge.id }}"> - {{ util.hidden_csrf_input() }} - <button class="btn btn-danger"><i class="bi bi-trash"></i> {{ _("page.admin.badge.delete_badge") }}</button> - </form> - </span> - {% endfor %} - </div> - - <form method="POST" enctype="multipart/form-data" action="{{ request.route_path('admin-badge-add') }}"> - <div class="mb-3"> - <label for="badge-title" class="form-label">{{ _("page.admin.badges.badge_title") }}</label> - <input type="text" class="form-control" id="badge-title" name="badge-title"> + <div id="adminContent"> + {% block admin_content %} + {% endblock %} </div> - <div class="mb-3"> - <label for="badge-image" class="form-label">{{ _("page.admin.badges.badge_image") }}</label> - <input class="form-control" type="file" name="badge-image"> - </div> - {{ util.hidden_csrf_input() }} - <button type="submit" class="btn btn-primary">{{ _("page.admin.badges.add_badge") }}</button> - </form> + </div> </div> {% endblock %} diff --git a/fietsboek/templates/admin_badges.jinja2 b/fietsboek/templates/admin_badges.jinja2 new file mode 100644 index 0000000..efe8d2c --- /dev/null +++ b/fietsboek/templates/admin_badges.jinja2 @@ -0,0 +1,45 @@ +{% set admin_index = 1 %} +{% extends "admin.jinja2" %} +{% import "util.jinja2" as util with context %} +{% block admin_content %} +<h2>{{ _("page.admin.badges") }}</h2> + +<div class="list-group"> + {% for badge in badges %} + <span href="#" class="list-group-item list-group-item-action d-flex admin-badge-list"> + {{ util.render_badge(badge) }} + <form method="POST" enctype="multipart/form-data" action="{{ request.route_path('admin-badge-edit') }}"> + <input type="hidden" name="badge-edit-id" value="{{ badge.id }}"> + <div class="mb-3"> + <input type="text" class="form-control" name="badge-title" value="{{ badge.title }}"> + </div> + <div class="mb-3"> + <input class="form-control" type="file" name="badge-image"> + </div> + {{ util.hidden_csrf_input() }} + <div class="mb-3"> + <button class="btn btn-success"><i class="bi bi-pencil"></i> {{ _("page.admin.badge.edit") }}</button> + <button class="btn btn-danger" form="deleteBadge{{ badge.id }}"><i class="bi bi-trash"></i> {{ _("page.admin.badge.delete_badge") }}</button> + </div> + </form> + <form method="POST" id="deleteBadge{{ badge.id }}" action="{{ request.route_path('admin-badge-delete') }}"> + <input type="hidden" name="badge-delete-id" value="{{ badge.id }}"> + {{ util.hidden_csrf_input() }} + </form> + </span> + {% endfor %} +</div> + +<form method="POST" enctype="multipart/form-data" action="{{ request.route_path('admin-badge-add') }}"> + <div class="mb-3"> + <label for="badge-title" class="form-label">{{ _("page.admin.badges.badge_title") }}</label> + <input type="text" class="form-control" id="badge-title" name="badge-title"> + </div> + <div class="mb-3"> + <label for="badge-image" class="form-label">{{ _("page.admin.badges.badge_image") }}</label> + <input class="form-control" type="file" name="badge-image"> + </div> + {{ util.hidden_csrf_input() }} + <button type="submit" class="btn btn-primary">{{ _("page.admin.badges.add_badge") }}</button> +</form> +{% endblock %} diff --git a/fietsboek/templates/admin_overview.jinja2 b/fietsboek/templates/admin_overview.jinja2 new file mode 100644 index 0000000..18a7633 --- /dev/null +++ b/fietsboek/templates/admin_overview.jinja2 @@ -0,0 +1,93 @@ +{% set admin_index = 0 %} +{% extends "admin.jinja2" %} +{% block admin_content %} +<p class="admin-stat"> + {{ _("admin.overview.instance_has") }}… +</p> + +<p class="admin-stat"> + … {{ ngettext("admin.overview.stat.user", "admin.overview.stat.users", user_count) }} +</p> + +<p class="admin-stat"> + … {{ ngettext("admin.overview.stat.track", "admin.overview.stat.tracks", track_count) }} +</p> + +<p class="admin-stat"> + … {{ (total_size / 1024 / 1024) | round(2) }} {{ _("admin.overview.stats.mib") }} +</p> + +<div style="position: relative; height: 500px; margin: auto; width: 75%;"> + <canvas id="graph-size-breakdown"></canvas> +</div> + +<h2>{{ _("admin.overview.system_overview") }}</h2> + +<table class="table"> + <tr> + <td>{{ _("admin.overview.fietsboek_version") }}</td> + <td>{{ versions["fietsboek"] }}</td> + </tr> + <tr> + <td>{{ _("admin.overview.python_version") }}</td> + <td>{{ versions["python"] }}</td> + </tr> + <tr> + <td>{{ _("admin.overview.kernel_version") }}</td> + <td>{{ versions["linux"] }}</td> + </tr> + <tr> + <td>{{ _("admin.overview.distro_version") }}</td> + <td>{{ versions["distro"] }}</td> + </tr> + <tr class="{% if cron_good %}table-success{% else %}table-warning{% endif %}"> + <td>{{ _("admin.overview.last_cronjob") }} {% if not cron_good %}<i class="bi bi-exclamation-triangle-fill"></i>{% endif %}</td> + <td>{{ last_cronjob }}</td> + </tr> +</table> +{% endblock %} + +{% block latescripts %} +<script> + (function() { + const data = { + labels: [ + {{ _("admin.overview.storage_graph.label.gpx") | tojson }}, + {{ _("admin.overview.storage_graph.label.images") | tojson }}, + {{ _("admin.overview.storage_graph.label.previews") | tojson }}, + {{ _("admin.overview.storage_graph.label.user_maps") | tojson }} + ], + datasets: [ + { + label: "MiB", + data: [ + {{ (size_breakdown.gpx_files / 1024 / 1024) | tojson }}, + {{ (size_breakdown.image_files / 1024 / 1024) | tojson }}, + {{ (size_breakdown.preview_files / 1024 / 1024) | tojson }}, + {{ (size_breakdown.user_maps / 1024 / 1024) | tojson }} + ] + } + ] + }; + + const config = { + type: 'pie', + data: data, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: {{ _("admin.overview.storage_graph.title") | tojson }} + } + } + } + }; + + new Chart("graph-size-breakdown", config); + })(); +</script> +{% endblock %} diff --git a/fietsboek/templates/browse.jinja2 b/fietsboek/templates/browse.jinja2 index 8877229..28693f6 100644 --- a/fietsboek/templates/browse.jinja2 +++ b/fietsboek/templates/browse.jinja2 @@ -151,64 +151,93 @@ {% endif %} </span> </h5> - <div class="card-body"> - <table class="table table-hover table-sm browse-summary"> - <tbody> - <tr> - <th scope="row">{{ _("page.details.date") }}</th> - <td>{{ track.date | format_datetime }}</td> - <th scope="row">{{ _("page.details.length") }}</th> - <td>{{ (track.length / 1000) | round(2) | format_decimal }} km</td> - </tr> - {% if track.show_organic_data() %} - <tr> - <th scope="row">{{ _("page.details.start_time") }}</th> - <td>{{ track.start_time | format_datetime }}</td> - <th scope="row">{{ _("page.details.end_time") }}</th> - <td>{{ track.end_time | format_datetime }}</td> - </tr> - {% endif %} - <tr> - <th scope="row">{{ _("page.details.uphill") }}</th> - <td>{{ track.uphill | round(2) | format_decimal }} m</td> - <th scope="row">{{ _("page.details.downhill") }}</th> - <td>{{ track.downhill | round(2) | format_decimal }} m</td> - </tr> - {% if track.show_organic_data() %} - <tr> - <th scope="row">{{ _("page.details.moving_time") }}</th> - <td>{{ track.moving_time }}</td> - <th scope="row">{{ _("page.details.stopped_time") }}</th> - <td>{{ track.stopped_time }}</td> - </tr> - <tr> - <th scope="row">{{ _("page.details.max_speed") }}</th> - <td>{{ mps_to_kph(track.max_speed) | round(2) | format_decimal }} km/h</td> - <th scope="row">{{ _("page.details.avg_speed") }}</th> - <td>{{ mps_to_kph(track.avg_speed) | round(2) | format_decimal }} km/h</td> - </tr> - {% endif %} - <tr> - <th scope="row"><i class="bi bi-chat-left-text-fill"></i> {{ _("page.browse.card.comments") }}</th> - <td>{{ track.comments | length }}</td> - <th scope="row"><i class="bi bi-images"></i> {{ _("page.browse.card.images") }}</th> - <td>{{ track.images | length }}</td> - </tr> - </tbody> - </table> + <div class="card-body browse-track-card"> + <div class="browse-track-preview"> + <img src="{{ request.route_url('track-map', track_id=track.id) }}"> + </div> + <div class="browse-track-data"> + <table class="table table-hover table-sm browse-summary"> + <tbody> + <tr> + <th scope="row">{{ _("page.details.date") }}</th> + <td>{{ track.date | format_datetime }}</td> + <th scope="row">{{ _("page.details.length") }}</th> + <td>{{ (track.length / 1000) | round(2) | format_decimal }} km</td> + </tr> + {% if track.show_organic_data() %} + <tr> + <th scope="row">{{ _("page.details.start_time") }}</th> + <td>{{ track.start_time | format_datetime }}</td> + <th scope="row">{{ _("page.details.end_time") }}</th> + <td>{{ track.end_time | format_datetime }}</td> + </tr> + {% endif %} + <tr> + <th scope="row">{{ _("page.details.uphill") }}</th> + <td>{{ track.uphill | round(2) | format_decimal }} m</td> + <th scope="row">{{ _("page.details.downhill") }}</th> + <td>{{ track.downhill | round(2) | format_decimal }} m</td> + </tr> + {% if track.show_organic_data() %} + <tr> + <th scope="row">{{ _("page.details.moving_time") }}</th> + <td>{{ track.moving_time }}</td> + <th scope="row">{{ _("page.details.stopped_time") }}</th> + <td>{{ track.stopped_time }}</td> + </tr> + <tr> + <th scope="row">{{ _("page.details.max_speed") }}</th> + <td>{{ mps_to_kph(track.max_speed) | round(2) | format_decimal }} km/h</td> + <th scope="row">{{ _("page.details.avg_speed") }}</th> + <td>{{ mps_to_kph(track.avg_speed) | round(2) | format_decimal }} km/h</td> + </tr> + {% endif %} + <tr> + <th scope="row"><i class="bi bi-chat-left-text-fill"></i> {{ _("page.browse.card.comments") }}</th> + <td>{{ track.comments | length }}</td> + <th scope="row"><i class="bi bi-images"></i> {{ _("page.browse.card.images") }}</th> + <td>{{ track.images | length }}</td> + </tr> + </tbody> + </table> - {% if track.show_organic_data() %} - <ul> - <li>{{ track.owner.name }}</li> - {% for user in track.tagged_people %} - <li>{{ user.name }}</li> - {% endfor %} - </ul> - {% endif %} + {% if track.show_organic_data() %} + <ul> + <li>{{ track.owner.name }}</li> + {% for user in track.tagged_people %} + <li>{{ user.name }}</li> + {% endfor %} + </ul> + {% endif %} + </div> </div> </div> {% endfor %} <button type="button" class="btn btn-primary ui-element" id="archiveDownloadButton" disabled><i class="bi bi-file-earmark-zip"></i> {{ _("page.browse.download_multiple") }}</button> + + <nav aria-label="Page navigation"> + <ul class="pagination justify-content-center"> + {% if page_previous is none %} + <li class="page-item disabled"> + <span class="page-link">{{ _("pagination.previous") }}</span> + </li> + {% else %} + <li class="page-item"> + <a class="page-link" href="{{ page_previous | safe }}">{{ _("pagination.previous") }}</a> + </li> + {% endif %} + {% if page_next is none %} + <li class="page-item disabled"> + <span class="page-link">{{ _("pagination.next") }}</span> + </li> + {% else %} + <li class="page-item"> + <a class="page-link" href="{{ page_next | safe }}">{{ _("pagination.next") }}</a> + </li> + {% endif %} + </ul> + </nav> + {% elif used_filters %} <p>{{ _("page.browse.no_results") }}</p> {% else %} diff --git a/fietsboek/templates/edit.jinja2 b/fietsboek/templates/edit.jinja2 index 6347ae6..26c520f 100644 --- a/fietsboek/templates/edit.jinja2 +++ b/fietsboek/templates/edit.jinja2 @@ -10,6 +10,10 @@ <noscript><p>{{ _("page.noscript") }}<p></noscript> </div> <form method="POST" enctype="multipart/form-data"> + <div class="mb-3"> + <label class="form-label ui-element" for="newGpx">{{ _("page.edit.form.new_track") }}</label> + <input id="newGpx" name="gpx" type="file" class="form-control ui-element"> + </div> {{ edit_form.edit_track(track.title, track.date_raw, track.date_tz or 0, track.visibility, track.type, track.description, track.text_tags(), badges, track.tagged_people, images) }} {{ util.hidden_csrf_input() }} <div class="btn-group" role="group"> diff --git a/fietsboek/templates/profile_overview.jinja2 b/fietsboek/templates/profile_overview.jinja2 index aa2333c..eaa10c6 100644 --- a/fietsboek/templates/profile_overview.jinja2 +++ b/fietsboek/templates/profile_overview.jinja2 @@ -9,48 +9,53 @@ {% for tag in track.tags %}<span class="badge bg-info text-dark">{{ tag.tag }}</span> {% endfor %} {% endif %} </h5> - <div class="card-body"> - <table class="table table-hover table-sm browse-summary"> - <tbody> - <tr> - <th scope="row">{{ _("page.details.date") }}</th> - <td>{{ track.date | format_datetime }}</td> - <th scope="row">{{ _("page.details.length") }}</th> - <td>{{ (track.length / 1000) | round(2) | format_decimal }} km</td> - </tr> - <tr> - <th scope="row">{{ _("page.details.start_time") }}</th> - <td>{{ track.start_time | format_datetime }}</td> - <th scope="row">{{ _("page.details.end_time") }}</th> - <td>{{ track.end_time | format_datetime }}</td> - </tr> - <tr> - <th scope="row">{{ _("page.details.uphill") }}</th> - <td>{{ track.uphill | round(2) | format_decimal }} m</td> - <th scope="row">{{ _("page.details.downhill") }}</th> - <td>{{ track.downhill | round(2) | format_decimal }} m</td> - </tr> - <tr> - <th scope="row">{{ _("page.details.moving_time") }}</th> - <td>{{ track.moving_time }}</td> - <th scope="row">{{ _("page.details.stopped_time") }}</th> - <td>{{ track.stopped_time }}</td> - </tr> - <tr> - <th scope="row">{{ _("page.details.max_speed") }}</th> - <td>{{ mps_to_kph(track.max_speed) | round(2) | format_decimal }} km/h</td> - <th scope="row">{{ _("page.details.avg_speed") }}</th> - <td>{{ mps_to_kph(track.avg_speed) | round(2) | format_decimal }} km/h</td> - </tr> - </tbody> - </table> - - <ul> - <li>{{ track.owner.name }}</li> - {% for user in track.tagged_people %} - <li>{{ user.name }}</li> - {% endfor %} - </ul> + <div class="card-body browse-track-card"> + <div class="browse-track-preview"> + <img src="{{ request.route_url('track-map', track_id=track.id) }}"> + </div> + <div class="browse-track-data"> + <table class="table table-hover table-sm browse-summary"> + <tbody> + <tr> + <th scope="row">{{ _("page.details.date") }}</th> + <td>{{ track.date | format_datetime }}</td> + <th scope="row">{{ _("page.details.length") }}</th> + <td>{{ (track.length / 1000) | round(2) | format_decimal }} km</td> + </tr> + <tr> + <th scope="row">{{ _("page.details.start_time") }}</th> + <td>{{ track.start_time | format_datetime }}</td> + <th scope="row">{{ _("page.details.end_time") }}</th> + <td>{{ track.end_time | format_datetime }}</td> + </tr> + <tr> + <th scope="row">{{ _("page.details.uphill") }}</th> + <td>{{ track.uphill | round(2) | format_decimal }} m</td> + <th scope="row">{{ _("page.details.downhill") }}</th> + <td>{{ track.downhill | round(2) | format_decimal }} m</td> + </tr> + <tr> + <th scope="row">{{ _("page.details.moving_time") }}</th> + <td>{{ track.moving_time }}</td> + <th scope="row">{{ _("page.details.stopped_time") }}</th> + <td>{{ track.stopped_time }}</td> + </tr> + <tr> + <th scope="row">{{ _("page.details.max_speed") }}</th> + <td>{{ mps_to_kph(track.max_speed) | round(2) | format_decimal }} km/h</td> + <th scope="row">{{ _("page.details.avg_speed") }}</th> + <td>{{ mps_to_kph(track.avg_speed) | round(2) | format_decimal }} km/h</td> + </tr> + </tbody> + </table> + + <ul> + <li>{{ track.owner.name }}</li> + {% for user in track.tagged_people %} + <li>{{ user.name }}</li> + {% endfor %} + </ul> + </div> </div> </div> {% endmacro %} diff --git a/fietsboek/trackmap.py b/fietsboek/trackmap.py new file mode 100644 index 0000000..584bf72 --- /dev/null +++ b/fietsboek/trackmap.py @@ -0,0 +1,134 @@ +"""Module to render tracks to static images on OSM tiles.""" + +import io +import math + +from gpxpy.gpx import GPX +from PIL import Image, ImageDraw + +from .config import TileLayerConfig +from .views.tileproxy import TileRequester + +TILE_SIZE = 256 + + +def to_web_mercator(lat: float, lon: float, zoom: int) -> tuple[int, int]: + """Convert a pari of latitude/longitude coordinates to web mercator form. + + :param lat: Latitude (in degrees). + :param lon: Longitude (in degrees). + :param zoom: Zoom level. + :return: The web mercator x/y coordinates. Both will be between 0 and + 2**zoom * 256. + """ + width = height = TILE_SIZE + + la = math.radians(lon) + phi = math.radians(lat) + + x = float(2**zoom) / (2 * math.pi) * width * (la + math.pi) + y = ( + float(2**zoom) + / (2 * math.pi) + * height + * (math.pi - math.log(math.tan((math.pi / 4 + phi / 2)))) + ) + + return (int(math.floor(x)), int(math.floor(y))) + + +class TrackMapRenderer: + """A renderer that renders GPX tracks onto small map excerpts.""" + + # pylint: disable=too-few-public-methods + + def __init__( + self, + track: GPX, + requester: TileRequester, + size: tuple[int, int], + layer: TileLayerConfig, + ): + self.track = track + self.requester = requester + self.size = size + self.layer = layer + self.maxzoom = layer.zoom + self.color = (0, 0, 255) + self.line_width = 5 + + def render(self) -> Image.Image: + """Render the track. + + :return: The image containing the rendered preview. + """ + zoom, bbox = self._find_zoom() + image = Image.new("RGB", self.size) + start_x, start_y = self._draw_base(image, zoom, bbox) + self._draw_lines(image, zoom, start_x, start_y) + return image + + def _find_zoom(self) -> tuple[int, tuple[int, int, int, int]]: + for zoom in range(self.maxzoom or 19, 0, -1): + min_x, max_x = 2**zoom * TILE_SIZE, 0 + min_y, max_y = 2**zoom * TILE_SIZE, 0 + + for point in self.track.walk(only_points=True): + x, y = to_web_mercator(point.latitude, point.longitude, zoom) + min_x = min(min_x, x) + max_x = max(max_x, x) + min_y = min(min_y, y) + max_y = max(max_y, y) + + if max_x - min_x > self.size[0] or max_y - min_y > self.size[1]: + break + else: + return zoom, (min_x, max_x, min_y, max_y) + + return 1, (0, 512, 0, 512) + + def _draw_base(self, image, zoom, bbox): + min_x, max_x, min_y, max_y = bbox + # We center the track by centering its bounding box + start_x = min_x - (self.size[0] - (max_x - min_x)) // 2 + start_y = min_y - (self.size[1] - (max_y - min_y)) // 2 + + offset_x = start_x // TILE_SIZE * TILE_SIZE - start_x + offset_y = start_y // TILE_SIZE * TILE_SIZE - start_y + + for i in range(int(math.ceil(self.size[0] / TILE_SIZE)) + 1): + for j in range(int(math.ceil(self.size[1] / TILE_SIZE)) + 1): + tile = self._load_tile(zoom, start_x // TILE_SIZE + i, start_y // TILE_SIZE + j) + image.paste(tile, (i * TILE_SIZE + offset_x, j * TILE_SIZE + offset_y)) + + return start_x, start_y + + def _load_tile(self, zoom, x, y) -> Image.Image: + tile_data = self.requester.load_tile(self.layer, zoom, x, y) + if not tile_data: + return Image.new("RGB", (TILE_SIZE, TILE_SIZE)) + return Image.open(io.BytesIO(tile_data)) + + def _draw_lines(self, image, zoom, start_x, start_y): + coords = ( + to_web_mercator(point.latitude, point.longitude, zoom) + for point in self.track.walk(only_points=True) + ) + coords = [(x - start_x, y - start_y) for x, y in coords] + + draw = ImageDraw.Draw(image) + draw.line(coords, fill=self.color, width=self.line_width, joint="curve") + + +def render(track: GPX, layer: TileLayerConfig, requester: TileRequester) -> Image.Image: + """Shorthand to construct a :class:`TrackMapRenderer` and render the preview. + + :param track: Parsed track to render. + :param layer: The tile layer to take the map tiles from. + :param requester: The requester which will be used to request the tiles. + :return: The image containing the rendered preview. + """ + return TrackMapRenderer(track, requester, (300, 300), layer).render() + + +__all__ = ["to_web_mercator", "TrackMapRenderer", "render"] diff --git a/fietsboek/updater/scripts/upd_20250618_v0.11.0.py b/fietsboek/updater/scripts/upd_20250618_v0.11.0.py new file mode 100644 index 0000000..11837f7 --- /dev/null +++ b/fietsboek/updater/scripts/upd_20250618_v0.11.0.py @@ -0,0 +1,27 @@ +"""Revision upgrade script v0.11.0 + +Date created: 2025-06-18 13:14:07.849714 +""" +from fietsboek.updater.script import UpdateScript + +update_id = 'v0.11.0' +previous = [ + 'v0.10.0', +] +alembic_revision = '2ebe1bf66430' + + +class Up(UpdateScript): + def pre_alembic(self, config): + pass + + def post_alembic(self, config): + pass + + +class Down(UpdateScript): + def pre_alembic(self, config): + pass + + def post_alembic(self, config): + pass diff --git a/fietsboek/util.py b/fietsboek/util.py index 9284ce2..5611c51 100644 --- a/fietsboek/util.py +++ b/fietsboek/util.py @@ -7,6 +7,7 @@ import os import re import secrets import unicodedata +from pathlib import Path from typing import Optional, TypeVar, Union import babel @@ -504,6 +505,18 @@ def secure_filename(filename: str) -> str: return filename +def recursive_size(path: Path) -> int: + """Recursively determines the size of the given directory. + + :param path: The directory. + :return: The combined size, in bytes. + """ + size = 0 + for root, _folders, files in os.walk(path): + size += sum(os.path.getsize(os.path.join(root, fname)) for fname in files) + return size + + __all__ = [ "ALLOWED_TAGS", "ALLOWED_ATTRIBUTES", @@ -529,4 +542,5 @@ __all__ = [ "tile_url", "encode_gpx", "secure_filename", + "recursive_size", ] diff --git a/fietsboek/views/admin.py b/fietsboek/views/admin.py index d078794..821a9fc 100644 --- a/fietsboek/views/admin.py +++ b/fietsboek/views/admin.py @@ -1,21 +1,129 @@ """Admin views.""" +import datetime +import platform +import stat +from dataclasses import dataclass +from pathlib import Path + from pyramid.httpexceptions import HTTPFound from pyramid.i18n import TranslationString as _ +from pyramid.request import Request from pyramid.view import view_config -from sqlalchemy import select +from sqlalchemy import func, select + +from .. import models, util + +GOOD_CRON_THRESHOLD = datetime.timedelta(hours=1) + + +def _safe_size(path: Path) -> int: + try: + res = path.stat() + if stat.S_ISDIR(res.st_mode): + return util.recursive_size(path) + if stat.S_ISREG(res.st_mode): + return res.st_size + return 0 + except FileNotFoundError: + return 0 + + +@dataclass +class SizeBreakdown: + """A breakdown of what objects take how much storage.""" + + gpx_files: int = 0 + image_files: int = 0 + preview_files: int = 0 + user_maps: int = 0 -from .. import models + +def _get_size_breakdown(data_manager): + breakdown = SizeBreakdown() + + for track_id in data_manager.list_tracks(): + track = data_manager.open(track_id) + breakdown.gpx_files += _safe_size(track.gpx_path()) + breakdown.gpx_files += _safe_size(track.backup_path()) + breakdown.preview_files += _safe_size(track.preview_path()) + for image_id in track.images(): + breakdown.image_files += _safe_size(track.image_path(image_id)) + + for user_id in data_manager.list_users(): + user = data_manager.open_user(user_id) + breakdown.user_maps += _safe_size(user.heatmap_path()) + breakdown.user_maps += _safe_size(user.tilehunt_path()) + + return breakdown + + +def _get_fietsboek_version(): + # pylint: disable=import-outside-toplevel + from fietsboek import __VERSION__ + + return __VERSION__ @view_config( route_name="admin", - renderer="fietsboek:templates/admin.jinja2", + renderer="fietsboek:templates/admin_overview.jinja2", + request_method="GET", + permission="admin", +) +def admin(request: Request): + """Renders the admin overview. + + :param request: The Pyramid request. + :return: The HTTP response. + """ + # False-positive with func.count() + # pylint: disable=not-callable + user_count = request.dbsession.execute(select(func.count()).select_from(models.User)).scalar() + track_count = request.dbsession.execute(select(func.count()).select_from(models.Track)).scalar() + size_total = request.data_manager.size() + size_breakdown = _get_size_breakdown(request.data_manager) + + try: + distro = platform.freedesktop_os_release()["PRETTY_NAME"] + except OSError: + distro = None + + try: + last_cronjob_timestamp = float(request.redis.get("last-cronjob")) + except (TypeError, ValueError): + last_cronjob = None + cron_good = False + else: + last_cronjob = datetime.datetime.fromtimestamp(last_cronjob_timestamp, datetime.UTC) + cron_good = (datetime.datetime.now(datetime.UTC) - last_cronjob) < GOOD_CRON_THRESHOLD + + versions = { + "fietsboek": _get_fietsboek_version(), + "python": platform.python_version(), + "linux": platform.platform(), + "distro": distro, + } + + return { + "user_count": user_count, + "track_count": track_count, + "total_size": size_total, + "size_breakdown": size_breakdown, + "versions": versions, + "last_cronjob": last_cronjob, + "cron_good": cron_good, + } + + +@view_config( + route_name="admin-badge", + renderer="fietsboek:templates/admin_badges.jinja2", request_method="GET", permission="admin", ) -def admin(request): - """Renders the main admin overview. +def admin_badges(request): + """Renders the badges editor. :param request: The Pyramid request. :type request: pyramid.request.Request @@ -47,7 +155,7 @@ def do_badge_add(request): request.dbsession.add(badge) request.session.flash(request.localizer.translate(_("flash.badge_added"))) - return HTTPFound(request.route_url("admin")) + return HTTPFound(request.route_url("admin-badge")) @view_config(route_name="admin-badge-edit", permission="admin", request_method="POST") @@ -71,7 +179,7 @@ def do_badge_edit(request): badge.title = request.params["badge-title"] request.session.flash(request.localizer.translate(_("flash.badge_modified"))) - return HTTPFound(request.route_url("admin")) + return HTTPFound(request.route_url("admin-badge")) @view_config(route_name="admin-badge-delete", permission="admin", request_method="POST") @@ -91,7 +199,7 @@ def do_badge_delete(request): request.dbsession.delete(badge) request.session.flash(request.localizer.translate(_("flash.badge_deleted"))) - return HTTPFound(request.route_url("admin")) + return HTTPFound(request.route_url("admin-badge")) -__all__ = ["admin", "do_badge_add", "do_badge_edit", "do_badge_delete"] +__all__ = ["admin", "admin_badges", "do_badge_add", "do_badge_edit", "do_badge_delete"] diff --git a/fietsboek/views/browse.py b/fietsboek/views/browse.py index 97bee35..e8e3edf 100644 --- a/fietsboek/views/browse.py +++ b/fietsboek/views/browse.py @@ -1,10 +1,11 @@ """Views for browsing all tracks.""" import datetime +import urllib.parse from collections.abc import Callable, Iterable from enum import Enum from io import RawIOBase -from typing import TypeVar +from typing import Iterator, TypeVar from zipfile import ZIP_DEFLATED, ZipFile from pyramid.httpexceptions import HTTPBadRequest, HTTPForbidden, HTTPNotFound @@ -12,12 +13,14 @@ from pyramid.request import Request from pyramid.response import Response from pyramid.view import view_config from sqlalchemy import func, not_, or_, select -from sqlalchemy.orm import aliased +from sqlalchemy.orm import Session, aliased from sqlalchemy.sql import Select from .. import models, util +from ..data import DataManager from ..models.track import TrackType, TrackWithMetadata +TRACKS_PER_PAGE = 20 T = TypeVar("T", bound=Enum) AliasedTrack = type[models.Track] @@ -69,6 +72,14 @@ def _get_enum(enum: type[T], value: str) -> T: raise HTTPBadRequest(f"Invalid enum value {value!r}") from exc +def _with_page(url: str, num: int) -> str: + parsed = urllib.parse.urlparse(url) + query = urllib.parse.parse_qs(parsed.query) + query["page"] = [str(num)] + new_qs = urllib.parse.urlencode(query, doseq=True) + return urllib.parse.urlunparse(parsed._replace(query=new_qs)) + + class ResultOrder(Enum): """Enum representing the different ways in which the tracks can be sorted in the result.""" @@ -415,6 +426,49 @@ def apply_order(query: Select, track: AliasedTrack, order: ResultOrder) -> Selec return query +def paginate( + dbsession: Session, + data_manager: DataManager, + query: Select, + filters: Filter, + start: int, + num: int, +) -> Iterator[TrackWithMetadata]: + """Paginates a query. + + Unlike a simple OFFSET/LIMIT solution, this generator will request more + elements if the filters end up throwing tracks out. + + :param dbsession: The current database session. + :param data_manager: The current data manager. + :param query: The (filtered and ordered) query. + :param filters: The filters to apply after retrieving elements from the + database. + :param track: The aliased ``Track`` class. + :param start: The offset from which to start the pagination. + :param num: How many items to retrieve at maximum. + :return: An iterable over ``num`` tracks (or fewer). + """ + # pylint: disable=too-many-arguments,too-many-positional-arguments + num_retrieved = 0 + offset = start + while num_retrieved < num: + # Best to try and get all at once + num_query = num - num_retrieved + this_query = query.offset(offset).limit(num_query) + offset += num_query + + tracks = list(dbsession.execute(this_query).scalars()) + if not tracks: + break + + for track in tracks: + track = TrackWithMetadata(track, data_manager) + if filters.apply(track): + num_retrieved += 1 + yield track + + @view_config( route_name="browse", renderer="fietsboek:templates/browse.jinja2", request_method="GET" ) @@ -431,18 +485,34 @@ def browse(request: Request) -> Response: query = select(track).join(models.TrackCache, isouter=True) query = filters.compile(query, track) + if "page" in request.params: + page = _get_int(request, "page") + else: + page = 1 + order = ResultOrder.DATE_DESC if request.params.get("sort"): order = _get_enum(ResultOrder, request.params.get("sort")) query = apply_order(query, track, order) - tracks = request.dbsession.execute(query).scalars() - tracks = (TrackWithMetadata(track, request.data_manager) for track in tracks) - tracks = [track for track in tracks if filters.apply(track)] + tracks = list( + paginate( + request.dbsession, + request.data_manager, + query, + filters, + (page - 1) * TRACKS_PER_PAGE, + # We request one more so we can tell easily if there is a next page + TRACKS_PER_PAGE + 1, + ) + ) + return { - "tracks": tracks, + "tracks": tracks[:TRACKS_PER_PAGE], "mps_to_kph": util.mps_to_kph, "used_filters": bool(filters), + "page_previous": None if page == 1 else _with_page(request.url, page - 1), + "page_next": None if len(tracks) <= TRACKS_PER_PAGE else _with_page(request.url, page + 1), } diff --git a/fietsboek/views/detail.py b/fietsboek/views/detail.py index 2bc5d9a..acc7bfe 100644 --- a/fietsboek/views/detail.py +++ b/fietsboek/views/detail.py @@ -2,8 +2,10 @@ import datetime import gzip +import io import logging +import gpxpy from pyramid.httpexceptions import ( HTTPFound, HTTPInternalServerError, @@ -11,12 +13,14 @@ from pyramid.httpexceptions import ( HTTPNotFound, ) from pyramid.i18n import TranslationString as _ +from pyramid.request import Request from pyramid.response import FileResponse, Response from pyramid.view import view_config from sqlalchemy import select -from .. import models, util +from .. import models, trackmap, util from ..models.track import Track, TrackWithMetadata +from .tileproxy import ITileRequester LOGGER = logging.getLogger(__name__) @@ -222,4 +226,51 @@ def add_comment(request): return HTTPFound(request.route_url("details", track_id=track.id)) -__all__ = ["details", "gpx", "invalidate_share", "delete_track", "badge", "image", "add_comment"] +@view_config(route_name="track-map", http_cache=3600, permission="track.view") +def track_map(request: Request): + """Endpoint to provide the track's preview image. + + Will use the cached version if available. Otherwise, will create the + preview and cache it. + + :param request: The pyramid request. + :return: The HTTP response. + """ + track = request.context + manager = request.data_manager.open(track.id) + preview_path = manager.preview_path() + try: + response = Response(preview_path.read_bytes(), content_type="image/png") + response.md5_etag() + return response + except FileNotFoundError: + pass + + loader: ITileRequester = request.registry.getUtility(ITileRequester) + layer = request.config.public_tile_layers()[0] + + parsed_gpx = gpxpy.parse(manager.decompress_gpx()) + track_image = trackmap.render(parsed_gpx, layer, loader) + + imageio = io.BytesIO() + track_image.save(imageio, "png") + tile_data = imageio.getvalue() + + with manager.lock(): + manager.set_preview(tile_data) + + response = Response(tile_data, content_type="image/png") + response.md5_etag() + return response + + +__all__ = [ + "details", + "gpx", + "invalidate_share", + "delete_track", + "badge", + "image", + "add_comment", + "track_map", +] diff --git a/fietsboek/views/edit.py b/fietsboek/views/edit.py index c3a4fc5..3daaacd 100644 --- a/fietsboek/views/edit.py +++ b/fietsboek/views/edit.py @@ -4,11 +4,13 @@ import datetime import logging from collections import namedtuple +import gpxpy from pyramid.httpexceptions import HTTPBadRequest, HTTPFound +from pyramid.i18n import TranslationString as _ from pyramid.view import view_config from sqlalchemy import select -from .. import actions, models, util +from .. import actions, convert, models, util from ..data import TrackDataDir from ..models.track import TrackType, Visibility @@ -68,7 +70,7 @@ def do_edit(request): :return: The HTTP response. :rtype: pyramid.response.Response """ - # pylint: disable=duplicate-code + # pylint: disable=duplicate-code,broad-exception-caught track = request.context user_friends = request.identity.get_friends() @@ -84,6 +86,25 @@ def do_edit(request): tz_offset = datetime.timedelta(minutes=int(request.params["date-tz"])) date = datetime.datetime.fromisoformat(request.params["date"]) with data, data.lock(): + redo_cache = False + try: + gpx_bytes = request.POST["gpx"].file.read() + except AttributeError: + pass + else: + LOGGER.info("Setting new track for %s", track.id) + gpx_bytes = convert.smart_convert(gpx_bytes) + try: + gpxpy.parse(gpx_bytes) + except Exception as exc: + request.session.flash(request.localizer.translate(_("flash.invalid_file"))) + LOGGER.info("Could not parse updated gpx: %s", exc) + return HTTPFound(request.route_url("edit", track_id=track.id)) + data.compress_gpx(gpx_bytes) + data.backup() + track.transformers = [] + redo_cache = True + track.date = date.replace(tzinfo=datetime.timezone(tz_offset)) track.tagged_people = tagged_people @@ -105,6 +126,14 @@ def do_edit(request): gpx=gpx, ) + # actions.execute_transformers automatically rebuilds the cache, so we only need to do + # this if execute_transformers didn't do it + if redo_cache and gpx is None: + LOGGER.info("New file detected, rebuilding cache for %s", track.id) + track.cache = None + track.ensure_cache(gpx_bytes) + request.dbsession.add(track.cache) + return HTTPFound(request.route_url("details", track_id=track.id)) diff --git a/fietsboek/views/errors.py b/fietsboek/views/errors.py new file mode 100644 index 0000000..39af14e --- /dev/null +++ b/fietsboek/views/errors.py @@ -0,0 +1,32 @@ +"""Error views.""" + +from pyramid.view import forbidden_view_config, notfound_view_config + + +@notfound_view_config(renderer="fietsboek:templates/404.jinja2") +def notfound_view(request): + """Renders the 404 response. + + :param request: The Pyramid request. + :type request: pyramid.request.Request + :return: The HTTP response. + :rtype: pyramid.response.Response + """ + request.response.status = 404 + return {} + + +@forbidden_view_config(renderer="fietsboek:templates/403.jinja2") +def forbidden_view(request): + """Renders the 403 response. + + :param request: The Pyramid request. + :type request: pyramid.request.Request + :return: The HTTP response. + :rtype: pyramid.response.Response + """ + request.response.status = 403 + return {} + + +__all__ = ["notfound_view", "forbidden_view"] diff --git a/fietsboek/views/notfound.py b/fietsboek/views/notfound.py deleted file mode 100644 index 2ec6c6c..0000000 --- a/fietsboek/views/notfound.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Error views.""" - -from pyramid.view import notfound_view_config - - -@notfound_view_config(renderer="fietsboek:templates/404.jinja2") -def notfound_view(request): - """Renders the 404 response. - - :param request: The Pyramid request. - :type request: pyramid.request.Request - :return: The HTTP response. - :rtype: pyramid.response.Response - """ - request.response.status = 404 - return {} - - -__all__ = ["notfound_view"] diff --git a/fietsboek/views/tileproxy.py b/fietsboek/views/tileproxy.py index 0efe4de..f472d6d 100644 --- a/fietsboek/views/tileproxy.py +++ b/fietsboek/views/tileproxy.py @@ -242,18 +242,44 @@ Note that new requests reset the timeout. class ITileRequester(Interface): # pylint: disable=inherit-non-class """An interface to define the tile requester.""" - def load_tile(self, url: str, headers: Optional[dict[str, str]] = None) -> requests.Response: + # pylint: disable=too-many-arguments + + def load_url(self, url: str, headers: Optional[dict[str, str]] = None) -> requests.Response: """Loads a tile at the given URL. + This is a low-level version of :meth:`~ITileRequester.load_tile`. + :param url: The URL of the tile to load. :param headers: Additional headers to send. :return: The response. """ raise NotImplementedError() + def load_tile( + self, + layer: TileLayerConfig, + zoom: int, + x: int, + y: int, + *, + headers: Optional[dict[str, str]] = None, + use_cache: bool = True, + ) -> bytes: + """Loads a tile from the given layer. + + :param layer: The configured tile layer. + :param zoom: The zoom level. + :param x: The tile's x coordinate. + :param y: The tile's y coordinate. + :param headers: Additional headers. + :param use_cache: Whether to use the cache (if available). + :return: The bytes of the tile. + """ + raise NotImplementedError() + @implementer(ITileRequester) -class TileRequester: # pylint: disable=too-few-public-methods +class TileRequester: """Implementation of the tile requester using requests sessions. The benefit of this over doing ``requests.get`` is that we can re-use @@ -261,7 +287,9 @@ class TileRequester: # pylint: disable=too-few-public-methods servers by not hammering them with too many connections. """ - def __init__(self): + # pylint: disable=too-many-arguments + + def __init__(self, redis): self.session = requests.Session() adapter = requests.adapters.HTTPAdapter( pool_maxsize=MAX_CONCURRENT_CONNECTIONS, @@ -271,14 +299,56 @@ class TileRequester: # pylint: disable=too-few-public-methods self.session.mount("https://", adapter) self.lock = threading.Lock() self.closer = None - - def load_tile(self, url: str, headers: Optional[dict[str, str]] = None) -> requests.Response: - """Implementation of :meth:`ITileRequester.load_tile`.""" + self.redis = redis + + def load_url(self, url: str, headers: Optional[dict[str, str]] = None) -> requests.Response: + """Implementation of :meth:`ITileRequester.load_url`.""" + if headers is None: + headers = {} + if "user-agent" not in headers: + headers["user-agent"] = f"Fietsboek/{__VERSION__}" response = self.session.get(url, headers=headers, timeout=TIMEOUT.total_seconds()) response.raise_for_status() self._schedule_session_close() return response + def load_tile( + self, + layer: TileLayerConfig, + zoom: int, + x: int, + y: int, + *, + headers: Optional[dict[str, str]] = None, + use_cache: bool = True, + ) -> bytes: + """Implementation of :meth:`ITileRequester.load_tile`.""" + cache_key = f"tile:{layer.layer_id}-{x}-{y}-{zoom}" + + if use_cache and self.redis is not None: + cached = self.redis.get(cache_key) + if cached is not None: + LOGGER.debug("Cache hit for %s/z:%s/x:%s/y:%s", layer.layer_id, zoom, x, y) + return cached + + url = ( + layer.url.unicode_string() + .replace(quote("{x}"), str(x)) + .replace(quote("{y}"), str(y)) + .replace(quote("{z}"), str(zoom)) + ) + + # Avoid doing actual requests during tests + if url.startswith("http://localhost:0"): + LOGGER.debug("Skipping tile request for testing URL") + return b"" + + response = self.load_url(url, headers=headers) + tile_data = response.content + if use_cache and self.redis is not None: + self.redis.set(cache_key, tile_data, ex=TTL) + return tile_data + def _schedule_session_close(self): with self.lock: if self.closer: @@ -332,29 +402,17 @@ def tile_proxy(request): LOGGER.debug("Aborted attempt to contact %s due to previous timeouts", provider) raise HTTPGatewayTimeout(f"Avoiding request to {provider}") - url = ( - tile_sources[provider] - .url.unicode_string() - .replace(quote("{x}"), str(x)) - .replace(quote("{y}"), str(y)) - .replace(quote("{z}"), str(z)) - ) - # Avoid doing actual requests during tests - if url.startswith("http://localhost:0"): - LOGGER.debug("Skipping tile proxy request for testing URL") - return Response(b"", content_type="image/png") - headers = { - "user-agent": f"Fietsboek-Tile-Proxy/{__VERSION__}", - } + headers = {} from_mail = request.config.email_from if from_mail: headers["from"] = from_mail loader: ITileRequester = request.registry.getUtility(ITileRequester) try: - resp = loader.load_tile(url, headers=headers) + # We already tried the cache, so bypass it here + resp = loader.load_tile(tile_sources[provider], z, x, y, headers=headers, use_cache=False) except ReadTimeout: - LOGGER.debug("Proxy timeout when accessing %r", url) + LOGGER.debug("Proxy timeout when accessing z:%s/x:%s/y:%s from %s", z, x, y, provider) request.redis.incr(timeout_tracker) request.redis.expire(timeout_tracker, PUNISHMENT_TTL) raise HTTPGatewayTimeout(f"No response in time from {provider}") from None @@ -365,8 +423,8 @@ def tile_proxy(request): status_code = exc.response.status_code return Response(f"Failed to get tile from {provider}", status_code=status_code) else: - request.redis.set(cache_key, resp.content, ex=TTL) - return Response(resp.content, content_type=resp.headers.get("Content-type", content_type)) + request.redis.set(cache_key, resp, ex=TTL) + return Response(resp, content_type=content_type) def sources_for(request: Request) -> list[TileLayerConfig]: @@ -375,6 +433,9 @@ def sources_for(request: Request) -> list[TileLayerConfig]: :param request: The Pyramid request. :return: A list of tile sources. """ + # This code is similar to Config.public_tile_layers. Maybe it's worth + # refactoring it when we refactor this module? + # pylint: disable=duplicate-code return [ source for source in chain( diff --git a/fietsboek/views/upload.py b/fietsboek/views/upload.py index c40319c..3eb1099 100644 --- a/fietsboek/views/upload.py +++ b/fietsboek/views/upload.py @@ -12,6 +12,7 @@ from sqlalchemy import select from .. import actions, convert, models, transformers, util from ..models.track import TrackType, Visibility +from ..views.tileproxy import ITileRequester LOGGER = logging.getLogger(__name__) @@ -53,8 +54,7 @@ def do_upload(request): request.session.flash(request.localizer.translate(_("flash.no_file_selected"))) return HTTPFound(request.route_url("upload")) - if len(gpx) > 11 and gpx[9:12] == b"FIT": - gpx = convert.from_fit(gpx).to_xml().encode("utf-8") + gpx = convert.smart_convert(gpx) # Before we do anything, we check if we can parse the file. # gpxpy might throw different exceptions, so we simply catch `Exception` @@ -165,6 +165,8 @@ def do_finish_upload(request): track = actions.add_track( request.dbsession, request.data_manager, + request.registry.getUtility(ITileRequester), + request.config.public_tile_layers()[0], owner=request.identity, title=request.params["title"], visibility=Visibility[request.params["visibility"]], diff --git a/poetry.lock b/poetry.lock index c1347df..440a4b2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. [[package]] name = "alabaster" @@ -6,6 +6,7 @@ version = "1.0.0" description = "A light, configurable Sphinx theme" optional = false python-versions = ">=3.10" +groups = ["docs"] files = [ {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, @@ -13,22 +14,23 @@ files = [ [[package]] name = "alembic" -version = "1.14.1" +version = "1.16.4" description = "A database migration tool for SQLAlchemy." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5"}, - {file = "alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213"}, + {file = "alembic-1.16.4-py3-none-any.whl", hash = "sha256:b05e51e8e82efc1abd14ba2af6392897e145930c3e0a2faf2b0da2f7f7fd660d"}, + {file = "alembic-1.16.4.tar.gz", hash = "sha256:efab6ada0dd0fae2c92060800e0bf5c1dc26af15a10e02fb4babff164b4725e2"}, ] [package.dependencies] Mako = "*" -SQLAlchemy = ">=1.3.0" -typing-extensions = ">=4" +SQLAlchemy = ">=1.4.0" +typing-extensions = ">=4.12" [package.extras] -tz = ["backports.zoneinfo", "tzdata"] +tz = ["tzdata"] [[package]] name = "annotated-types" @@ -36,6 +38,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -43,24 +46,24 @@ files = [ [[package]] name = "astroid" -version = "3.3.8" +version = "3.3.11" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" +groups = ["linters"] files = [ - {file = "astroid-3.3.8-py3-none-any.whl", hash = "sha256:187ccc0c248bfbba564826c26f070494f7bc964fd286b6d9fff4420e55de828c"}, - {file = "astroid-3.3.8.tar.gz", hash = "sha256:a88c7994f914a4ea8572fac479459f4955eeccc877be3f2d959a33273b0cf40b"}, + {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"}, + {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - [[package]] name = "async-timeout" version = "5.0.1" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version == \"3.11\" and python_full_version < \"3.11.3\"" files = [ {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, @@ -68,31 +71,34 @@ files = [ [[package]] name = "babel" -version = "2.16.0" +version = "2.17.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" +groups = ["main", "docs"] files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, ] [package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] [[package]] name = "beautifulsoup4" -version = "4.12.3" +version = "4.13.4" description = "Screen-scraping library" optional = false -python-versions = ">=3.6.0" +python-versions = ">=3.7.0" +groups = ["testing"] files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, + {file = "beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b"}, + {file = "beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195"}, ] [package.dependencies] soupsieve = ">1.2" +typing-extensions = ">=4.0.0" [package.extras] cchardet = ["cchardet"] @@ -107,6 +113,7 @@ version = "25.1.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["linters"] files = [ {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, @@ -138,8 +145,6 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -153,6 +158,7 @@ version = "1.1.0" description = "Python bindings for the Brotli compression library" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, @@ -241,13 +247,14 @@ files = [ [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.7.14" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +groups = ["main", "docs", "testing"] files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2"}, + {file = "certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995"}, ] [[package]] @@ -256,6 +263,8 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main", "types"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -331,114 +340,116 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" -files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +groups = ["main", "docs", "testing"] +files = [ + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] name = "click" -version = "8.1.8" +version = "8.2.2" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" +groups = ["main", "linters"] files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "click-8.2.2-py3-none-any.whl", hash = "sha256:52e1e9f5d3db8c85aa76968c7c67ed41ddbacb167f43201511c8fd61eb5ba2ca"}, + {file = "click-8.2.2.tar.gz", hash = "sha256:068616e6ef9705a07b6db727cb9c248f4eb9dae437a30239f56fa94b18b852ef"}, ] [package.dependencies] @@ -446,22 +457,24 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "click-option-group" -version = "0.5.6" +version = "0.5.7" description = "Option groups missing in Click" optional = false -python-versions = ">=3.6,<4" +python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "click-option-group-0.5.6.tar.gz", hash = "sha256:97d06703873518cc5038509443742b25069a3c7562d1ea72ff08bfadde1ce777"}, - {file = "click_option_group-0.5.6-py3-none-any.whl", hash = "sha256:38a26d963ee3ad93332ddf782f9259c5bdfe405e73408d943ef5e7d0c3767ec7"}, + {file = "click_option_group-0.5.7-py3-none-any.whl", hash = "sha256:96b9f52f397ef4d916f81929bd6c1f85e89046c7a401a64e72a61ae74ad35c24"}, + {file = "click_option_group-0.5.7.tar.gz", hash = "sha256:8dc780be038712fc12c9fecb3db4fe49e0d0723f9c171d7cda85c20369be693c"}, ] [package.dependencies] -Click = ">=7.0,<9" +click = ">=7.0" [package.extras] -docs = ["Pallets-Sphinx-Themes", "m2r2", "sphinx"] -tests = ["pytest"] -tests-cov = ["coverage", "coveralls", "pytest", "pytest-cov"] +dev = ["pre-commit", "pytest"] +docs = ["m2r2", "pallets-sphinx-themes", "sphinx"] +test = ["pytest"] +test-cov = ["pytest", "pytest-cov"] [[package]] name = "colorama" @@ -469,146 +482,184 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "docs", "linters", "testing"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\"", docs = "sys_platform == \"win32\"", linters = "platform_system == \"Windows\" or sys_platform == \"win32\"", testing = "sys_platform == \"win32\""} [[package]] name = "coverage" -version = "7.6.10" +version = "7.10.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" -files = [ - {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, - {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, - {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, - {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, - {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, - {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, - {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, - {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, - {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, - {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, - {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, - {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, - {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, - {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, - {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, - {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, - {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, - {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, - {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, - {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, - {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, - {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, - {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, - {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, - {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, - {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, - {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, - {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, - {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, - {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, - {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, - {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, - {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, - {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, - {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, - {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, - {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, - {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, +groups = ["testing"] +files = [ + {file = "coverage-7.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1c86eb388bbd609d15560e7cc0eb936c102b6f43f31cf3e58b4fd9afe28e1372"}, + {file = "coverage-7.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b4ba0f488c1bdb6bd9ba81da50715a372119785458831c73428a8566253b86b"}, + {file = "coverage-7.10.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083442ecf97d434f0cb3b3e3676584443182653da08b42e965326ba12d6b5f2a"}, + {file = "coverage-7.10.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c1a40c486041006b135759f59189385da7c66d239bad897c994e18fd1d0c128f"}, + {file = "coverage-7.10.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3beb76e20b28046989300c4ea81bf690df84ee98ade4dc0bbbf774a28eb98440"}, + {file = "coverage-7.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc265a7945e8d08da28999ad02b544963f813a00f3ed0a7a0ce4165fd77629f8"}, + {file = "coverage-7.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:47c91f32ba4ac46f1e224a7ebf3f98b4b24335bad16137737fe71a5961a0665c"}, + {file = "coverage-7.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1a108dd78ed185020f66f131c60078f3fae3f61646c28c8bb4edd3fa121fc7fc"}, + {file = "coverage-7.10.1-cp310-cp310-win32.whl", hash = "sha256:7092cc82382e634075cc0255b0b69cb7cada7c1f249070ace6a95cb0f13548ef"}, + {file = "coverage-7.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:ac0c5bba938879c2fc0bc6c1b47311b5ad1212a9dcb8b40fe2c8110239b7faed"}, + {file = "coverage-7.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b45e2f9d5b0b5c1977cb4feb5f594be60eb121106f8900348e29331f553a726f"}, + {file = "coverage-7.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a7a4d74cb0f5e3334f9aa26af7016ddb94fb4bfa11b4a573d8e98ecba8c34f1"}, + {file = "coverage-7.10.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d4b0aab55ad60ead26159ff12b538c85fbab731a5e3411c642b46c3525863437"}, + {file = "coverage-7.10.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dcc93488c9ebd229be6ee1f0d9aad90da97b33ad7e2912f5495804d78a3cd6b7"}, + {file = "coverage-7.10.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa309df995d020f3438407081b51ff527171cca6772b33cf8f85344b8b4b8770"}, + {file = "coverage-7.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cfb8b9d8855c8608f9747602a48ab525b1d320ecf0113994f6df23160af68262"}, + {file = "coverage-7.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:320d86da829b012982b414c7cdda65f5d358d63f764e0e4e54b33097646f39a3"}, + {file = "coverage-7.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dc60ddd483c556590da1d9482a4518292eec36dd0e1e8496966759a1f282bcd0"}, + {file = "coverage-7.10.1-cp311-cp311-win32.whl", hash = "sha256:4fcfe294f95b44e4754da5b58be750396f2b1caca8f9a0e78588e3ef85f8b8be"}, + {file = "coverage-7.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:efa23166da3fe2915f8ab452dde40319ac84dc357f635737174a08dbd912980c"}, + {file = "coverage-7.10.1-cp311-cp311-win_arm64.whl", hash = "sha256:d12b15a8c3759e2bb580ffa423ae54be4f184cf23beffcbd641f4fe6e1584293"}, + {file = "coverage-7.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6b7dc7f0a75a7eaa4584e5843c873c561b12602439d2351ee28c7478186c4da4"}, + {file = "coverage-7.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:607f82389f0ecafc565813aa201a5cade04f897603750028dd660fb01797265e"}, + {file = "coverage-7.10.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f7da31a1ba31f1c1d4d5044b7c5813878adae1f3af8f4052d679cc493c7328f4"}, + {file = "coverage-7.10.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51fe93f3fe4f5d8483d51072fddc65e717a175490804e1942c975a68e04bf97a"}, + {file = "coverage-7.10.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e59d00830da411a1feef6ac828b90bbf74c9b6a8e87b8ca37964925bba76dbe"}, + {file = "coverage-7.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:924563481c27941229cb4e16eefacc35da28563e80791b3ddc5597b062a5c386"}, + {file = "coverage-7.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ca79146ee421b259f8131f153102220b84d1a5e6fb9c8aed13b3badfd1796de6"}, + {file = "coverage-7.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2b225a06d227f23f386fdc0eab471506d9e644be699424814acc7d114595495f"}, + {file = "coverage-7.10.1-cp312-cp312-win32.whl", hash = "sha256:5ba9a8770effec5baaaab1567be916c87d8eea0c9ad11253722d86874d885eca"}, + {file = "coverage-7.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:9eb245a8d8dd0ad73b4062135a251ec55086fbc2c42e0eb9725a9b553fba18a3"}, + {file = "coverage-7.10.1-cp312-cp312-win_arm64.whl", hash = "sha256:7718060dd4434cc719803a5e526838a5d66e4efa5dc46d2b25c21965a9c6fcc4"}, + {file = "coverage-7.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ebb08d0867c5a25dffa4823377292a0ffd7aaafb218b5d4e2e106378b1061e39"}, + {file = "coverage-7.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f32a95a83c2e17422f67af922a89422cd24c6fa94041f083dd0bb4f6057d0bc7"}, + {file = "coverage-7.10.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c4c746d11c8aba4b9f58ca8bfc6fbfd0da4efe7960ae5540d1a1b13655ee8892"}, + {file = "coverage-7.10.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7f39edd52c23e5c7ed94e0e4bf088928029edf86ef10b95413e5ea670c5e92d7"}, + {file = "coverage-7.10.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab6e19b684981d0cd968906e293d5628e89faacb27977c92f3600b201926b994"}, + {file = "coverage-7.10.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5121d8cf0eacb16133501455d216bb5f99899ae2f52d394fe45d59229e6611d0"}, + {file = "coverage-7.10.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df1c742ca6f46a6f6cbcaef9ac694dc2cb1260d30a6a2f5c68c5f5bcfee1cfd7"}, + {file = "coverage-7.10.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:40f9a38676f9c073bf4b9194707aa1eb97dca0e22cc3766d83879d72500132c7"}, + {file = "coverage-7.10.1-cp313-cp313-win32.whl", hash = "sha256:2348631f049e884839553b9974f0821d39241c6ffb01a418efce434f7eba0fe7"}, + {file = "coverage-7.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:4072b31361b0d6d23f750c524f694e1a417c1220a30d3ef02741eed28520c48e"}, + {file = "coverage-7.10.1-cp313-cp313-win_arm64.whl", hash = "sha256:3e31dfb8271937cab9425f19259b1b1d1f556790e98eb266009e7a61d337b6d4"}, + {file = "coverage-7.10.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1c4f679c6b573a5257af6012f167a45be4c749c9925fd44d5178fd641ad8bf72"}, + {file = "coverage-7.10.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:871ebe8143da284bd77b84a9136200bd638be253618765d21a1fce71006d94af"}, + {file = "coverage-7.10.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:998c4751dabf7d29b30594af416e4bf5091f11f92a8d88eb1512c7ba136d1ed7"}, + {file = "coverage-7.10.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:780f750a25e7749d0af6b3631759c2c14f45de209f3faaa2398312d1c7a22759"}, + {file = "coverage-7.10.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:590bdba9445df4763bdbebc928d8182f094c1f3947a8dc0fc82ef014dbdd8324"}, + {file = "coverage-7.10.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b2df80cb6a2af86d300e70acb82e9b79dab2c1e6971e44b78dbfc1a1e736b53"}, + {file = "coverage-7.10.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d6a558c2725bfb6337bf57c1cd366c13798bfd3bfc9e3dd1f4a6f6fc95a4605f"}, + {file = "coverage-7.10.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e6150d167f32f2a54690e572e0a4c90296fb000a18e9b26ab81a6489e24e78dd"}, + {file = "coverage-7.10.1-cp313-cp313t-win32.whl", hash = "sha256:d946a0c067aa88be4a593aad1236493313bafaa27e2a2080bfe88db827972f3c"}, + {file = "coverage-7.10.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e37c72eaccdd5ed1130c67a92ad38f5b2af66eeff7b0abe29534225db2ef7b18"}, + {file = "coverage-7.10.1-cp313-cp313t-win_arm64.whl", hash = "sha256:89ec0ffc215c590c732918c95cd02b55c7d0f569d76b90bb1a5e78aa340618e4"}, + {file = "coverage-7.10.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:166d89c57e877e93d8827dac32cedae6b0277ca684c6511497311249f35a280c"}, + {file = "coverage-7.10.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bed4a2341b33cd1a7d9ffc47df4a78ee61d3416d43b4adc9e18b7d266650b83e"}, + {file = "coverage-7.10.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddca1e4f5f4c67980533df01430184c19b5359900e080248bbf4ed6789584d8b"}, + {file = "coverage-7.10.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:37b69226001d8b7de7126cad7366b0778d36777e4d788c66991455ba817c5b41"}, + {file = "coverage-7.10.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2f22102197bcb1722691296f9e589f02b616f874e54a209284dd7b9294b0b7f"}, + {file = "coverage-7.10.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1e0c768b0f9ac5839dac5cf88992a4bb459e488ee8a1f8489af4cb33b1af00f1"}, + {file = "coverage-7.10.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:991196702d5e0b120a8fef2664e1b9c333a81d36d5f6bcf6b225c0cf8b0451a2"}, + {file = "coverage-7.10.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae8e59e5f4fd85d6ad34c2bb9d74037b5b11be072b8b7e9986beb11f957573d4"}, + {file = "coverage-7.10.1-cp314-cp314-win32.whl", hash = "sha256:042125c89cf74a074984002e165d61fe0e31c7bd40ebb4bbebf07939b5924613"}, + {file = "coverage-7.10.1-cp314-cp314-win_amd64.whl", hash = "sha256:a22c3bfe09f7a530e2c94c87ff7af867259c91bef87ed2089cd69b783af7b84e"}, + {file = "coverage-7.10.1-cp314-cp314-win_arm64.whl", hash = "sha256:ee6be07af68d9c4fca4027c70cea0c31a0f1bc9cb464ff3c84a1f916bf82e652"}, + {file = "coverage-7.10.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d24fb3c0c8ff0d517c5ca5de7cf3994a4cd559cde0315201511dbfa7ab528894"}, + {file = "coverage-7.10.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1217a54cfd79be20512a67ca81c7da3f2163f51bbfd188aab91054df012154f5"}, + {file = "coverage-7.10.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:51f30da7a52c009667e02f125737229d7d8044ad84b79db454308033a7808ab2"}, + {file = "coverage-7.10.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ed3718c757c82d920f1c94089066225ca2ad7f00bb904cb72b1c39ebdd906ccb"}, + {file = "coverage-7.10.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc452481e124a819ced0c25412ea2e144269ef2f2534b862d9f6a9dae4bda17b"}, + {file = "coverage-7.10.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9d6f494c307e5cb9b1e052ec1a471060f1dea092c8116e642e7a23e79d9388ea"}, + {file = "coverage-7.10.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fc0e46d86905ddd16b85991f1f4919028092b4e511689bbdaff0876bd8aab3dd"}, + {file = "coverage-7.10.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80b9ccd82e30038b61fc9a692a8dc4801504689651b281ed9109f10cc9fe8b4d"}, + {file = "coverage-7.10.1-cp314-cp314t-win32.whl", hash = "sha256:e58991a2b213417285ec866d3cd32db17a6a88061a985dbb7e8e8f13af429c47"}, + {file = "coverage-7.10.1-cp314-cp314t-win_amd64.whl", hash = "sha256:e88dd71e4ecbc49d9d57d064117462c43f40a21a1383507811cf834a4a620651"}, + {file = "coverage-7.10.1-cp314-cp314t-win_arm64.whl", hash = "sha256:1aadfb06a30c62c2eb82322171fe1f7c288c80ca4156d46af0ca039052814bab"}, + {file = "coverage-7.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:57b6e8789cbefdef0667e4a94f8ffa40f9402cee5fc3b8e4274c894737890145"}, + {file = "coverage-7.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:85b22a9cce00cb03156334da67eb86e29f22b5e93876d0dd6a98646bb8a74e53"}, + {file = "coverage-7.10.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:97b6983a2f9c76d345ca395e843a049390b39652984e4a3b45b2442fa733992d"}, + {file = "coverage-7.10.1-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ddf2a63b91399a1c2f88f40bc1705d5a7777e31c7e9eb27c602280f477b582ba"}, + {file = "coverage-7.10.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47ab6dbbc31a14c5486420c2c1077fcae692097f673cf5be9ddbec8cdaa4cdbc"}, + {file = "coverage-7.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21eb7d8b45d3700e7c2936a736f732794c47615a20f739f4133d5230a6512a88"}, + {file = "coverage-7.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:283005bb4d98ae33e45f2861cd2cde6a21878661c9ad49697f6951b358a0379b"}, + {file = "coverage-7.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:fefe31d61d02a8b2c419700b1fade9784a43d726de26495f243b663cd9fe1513"}, + {file = "coverage-7.10.1-cp39-cp39-win32.whl", hash = "sha256:e8ab8e4c7ec7f8a55ac05b5b715a051d74eac62511c6d96d5bb79aaafa3b04cf"}, + {file = "coverage-7.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:c36baa0ecde742784aa76c2b816466d3ea888d5297fda0edbac1bf48fa94688a"}, + {file = "coverage-7.10.1-py3-none-any.whl", hash = "sha256:fa2a258aa6bf188eb9a8948f7102a83da7c430a0dce918dbd8b60ef8fcb772d7"}, + {file = "coverage-7.10.1.tar.gz", hash = "sha256:ae2b4856f29ddfe827106794f3589949a57da6f0d38ab01e24ec35107979ba57"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "cryptography" -version = "44.0.0" +version = "45.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" -files = [ - {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, - {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, - {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, - {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, - {file = "cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd"}, - {file = "cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, - {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, - {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, - {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, - {file = "cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa"}, - {file = "cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c"}, - {file = "cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02"}, +groups = ["main", "types"] +files = [ + {file = "cryptography-45.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:101ee65078f6dd3e5a028d4f19c07ffa4dd22cce6a20eaa160f8b5219911e7d8"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3a264aae5f7fbb089dbc01e0242d3b67dffe3e6292e1f5182122bdf58e65215d"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e74d30ec9c7cb2f404af331d5b4099a9b322a8a6b25c4632755c8757345baac5"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3af26738f2db354aafe492fb3869e955b12b2ef2e16908c8b9cb928128d42c57"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e6c00130ed423201c5bc5544c23359141660b07999ad82e34e7bb8f882bb78e0"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:dd420e577921c8c2d31289536c386aaa30140b473835e97f83bc71ea9d2baf2d"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d05a38884db2ba215218745f0781775806bde4f32e07b135348355fe8e4991d9"}, + {file = "cryptography-45.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:ad0caded895a00261a5b4aa9af828baede54638754b51955a0ac75576b831b27"}, + {file = "cryptography-45.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9024beb59aca9d31d36fcdc1604dd9bbeed0a55bface9f1908df19178e2f116e"}, + {file = "cryptography-45.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:91098f02ca81579c85f66df8a588c78f331ca19089763d733e34ad359f474174"}, + {file = "cryptography-45.0.5-cp311-abi3-win32.whl", hash = "sha256:926c3ea71a6043921050eaa639137e13dbe7b4ab25800932a8498364fc1abec9"}, + {file = "cryptography-45.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:b85980d1e345fe769cfc57c57db2b59cff5464ee0c045d52c0df087e926fbe63"}, + {file = "cryptography-45.0.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3562c2f23c612f2e4a6964a61d942f891d29ee320edb62ff48ffb99f3de9ae8"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3fcfbefc4a7f332dece7272a88e410f611e79458fab97b5efe14e54fe476f4fd"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:460f8c39ba66af7db0545a8c6f2eabcbc5a5528fc1cf6c3fa9a1e44cec33385e"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9b4cf6318915dccfe218e69bbec417fdd7c7185aa7aab139a2c0beb7468c89f0"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2089cc8f70a6e454601525e5bf2779e665d7865af002a5dec8d14e561002e135"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0027d566d65a38497bc37e0dd7c2f8ceda73597d2ac9ba93810204f56f52ebc7"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:be97d3a19c16a9be00edf79dca949c8fa7eff621763666a145f9f9535a5d7f42"}, + {file = "cryptography-45.0.5-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:7760c1c2e1a7084153a0f68fab76e754083b126a47d0117c9ed15e69e2103492"}, + {file = "cryptography-45.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6ff8728d8d890b3dda5765276d1bc6fb099252915a2cd3aff960c4c195745dd0"}, + {file = "cryptography-45.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7259038202a47fdecee7e62e0fd0b0738b6daa335354396c6ddebdbe1206af2a"}, + {file = "cryptography-45.0.5-cp37-abi3-win32.whl", hash = "sha256:1e1da5accc0c750056c556a93c3e9cb828970206c68867712ca5805e46dc806f"}, + {file = "cryptography-45.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:90cb0a7bb35959f37e23303b7eed0a32280510030daba3f7fdfbb65defde6a97"}, + {file = "cryptography-45.0.5-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:206210d03c1193f4e1ff681d22885181d47efa1ab3018766a7b32a7b3d6e6afd"}, + {file = "cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c648025b6840fe62e57107e0a25f604db740e728bd67da4f6f060f03017d5097"}, + {file = "cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b8fa8b0a35a9982a3c60ec79905ba5bb090fc0b9addcfd3dc2dd04267e45f25e"}, + {file = "cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:14d96584701a887763384f3c47f0ca7c1cce322aa1c31172680eb596b890ec30"}, + {file = "cryptography-45.0.5-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:57c816dfbd1659a367831baca4b775b2a5b43c003daf52e9d57e1d30bc2e1b0e"}, + {file = "cryptography-45.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b9e38e0a83cd51e07f5a48ff9691cae95a79bea28fe4ded168a8e5c6c77e819d"}, + {file = "cryptography-45.0.5-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8c4a6ff8a30e9e3d38ac0539e9a9e02540ab3f827a3394f8852432f6b0ea152e"}, + {file = "cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bd4c45986472694e5121084c6ebbd112aa919a25e783b87eb95953c9573906d6"}, + {file = "cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:982518cd64c54fcada9d7e5cf28eabd3ee76bd03ab18e08a48cad7e8b6f31b18"}, + {file = "cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:12e55281d993a793b0e883066f590c1ae1e802e3acb67f8b442e721e475e6463"}, + {file = "cryptography-45.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:5aa1e32983d4443e310f726ee4b071ab7569f58eedfdd65e9675484a4eb67bd1"}, + {file = "cryptography-45.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e357286c1b76403dd384d938f93c46b2b058ed4dfcdce64a770f0537ed3feb6f"}, + {file = "cryptography-45.0.5.tar.gz", hash = "sha256:72e76caa004ab63accdf26023fccd1d087f6d90ec6048ff33ad0445abf7f605a"}, ] [package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} +cffi = {version = ">=1.14", markers = "platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs ; python_full_version >= \"3.8.0\"", "sphinx-rtd-theme (>=3.0.0) ; python_full_version >= \"3.8.0\""] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_full_version >= \"3.8.0\""] +pep8test = ["check-sdist ; python_full_version >= \"3.8.0\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==45.0.5)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] name = "dill" -version = "0.3.9" +version = "0.4.0" description = "serialize all of Python" optional = false python-versions = ">=3.8" +groups = ["linters"] files = [ - {file = "dill-0.3.9-py3-none-any.whl", hash = "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a"}, - {file = "dill-0.3.9.tar.gz", hash = "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c"}, + {file = "dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049"}, + {file = "dill-0.4.0.tar.gz", hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0"}, ] [package.extras] @@ -621,40 +672,28 @@ version = "0.21.2" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2"}, {file = "docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f"}, ] [[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] name = "filelock" -version = "3.17.0" +version = "3.18.0" description = "A platform independent file lock." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, - {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, + {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, + {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, ] [package.extras] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] +typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""] [[package]] name = "fitparse" @@ -662,6 +701,7 @@ version = "1.2.0" description = "Python library to parse ANT/Garmin .FIT files" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "fitparse-1.2.0.tar.gz", hash = "sha256:2d691022452dea6dabad13cc6e017ca467fe8a3a895cd3ac67a50a7bb716b4a9"}, ] @@ -672,6 +712,7 @@ version = "1.6.2" description = "GPX file parser and GPS track manipulation library" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "gpxpy-1.6.2-py3-none-any.whl", hash = "sha256:289bc2d80f116c988d0a1e763fda22838f83005573ece2bbc6521817b26fb40a"}, {file = "gpxpy-1.6.2.tar.gz", hash = "sha256:a72c484b97ec42b80834353b029cc8ee1b79f0ffca1179b2210bb3baf26c01ae"}, @@ -679,85 +720,68 @@ files = [ [[package]] name = "greenlet" -version = "3.1.1" +version = "3.2.3" description = "Lightweight in-process concurrent programming" optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, - {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, - {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, - {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, - {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, - {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, - {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, - {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, - {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, - {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, - {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, - {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, - {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, - {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, - {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, - {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, - {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, -] +python-versions = ">=3.9" +groups = ["main", "testing"] +files = [ + {file = "greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be"}, + {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac"}, + {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a433dbc54e4a37e4fff90ef34f25a8c00aed99b06856f0119dcf09fbafa16392"}, + {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:72e77ed69312bab0434d7292316d5afd6896192ac4327d44f3d613ecb85b037c"}, + {file = "greenlet-3.2.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:68671180e3849b963649254a882cd544a3c75bfcd2c527346ad8bb53494444db"}, + {file = "greenlet-3.2.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49c8cfb18fb419b3d08e011228ef8a25882397f3a859b9fe1436946140b6756b"}, + {file = "greenlet-3.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:efc6dc8a792243c31f2f5674b670b3a95d46fa1c6a912b8e310d6f542e7b0712"}, + {file = "greenlet-3.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:731e154aba8e757aedd0781d4b240f1225b075b4409f1bb83b05ff410582cf00"}, + {file = "greenlet-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:96c20252c2f792defe9a115d3287e14811036d51e78b3aaddbee23b69b216302"}, + {file = "greenlet-3.2.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822"}, + {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83"}, + {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf"}, + {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b"}, + {file = "greenlet-3.2.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147"}, + {file = "greenlet-3.2.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5"}, + {file = "greenlet-3.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc"}, + {file = "greenlet-3.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba"}, + {file = "greenlet-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34"}, + {file = "greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d"}, + {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b"}, + {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d"}, + {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264"}, + {file = "greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688"}, + {file = "greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb"}, + {file = "greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c"}, + {file = "greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163"}, + {file = "greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849"}, + {file = "greenlet-3.2.3-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:500b8689aa9dd1ab26872a34084503aeddefcb438e2e7317b89b11eaea1901ad"}, + {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a07d3472c2a93117af3b0136f246b2833fdc0b542d4a9799ae5f41c28323faef"}, + {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8704b3768d2f51150626962f4b9a9e4a17d2e37c8a8d9867bbd9fa4eb938d3b3"}, + {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5035d77a27b7c62db6cf41cf786cfe2242644a7a337a0e155c80960598baab95"}, + {file = "greenlet-3.2.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2d8aa5423cd4a396792f6d4580f88bdc6efcb9205891c9d40d20f6e670992efb"}, + {file = "greenlet-3.2.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c724620a101f8170065d7dded3f962a2aea7a7dae133a009cada42847e04a7b"}, + {file = "greenlet-3.2.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:873abe55f134c48e1f2a6f53f7d1419192a3d1a4e873bace00499a4e45ea6af0"}, + {file = "greenlet-3.2.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:024571bbce5f2c1cfff08bf3fbaa43bbc7444f580ae13b0099e95d0e6e67ed36"}, + {file = "greenlet-3.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5195fb1e75e592dd04ce79881c8a22becdfa3e6f500e7feb059b1e6fdd54d3e3"}, + {file = "greenlet-3.2.3-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:3d04332dddb10b4a211b68111dabaee2e1a073663d117dc10247b5b1642bac86"}, + {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8186162dffde068a465deab08fc72c767196895c39db26ab1c17c0b77a6d8b97"}, + {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f4bfbaa6096b1b7a200024784217defedf46a07c2eee1a498e94a1b5f8ec5728"}, + {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:ed6cfa9200484d234d8394c70f5492f144b20d4533f69262d530a1a082f6ee9a"}, + {file = "greenlet-3.2.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02b0df6f63cd15012bed5401b47829cfd2e97052dc89da3cfaf2c779124eb892"}, + {file = "greenlet-3.2.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86c2d68e87107c1792e2e8d5399acec2487a4e993ab76c792408e59394d52141"}, + {file = "greenlet-3.2.3-cp314-cp314-win_amd64.whl", hash = "sha256:8c47aae8fbbfcf82cc13327ae802ba13c9c36753b67e760023fd116bc124a62a"}, + {file = "greenlet-3.2.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:42efc522c0bd75ffa11a71e09cd8a399d83fafe36db250a87cf1dacfaa15dc64"}, + {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d760f9bdfe79bff803bad32b4d8ffb2c1d2ce906313fc10a83976ffb73d64ca7"}, + {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:8324319cbd7b35b97990090808fdc99c27fe5338f87db50514959f8059999805"}, + {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8c37ef5b3787567d322331d5250e44e42b58c8c713859b8a04c6065f27efbf72"}, + {file = "greenlet-3.2.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce539fb52fb774d0802175d37fcff5c723e2c7d249c65916257f0a940cee8904"}, + {file = "greenlet-3.2.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:003c930e0e074db83559edc8705f3a2d066d4aa8c2f198aff1e454946efd0f26"}, + {file = "greenlet-3.2.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7e70ea4384b81ef9e84192e8a77fb87573138aa5d4feee541d8014e452b434da"}, + {file = "greenlet-3.2.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:22eb5ba839c4b2156f18f76768233fe44b23a31decd9cc0d4cc8141c211fd1b4"}, + {file = "greenlet-3.2.3-cp39-cp39-win32.whl", hash = "sha256:4532f0d25df67f896d137431b13f4cdce89f7e3d4a96387a41290910df4d3a57"}, + {file = "greenlet-3.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:aaa7aae1e7f75eaa3ae400ad98f8644bb81e1dc6ba47ce8a93d3f17274e08322"}, + {file = "greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365"}, +] +markers = {main = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} [package.extras] docs = ["Sphinx", "furo"] @@ -769,6 +793,7 @@ version = "1.12.1" description = "Integrated process monitor for developing and reloading daemons." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "hupper-1.12.1-py3-none-any.whl", hash = "sha256:e872b959f09d90be5fb615bd2e62de89a0b57efc037bdf9637fb09cdf8552b19"}, {file = "hupper-1.12.1.tar.gz", hash = "sha256:06bf54170ff4ecf4c84ad5f188dee3901173ab449c2608ad05b9bfd6b13e32eb"}, @@ -784,6 +809,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main", "docs", "testing"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -798,6 +824,7 @@ version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["docs"] files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, @@ -805,24 +832,26 @@ files = [ [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +groups = ["testing"] files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] name = "isort" -version = "6.0.0" +version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.9.0" +groups = ["linters"] files = [ - {file = "isort-6.0.0-py3-none-any.whl", hash = "sha256:567954102bb47bb12e0fae62606570faacddd441e45683968c8d1734fb1af892"}, - {file = "isort-6.0.0.tar.gz", hash = "sha256:75d9d8a1438a9432a7d7b54f2d3b45cad9a4a0fdba43617d9873379704a8bdf1"}, + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, ] [package.extras] @@ -831,13 +860,14 @@ plugins = ["setuptools"] [[package]] name = "jinja2" -version = "3.1.5" +version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main", "docs"] files = [ - {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, - {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] @@ -848,24 +878,27 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "legacy-cgi" -version = "2.6.2" -description = "Fork of the standard library cgi and cgitb modules, being deprecated in PEP-594" +version = "2.6.3" +description = "Fork of the standard library cgi and cgitb modules removed in Python 3.13" optional = false -python-versions = ">=3.10" +python-versions = ">=3.8" +groups = ["main", "testing"] +markers = "python_version >= \"3.13\"" files = [ - {file = "legacy_cgi-2.6.2-py3-none-any.whl", hash = "sha256:a7b83afb1baf6ebeb56522537c5943ef9813cf933f6715e88a803f7edbce0bff"}, - {file = "legacy_cgi-2.6.2.tar.gz", hash = "sha256:9952471ceb304043b104c22d00b4f333cac27a6abe446d8a528fc437cf13c85f"}, + {file = "legacy_cgi-2.6.3-py3-none-any.whl", hash = "sha256:6df2ea5ae14c71ef6f097f8b6372b44f6685283dc018535a75c924564183cdab"}, + {file = "legacy_cgi-2.6.3.tar.gz", hash = "sha256:4c119d6cb8e9d8b6ad7cc0ddad880552c62df4029622835d06dfd18f438a8154"}, ] [[package]] name = "mako" -version = "1.3.8" +version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "Mako-1.3.8-py3-none-any.whl", hash = "sha256:42f48953c7eb91332040ff567eb7eea69b22e7a4affbc5ba8e845e8f730f6627"}, - {file = "mako-1.3.8.tar.gz", hash = "sha256:577b97e414580d3e088d47c2dbbe9594aa7a5146ed2875d4dfa9075af2dd3cc8"}, + {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, + {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, ] [package.dependencies] @@ -878,17 +911,18 @@ testing = ["pytest"] [[package]] name = "markdown" -version = "3.7" +version = "3.8.2" description = "Python implementation of John Gruber's Markdown." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, - {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, + {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, + {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, ] [package.extras] -docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] @@ -897,6 +931,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main", "docs"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -967,6 +1002,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["linters"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -974,54 +1010,55 @@ files = [ [[package]] name = "mypy" -version = "1.14.1" +version = "1.17.1" description = "Optional static typing for Python" optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, +python-versions = ">=3.9" +groups = ["main", "types"] +files = [ + {file = "mypy-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3fbe6d5555bf608c47203baa3e72dbc6ec9965b3d7c318aa9a4ca76f465bd972"}, + {file = "mypy-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80ef5c058b7bce08c83cac668158cb7edea692e458d21098c7d3bce35a5d43e7"}, + {file = "mypy-1.17.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a580f8a70c69e4a75587bd925d298434057fe2a428faaf927ffe6e4b9a98df"}, + {file = "mypy-1.17.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd86bb649299f09d987a2eebb4d52d10603224500792e1bee18303bbcc1ce390"}, + {file = "mypy-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a76906f26bd8d51ea9504966a9c25419f2e668f012e0bdf3da4ea1526c534d94"}, + {file = "mypy-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:e79311f2d904ccb59787477b7bd5d26f3347789c06fcd7656fa500875290264b"}, + {file = "mypy-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad37544be07c5d7fba814eb370e006df58fed8ad1ef33ed1649cb1889ba6ff58"}, + {file = "mypy-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:064e2ff508e5464b4bd807a7c1625bc5047c5022b85c70f030680e18f37273a5"}, + {file = "mypy-1.17.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70401bbabd2fa1aa7c43bb358f54037baf0586f41e83b0ae67dd0534fc64edfd"}, + {file = "mypy-1.17.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e92bdc656b7757c438660f775f872a669b8ff374edc4d18277d86b63edba6b8b"}, + {file = "mypy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c1fdf4abb29ed1cb091cf432979e162c208a5ac676ce35010373ff29247bcad5"}, + {file = "mypy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:ff2933428516ab63f961644bc49bc4cbe42bbffb2cd3b71cc7277c07d16b1a8b"}, + {file = "mypy-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:69e83ea6553a3ba79c08c6e15dbd9bfa912ec1e493bf75489ef93beb65209aeb"}, + {file = "mypy-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b16708a66d38abb1e6b5702f5c2c87e133289da36f6a1d15f6a5221085c6403"}, + {file = "mypy-1.17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:89e972c0035e9e05823907ad5398c5a73b9f47a002b22359b177d40bdaee7056"}, + {file = "mypy-1.17.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03b6d0ed2b188e35ee6d5c36b5580cffd6da23319991c49ab5556c023ccf1341"}, + {file = "mypy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c837b896b37cd103570d776bda106eabb8737aa6dd4f248451aecf53030cdbeb"}, + {file = "mypy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:665afab0963a4b39dff7c1fa563cc8b11ecff7910206db4b2e64dd1ba25aed19"}, + {file = "mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7"}, + {file = "mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81"}, + {file = "mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6"}, + {file = "mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849"}, + {file = "mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14"}, + {file = "mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a"}, + {file = "mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733"}, + {file = "mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd"}, + {file = "mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0"}, + {file = "mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a"}, + {file = "mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91"}, + {file = "mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed"}, + {file = "mypy-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d1092694f166a7e56c805caaf794e0585cabdbf1df36911c414e4e9abb62ae9"}, + {file = "mypy-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79d44f9bfb004941ebb0abe8eff6504223a9c1ac51ef967d1263c6572bbebc99"}, + {file = "mypy-1.17.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b01586eed696ec905e61bd2568f48740f7ac4a45b3a468e6423a03d3788a51a8"}, + {file = "mypy-1.17.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43808d9476c36b927fbcd0b0255ce75efe1b68a080154a38ae68a7e62de8f0f8"}, + {file = "mypy-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:feb8cc32d319edd5859da2cc084493b3e2ce5e49a946377663cc90f6c15fb259"}, + {file = "mypy-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7598cf74c3e16539d4e2f0b8d8c318e00041553d83d4861f87c7a72e95ac24d"}, + {file = "mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9"}, + {file = "mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01"}, ] [package.dependencies] mypy_extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +pathspec = ">=0.9.0" typing_extensions = ">=4.6.0" [package.extras] @@ -1033,57 +1070,62 @@ reports = ["lxml"] [[package]] name = "mypy-extensions" -version = "1.0.0" +version = "1.1.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" +groups = ["main", "linters", "types"] files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] [[package]] name = "nh3" -version = "0.2.20" +version = "0.3.0" description = "Python binding to Ammonia HTML sanitizer Rust crate" optional = false python-versions = ">=3.8" -files = [ - {file = "nh3-0.2.20-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e1061a4ab6681f6bdf72b110eea0c4e1379d57c9de937db3be4202f7ad6043db"}, - {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb4254b1dac4a1ee49919a5b3f1caf9803ea8dada1816d9e8289e63d3cd0dd9a"}, - {file = "nh3-0.2.20-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ae9cbd713524cdb81e64663d0d6aae26f678db9f2cd9db0bf162606f1f9f20c"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1f7370b4e14cc03f5ae141ef30a1caf81fa5787711f80be9081418dd9eb79d2"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:ac4d27dc836a476efffc6eb661994426b8b805c951b29c9cf2ff36bc9ad58bc5"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:4fd2e9248725ebcedac3997a8d3da0d90a12a28c9179c6ba51f1658938ac30d0"}, - {file = "nh3-0.2.20-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f7d564871833ddbe54df3aa59053b1110729d3a800cb7628ae8f42adb3d75208"}, - {file = "nh3-0.2.20-cp313-cp313t-win32.whl", hash = "sha256:d2a176fd4306b6f0f178a3f67fac91bd97a3a8d8fafb771c9b9ef675ba5c8886"}, - {file = "nh3-0.2.20-cp313-cp313t-win_amd64.whl", hash = "sha256:6ed834c68452a600f517dd3e1534dbfaff1f67f98899fecf139a055a25d99150"}, - {file = "nh3-0.2.20-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:76e2f603b30c02ff6456b233a83fc377dedab6a50947b04e960a6b905637b776"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:181063c581defe683bd4bb78188ac9936d208aebbc74c7f7c16b6a32ae2ebb38"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:231addb7643c952cd6d71f1c8702d703f8fe34afcb20becb3efb319a501a12d7"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1b9a8340a0aab991c68a5ca938d35ef4a8a3f4bf1b455da8855a40bee1fa0ace"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10317cd96fe4bbd4eb6b95f3920b71c902157ad44fed103fdcde43e3b8ee8be6"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8698db4c04b140800d1a1cd3067fda399e36e1e2b8fc1fe04292a907350a3e9b"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eb04b9c3deb13c3a375ea39fd4a3c00d1f92e8fb2349f25f1e3e4506751774b"}, - {file = "nh3-0.2.20-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92f3f1c4f47a2c6f3ca7317b1d5ced05bd29556a75d3a4e2715652ae9d15c05d"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ddefa9fd6794a87e37d05827d299d4b53a3ec6f23258101907b96029bfef138a"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ce3731c8f217685d33d9268362e5b4f770914e922bba94d368ab244a59a6c397"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:09f037c02fc2c43b211ff1523de32801dcfb0918648d8e651c36ef890f1731ec"}, - {file = "nh3-0.2.20-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:813f1c8012dd64c990514b795508abb90789334f76a561fa0fd4ca32d2275330"}, - {file = "nh3-0.2.20-cp38-abi3-win32.whl", hash = "sha256:47b2946c0e13057855209daeffb45dc910bd0c55daf10190bb0b4b60e2999784"}, - {file = "nh3-0.2.20-cp38-abi3-win_amd64.whl", hash = "sha256:da87573f03084edae8eb87cfe811ec338606288f81d333c07d2a9a0b9b976c0b"}, - {file = "nh3-0.2.20.tar.gz", hash = "sha256:9705c42d7ff88a0bea546c82d7fe5e59135e3d3f057e485394f491248a1f8ed5"}, +groups = ["main"] +files = [ + {file = "nh3-0.3.0-cp313-cp313t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:a537ece1bf513e5a88d8cff8a872e12fe8d0f42ef71dd15a5e7520fecd191bbb"}, + {file = "nh3-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c915060a2c8131bef6a29f78debc29ba40859b6dbe2362ef9e5fd44f11487c2"}, + {file = "nh3-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba0caa8aa184196daa6e574d997a33867d6d10234018012d35f86d46024a2a95"}, + {file = "nh3-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:80fe20171c6da69c7978ecba33b638e951b85fb92059259edd285ff108b82a6d"}, + {file = "nh3-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:e90883f9f85288f423c77b3f5a6f4486375636f25f793165112679a7b6363b35"}, + {file = "nh3-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0649464ac8eee018644aacbc103874ccbfac80e3035643c3acaab4287e36e7f5"}, + {file = "nh3-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1adeb1062a1c2974bc75b8d1ecb014c5fd4daf2df646bbe2831f7c23659793f9"}, + {file = "nh3-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:7275fdffaab10cc5801bf026e3c089d8de40a997afc9e41b981f7ac48c5aa7d5"}, + {file = "nh3-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:423201bbdf3164a9e09aa01e540adbb94c9962cc177d5b1cbb385f5e1e79216e"}, + {file = "nh3-0.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:16f8670201f7e8e0e05ed1a590eb84bfa51b01a69dd5caf1d3ea57733de6a52f"}, + {file = "nh3-0.3.0-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ec6cfdd2e0399cb79ba4dcffb2332b94d9696c52272ff9d48a630c5dca5e325a"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5e7185599f89b0e391e2f29cc12dc2e206167380cea49b33beda4891be2fe1"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:389d93d59b8214d51c400fb5b07866c2a4f79e4e14b071ad66c92184fec3a392"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e9e6a7e4d38f7e8dda9edd1433af5170c597336c1a74b4693c5cb75ab2b30f2a"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7852f038a054e0096dac12b8141191e02e93e0b4608c4b993ec7d4ffafea4e49"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af5aa8127f62bbf03d68f67a956627b1bd0469703a35b3dad28d0c1195e6c7fb"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f416c35efee3e6a6c9ab7716d9e57aa0a49981be915963a82697952cba1353e1"}, + {file = "nh3-0.3.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37d3003d98dedca6cd762bf88f2e70b67f05100f6b949ffe540e189cc06887f9"}, + {file = "nh3-0.3.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:634e34e6162e0408e14fb61d5e69dbaea32f59e847cfcfa41b66100a6b796f62"}, + {file = "nh3-0.3.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b0612ccf5de8a480cf08f047b08f9d3fecc12e63d2ee91769cb19d7290614c23"}, + {file = "nh3-0.3.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c7a32a7f0d89f7d30cb8f4a84bdbd56d1eb88b78a2434534f62c71dac538c450"}, + {file = "nh3-0.3.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3f1b4f8a264a0c86ea01da0d0c390fe295ea0bcacc52c2103aca286f6884f518"}, + {file = "nh3-0.3.0-cp38-abi3-win32.whl", hash = "sha256:6d68fa277b4a3cf04e5c4b84dd0c6149ff7d56c12b3e3fab304c525b850f613d"}, + {file = "nh3-0.3.0-cp38-abi3-win_amd64.whl", hash = "sha256:bae63772408fd63ad836ec569a7c8f444dd32863d0c67f6e0b25ebbd606afa95"}, + {file = "nh3-0.3.0-cp38-abi3-win_arm64.whl", hash = "sha256:d97d3efd61404af7e5721a0e74d81cdbfc6e5f97e11e731bb6d090e30a7b62b2"}, + {file = "nh3-0.3.0.tar.gz", hash = "sha256:d8ba24cb31525492ea71b6aac11a4adac91d828aadeff7c4586541bf5dc34d2f"}, ] [[package]] name = "packaging" -version = "24.2" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "docs", "linters", "testing"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -1092,6 +1134,7 @@ version = "3.1.0" description = "Load, configure, and compose WSGI applications and servers" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "PasteDeploy-3.1.0-py3-none-any.whl", hash = "sha256:76388ad53a661448d436df28c798063108f70e994ddc749540d733cdbd1b38cf"}, {file = "PasteDeploy-3.1.0.tar.gz", hash = "sha256:9ddbaf152f8095438a9fe81f82c78a6714b92ae8e066bed418b6a7ff6a095a95"}, @@ -1108,17 +1151,144 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["main", "linters", "types"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] +name = "pillow" +version = "11.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, + {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"}, + {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"}, + {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e"}, + {file = "pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6"}, + {file = "pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f"}, + {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"}, + {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"}, + {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"}, + {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"}, + {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94"}, + {file = "pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0"}, + {file = "pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac"}, + {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"}, + {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"}, + {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"}, + {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"}, + {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d"}, + {file = "pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149"}, + {file = "pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d"}, + {file = "pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"}, + {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"}, + {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"}, + {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"}, + {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b"}, + {file = "pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3"}, + {file = "pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51"}, + {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"}, + {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"}, + {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"}, + {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"}, + {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c"}, + {file = "pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788"}, + {file = "pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31"}, + {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"}, + {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"}, + {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"}, + {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"}, + {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a"}, + {file = "pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214"}, + {file = "pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635"}, + {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"}, + {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"}, + {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"}, + {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"}, + {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b"}, + {file = "pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12"}, + {file = "pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db"}, + {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"}, + {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"}, + {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"}, + {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"}, + {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d"}, + {file = "pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71"}, + {file = "pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada"}, + {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"}, + {file = "pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +test-arrow = ["pyarrow"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] +typing = ["typing-extensions ; python_version < \"3.10\""] +xmp = ["defusedxml"] + +[[package]] name = "plaster" version = "1.1.2" description = "A loader interface around multiple config file formats." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "plaster-1.1.2-py2.py3-none-any.whl", hash = "sha256:42992ab1f4865f1278e2ad740e8ad145683bb4022e03534265528f0c23c0df2d"}, {file = "plaster-1.1.2.tar.gz", hash = "sha256:f8befc54bf8c1147c10ab40297ec84c2676fa2d4ea5d6f524d9436a80074ef98"}, @@ -1134,6 +1304,7 @@ version = "1.0.1" description = "A loader implementing the PasteDeploy syntax to be used by plaster." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "plaster_pastedeploy-1.0.1-py2.py3-none-any.whl", hash = "sha256:ad3550cc744648969ed3b810f33c9344f515ee8d8a8cec18e8f2c4a643c2181f"}, {file = "plaster_pastedeploy-1.0.1.tar.gz", hash = "sha256:be262e6d2e41a7264875daa2fe2850cbb0615728bcdc92828fdc72736e381412"}, @@ -1148,54 +1319,58 @@ testing = ["pytest", "pytest-cov"] [[package]] name = "platformdirs" -version = "4.3.6" +version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["linters"] files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] [[package]] name = "playwright" -version = "1.49.1" +version = "1.54.0" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" +groups = ["testing"] files = [ - {file = "playwright-1.49.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:1041ffb45a0d0bc44d698d3a5aa3ac4b67c9bd03540da43a0b70616ad52592b8"}, - {file = "playwright-1.49.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f38ed3d0c1f4e0a6d1c92e73dd9a61f8855133249d6f0cec28648d38a7137be"}, - {file = "playwright-1.49.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:3be48c6d26dc819ca0a26567c1ae36a980a0303dcd4249feb6f59e115aaddfb8"}, - {file = "playwright-1.49.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:753ca90ee31b4b03d165cfd36e477309ebf2b4381953f2a982ff612d85b147d2"}, - {file = "playwright-1.49.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd9bc8dab37aa25198a01f555f0a2e2c3813fe200fef018ac34dfe86b34994b9"}, - {file = "playwright-1.49.1-py3-none-win32.whl", hash = "sha256:43b304be67f096058e587dac453ece550eff87b8fbed28de30f4f022cc1745bb"}, - {file = "playwright-1.49.1-py3-none-win_amd64.whl", hash = "sha256:47b23cb346283278f5b4d1e1990bcb6d6302f80c0aa0ca93dd0601a1400191df"}, + {file = "playwright-1.54.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bf3b845af744370f1bd2286c2a9536f474cc8a88dc995b72ea9a5be714c9a77d"}, + {file = "playwright-1.54.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:780928b3ca2077aea90414b37e54edd0c4bbb57d1aafc42f7aa0b3fd2c2fac02"}, + {file = "playwright-1.54.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:81d0b6f28843b27f288cfe438af0a12a4851de57998009a519ea84cee6fbbfb9"}, + {file = "playwright-1.54.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:09919f45cc74c64afb5432646d7fef0d19fff50990c862cb8d9b0577093f40cc"}, + {file = "playwright-1.54.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ae206c55737e8e3eae51fb385d61c0312eeef31535643bb6232741b41b6fdc"}, + {file = "playwright-1.54.0-py3-none-win32.whl", hash = "sha256:0b108622ffb6906e28566f3f31721cd57dda637d7e41c430287804ac01911f56"}, + {file = "playwright-1.54.0-py3-none-win_amd64.whl", hash = "sha256:9e5aee9ae5ab1fdd44cd64153313a2045b136fcbcfb2541cc0a3d909132671a2"}, + {file = "playwright-1.54.0-py3-none-win_arm64.whl", hash = "sha256:a975815971f7b8dca505c441a4c56de1aeb56a211290f8cc214eeef5524e8d75"}, ] [package.dependencies] -greenlet = "3.1.1" -pyee = "12.0.0" +greenlet = ">=3.1.1,<4.0.0" +pyee = ">=13,<14" [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["testing"] files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "pycparser" @@ -1203,6 +1378,8 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main", "types"] +markers = "platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -1210,131 +1387,133 @@ files = [ [[package]] name = "pydantic" -version = "2.10.6" +version = "2.11.7" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, + {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, + {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" +pydantic-core = "2.33.2" typing-extensions = ">=4.12.2" +typing-inspection = ">=0.4.0" [package.extras] email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.33.2" description = "Core functionality for Pydantic validation and serialization" optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, ] [package.dependencies] @@ -1342,30 +1521,32 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pyee" -version = "12.0.0" +version = "13.0.0" description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own" optional = false python-versions = ">=3.8" +groups = ["testing"] files = [ - {file = "pyee-12.0.0-py3-none-any.whl", hash = "sha256:7b14b74320600049ccc7d0e0b1becd3b4bd0a03c745758225e31a59f4095c990"}, - {file = "pyee-12.0.0.tar.gz", hash = "sha256:c480603f4aa2927d4766eb41fa82793fe60a82cbfdb8d688e0d08c55a534e145"}, + {file = "pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498"}, + {file = "pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37"}, ] [package.dependencies] typing-extensions = "*" [package.extras] -dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio", "pytest-trio", "sphinx", "toml", "tox", "trio", "trio", "trio-typing", "twine", "twisted", "validate-pyproject[all]"] +dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "mypy", "pytest", "pytest-asyncio ; python_version >= \"3.4\"", "pytest-trio ; python_version >= \"3.7\"", "sphinx", "toml", "tox", "trio", "trio ; python_version > \"3.6\"", "trio-typing ; python_version > \"3.6\"", "twine", "twisted", "validate-pyproject[all]"] [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" +groups = ["main", "docs", "testing"] files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -1373,27 +1554,26 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.3.4" +version = "3.3.7" description = "python code static checker" optional = false python-versions = ">=3.9.0" +groups = ["linters"] files = [ - {file = "pylint-3.3.4-py3-none-any.whl", hash = "sha256:289e6a1eb27b453b08436478391a48cd53bb0efb824873f949e709350f3de018"}, - {file = "pylint-3.3.4.tar.gz", hash = "sha256:74ae7a38b177e69a9b525d0794bd8183820bfa7eb68cc1bee6e8ed22a42be4ce"}, + {file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, + {file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, ] [package.dependencies] -astroid = ">=3.3.8,<=3.4.0-dev0" +astroid = ">=3.3.8,<=3.4.0.dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=0.3.6", markers = "python_version == \"3.11\""}, ] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<7" +isort = ">=4.2.5,<5.13 || >5.13,<7" mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +platformdirs = ">=2.2" tomlkit = ">=0.10.1" [package.extras] @@ -1406,6 +1586,7 @@ version = "2.0.2" description = "The Pyramid Web Framework, a Pylons project" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "pyramid-2.0.2-py3-none-any.whl", hash = "sha256:2e6585ac55c147f0a51bc00dadf72075b3bdd9a871b332ff9e5e04117ccd76fa"}, {file = "pyramid-2.0.2.tar.gz", hash = "sha256:372138a738e4216535cc76dcce6eddd5a1aaca95130f2354fb834264c06f18de"}, @@ -1432,6 +1613,7 @@ version = "4.12.1" description = "A package which provides an interactive HTML debugger for Pyramid application development" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pyramid_debugtoolbar-4.12.1-py3-none-any.whl", hash = "sha256:1d13a82444b3396d5a76d1e611d0de1b38da096f04440041e8e889536103864b"}, {file = "pyramid_debugtoolbar-4.12.1.tar.gz", hash = "sha256:71e888d349c85fcca12b3e6dc4c7ae8e3f02a1d5acc05154fd9ba8c7f661b43d"}, @@ -1452,6 +1634,7 @@ version = "2.10.1" description = "Jinja2 template bindings for the Pyramid web framework" optional = false python-versions = ">=3.7.0" +groups = ["main"] files = [ {file = "pyramid_jinja2-2.10.1-py3-none-any.whl", hash = "sha256:425a52a0c1cc2a83e183d22bb15cdd999112aa4c7b1e0f4e21c49a6b523ad6e1"}, {file = "pyramid_jinja2-2.10.1.tar.gz", hash = "sha256:8c508cb35c135f95149ca236110f9c8875343575740d16c5cb73a50ef1c21677"}, @@ -1473,6 +1656,7 @@ version = "1.1.0" description = "Mako template bindings for the Pyramid web framework" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyramid_mako-1.1.0-py2.py3-none-any.whl", hash = "sha256:76104592d292b6974cf7080aa52405c51f396a621535f01e274d7fe546e85a43"}, {file = "pyramid_mako-1.1.0.tar.gz", hash = "sha256:0066c863441f1c3ddea60cee1ccc50d00a91a317a8052ca44131da1a12a840e2"}, @@ -1492,6 +1676,7 @@ version = "2.1.1" description = "An execution policy for Pyramid that supports retrying requests after certain failure exceptions." optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pyramid_retry-2.1.1-py2.py3-none-any.whl", hash = "sha256:b5129a60eb9d7409234ea52839006426d2ae887b4a1f0530c75ec336cabf2476"}, {file = "pyramid_retry-2.1.1.tar.gz", hash = "sha256:baa8276ae68babad09e5f2f94efc4f7421f3b8fb526151df522052f8cd3ec0c9"}, @@ -1511,6 +1696,7 @@ version = "2.6" description = "A package which allows Pyramid requests to join the active transaction" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pyramid_tm-2.6-py3-none-any.whl", hash = "sha256:665a4ee1d6f41f0c7ffa5e54d9b01d70cd3e8e5bd76277529acbdd6b6bd6fe9e"}, {file = "pyramid_tm-2.6.tar.gz", hash = "sha256:8148d2191285280c9a0c23e6df1018b3514b4cef02115b872dd0350a4d78709c"}, @@ -1526,25 +1712,25 @@ testing = ["WebTest", "coverage (>=5.0)", "pytest", "pytest-cov"] [[package]] name = "pytest" -version = "8.3.4" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["testing"] files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} +pygments = ">=2.7.2" [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-base-url" @@ -1552,6 +1738,7 @@ version = "2.1.0" description = "pytest plugin for URL based testing" optional = false python-versions = ">=3.8" +groups = ["testing"] files = [ {file = "pytest_base_url-2.1.0-py3-none-any.whl", hash = "sha256:3ad15611778764d451927b2a53240c1a7a591b521ea44cebfe45849d2d2812e6"}, {file = "pytest_base_url-2.1.0.tar.gz", hash = "sha256:02748589a54f9e63fcbe62301d6b0496da0d10231b753e950c63e03aee745d45"}, @@ -1566,31 +1753,34 @@ test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "pytest [[package]] name = "pytest-cov" -version = "6.0.0" +version = "6.2.1" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.9" +groups = ["testing"] files = [ - {file = "pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0"}, - {file = "pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35"}, + {file = "pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5"}, + {file = "pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2"}, ] [package.dependencies] coverage = {version = ">=7.5", extras = ["toml"]} -pytest = ">=4.6" +pluggy = ">=1.2" +pytest = ">=6.2.5" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-playwright" -version = "0.6.2" +version = "0.7.0" description = "A pytest wrapper with fixtures for Playwright to automate web browsers" optional = false python-versions = ">=3.9" +groups = ["testing"] files = [ - {file = "pytest_playwright-0.6.2-py3-none-any.whl", hash = "sha256:0eff73bebe497b0158befed91e2f5fe94cfa17181f8b3acf575beed84e7e9043"}, - {file = "pytest_playwright-0.6.2.tar.gz", hash = "sha256:ff4054b19aa05df096ac6f74f0572591566aaf0f6d97f6cb9674db8a4d4ed06c"}, + {file = "pytest_playwright-0.7.0-py3-none-any.whl", hash = "sha256:2516d0871fa606634bfe32afbcc0342d68da2dbff97fe3459849e9c428486da2"}, + {file = "pytest_playwright-0.7.0.tar.gz", hash = "sha256:b3f2ea514bbead96d26376fac182f68dcd6571e7cb41680a89ff1673c05d60b6"}, ] [package.dependencies] @@ -1605,6 +1795,7 @@ version = "8.0.4" description = "A Python slugify application that also handles Unicode" optional = false python-versions = ">=3.7" +groups = ["testing"] files = [ {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, @@ -1618,36 +1809,39 @@ unidecode = ["Unidecode (>=1.1.1)"] [[package]] name = "redis" -version = "5.2.1" +version = "6.2.0" description = "Python client for Redis database and key-value store" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"}, - {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"}, + {file = "redis-6.2.0-py3-none-any.whl", hash = "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e"}, + {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, ] [package.dependencies] async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} [package.extras] -hiredis = ["hiredis (>=3.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] +hiredis = ["hiredis (>=3.2.0)"] +jwt = ["pyjwt (>=2.9.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (>=20.0.1)", "requests (>=2.31.0)"] [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main", "docs", "testing"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -1656,56 +1850,76 @@ socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] +name = "roman-numerals-py" +version = "3.1.0" +description = "Manipulate well-formed Roman numerals" +optional = false +python-versions = ">=3.9" +groups = ["docs"] +files = [ + {file = "roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c"}, + {file = "roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d"}, +] + +[package.extras] +lint = ["mypy (==1.15.0)", "pyright (==1.1.394)", "ruff (==0.9.7)"] +test = ["pytest (>=8)"] + +[[package]] name = "setuptools" -version = "75.8.0" +version = "80.9.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"}, - {file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"}, + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] -core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +version = "3.0.1" +description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." optional = false -python-versions = "*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" +groups = ["docs"] files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, + {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, + {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, ] [[package]] name = "soupsieve" -version = "2.6" +version = "2.7" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" +groups = ["testing"] files = [ - {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, - {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, + {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, + {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, ] [[package]] name = "sphinx" -version = "8.1.3" +version = "8.2.3" description = "Python documentation generator" optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" +groups = ["docs"] files = [ - {file = "sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2"}, - {file = "sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927"}, + {file = "sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3"}, + {file = "sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348"}, ] [package.dependencies] @@ -1718,6 +1932,7 @@ Jinja2 = ">=3.1" packaging = ">=23.0" Pygments = ">=2.17" requests = ">=2.30.0" +roman-numerals-py = ">=1.0.0" snowballstemmer = ">=2.2" sphinxcontrib-applehelp = ">=1.0.7" sphinxcontrib-devhelp = ">=1.0.6" @@ -1725,30 +1940,30 @@ sphinxcontrib-htmlhelp = ">=2.0.6" sphinxcontrib-jsmath = ">=1.0.1" sphinxcontrib-qthelp = ">=1.0.6" sphinxcontrib-serializinghtml = ">=1.1.9" -tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=6.0)", "mypy (==1.11.1)", "pyright (==1.1.384)", "pytest (>=6.0)", "ruff (==0.6.9)", "sphinx-lint (>=0.9)", "tomli (>=2)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.18.0.20240506)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241005)", "types-requests (==2.32.0.20240914)", "types-urllib3 (==1.26.25.14)"] -test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] +lint = ["betterproto (==2.0.0b6)", "mypy (==1.15.0)", "pypi-attestations (==0.0.21)", "pyright (==1.1.395)", "pytest (>=8.0)", "ruff (==0.9.9)", "sphinx-lint (>=0.9)", "types-Pillow (==10.2.0.20240822)", "types-Pygments (==2.19.0.20250219)", "types-colorama (==0.4.15.20240311)", "types-defusedxml (==0.7.0.20240218)", "types-docutils (==0.21.0.20241128)", "types-requests (==2.32.0.20241016)", "types-urllib3 (==1.26.25.14)"] +test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=8.0)", "pytest-xdist[psutil] (>=3.4)", "setuptools (>=70.0)", "typing_extensions (>=4.9)"] [[package]] name = "sphinx-autodoc-typehints" -version = "3.0.1" +version = "3.2.0" description = "Type hints (PEP 484) support for the Sphinx autodoc extension" optional = false -python-versions = ">=3.10" +python-versions = ">=3.11" +groups = ["docs"] files = [ - {file = "sphinx_autodoc_typehints-3.0.1-py3-none-any.whl", hash = "sha256:4b64b676a14b5b79cefb6628a6dc8070e320d4963e8ff640a2f3e9390ae9045a"}, - {file = "sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55"}, + {file = "sphinx_autodoc_typehints-3.2.0-py3-none-any.whl", hash = "sha256:884b39be23b1d884dcc825d4680c9c6357a476936e3b381a67ae80091984eb49"}, + {file = "sphinx_autodoc_typehints-3.2.0.tar.gz", hash = "sha256:107ac98bc8b4837202c88c0736d59d6da44076e65a0d7d7d543a78631f662a9b"}, ] [package.dependencies] -sphinx = ">=8.1.3" +sphinx = ">=8.2" [package.extras] docs = ["furo (>=2024.8.6)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "defusedxml (>=0.7.1)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "sphobjinv (>=2.3.1.2)", "typing-extensions (>=4.12.2)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.12)", "defusedxml (>=0.7.1)", "diff-cover (>=9.2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "sphobjinv (>=2.3.1.2)", "typing-extensions (>=4.12.2)"] [[package]] name = "sphinxcontrib-applehelp" @@ -1756,6 +1971,7 @@ version = "2.0.0" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, @@ -1772,6 +1988,7 @@ version = "2.0.0" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, @@ -1788,6 +2005,7 @@ version = "2.1.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, @@ -1804,6 +2022,7 @@ version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = false python-versions = ">=3.5" +groups = ["docs"] files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, @@ -1818,6 +2037,7 @@ version = "2.0.0" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, @@ -1834,6 +2054,7 @@ version = "2.0.0" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = false python-versions = ">=3.9" +groups = ["docs"] files = [ {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, @@ -1846,81 +2067,82 @@ test = ["pytest"] [[package]] name = "sqlalchemy" -version = "2.0.37" +version = "2.0.42" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" -files = [ - {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da36c3b0e891808a7542c5c89f224520b9a16c7f5e4d6a1156955605e54aef0e"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e7402ff96e2b073a98ef6d6142796426d705addd27b9d26c3b32dbaa06d7d069"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6f5d254a22394847245f411a2956976401e84da4288aa70cbcd5190744062c1"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41296bbcaa55ef5fdd32389a35c710133b097f7b2609d8218c0eabded43a1d84"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bedee60385c1c0411378cbd4dc486362f5ee88deceea50002772912d798bb00f"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6c67415258f9f3c69867ec02fea1bf6508153709ecbd731a982442a590f2b7e4"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-win32.whl", hash = "sha256:650dcb70739957a492ad8acff65d099a9586b9b8920e3507ca61ec3ce650bb72"}, - {file = "SQLAlchemy-2.0.37-cp310-cp310-win_amd64.whl", hash = "sha256:93d1543cd8359040c02b6614421c8e10cd7a788c40047dbc507ed46c29ae5636"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:78361be6dc9073ed17ab380985d1e45e48a642313ab68ab6afa2457354ff692c"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b661b49d0cb0ab311a189b31e25576b7ac3e20783beb1e1817d72d9d02508bf5"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d57bafbab289e147d064ffbd5cca2d7b1394b63417c0636cea1f2e93d16eb9e8"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa2c0913f02341d25fb858e4fb2031e6b0813494cca1ba07d417674128ce11b"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9df21b8d9e5c136ea6cde1c50d2b1c29a2b5ff2b1d610165c23ff250e0704087"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db18ff6b8c0f1917f8b20f8eca35c28bbccb9f83afa94743e03d40203ed83de9"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-win32.whl", hash = "sha256:46954173612617a99a64aee103bcd3f078901b9a8dcfc6ae80cbf34ba23df989"}, - {file = "SQLAlchemy-2.0.37-cp311-cp311-win_amd64.whl", hash = "sha256:7b7e772dc4bc507fdec4ee20182f15bd60d2a84f1e087a8accf5b5b7a0dcf2ba"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2952748ecd67ed3b56773c185e85fc084f6bdcdec10e5032a7c25a6bc7d682ef"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3151822aa1db0eb5afd65ccfafebe0ef5cda3a7701a279c8d0bf17781a793bb4"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eaa8039b6d20137a4e02603aba37d12cd2dde7887500b8855356682fc33933f4"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cdba1f73b64530c47b27118b7053b8447e6d6f3c8104e3ac59f3d40c33aa9fd"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1b2690456528a87234a75d1a1644cdb330a6926f455403c8e4f6cad6921f9098"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cf5ae8a9dcf657fd72144a7fd01f243236ea39e7344e579a121c4205aedf07bb"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-win32.whl", hash = "sha256:ea308cec940905ba008291d93619d92edaf83232ec85fbd514dcb329f3192761"}, - {file = "SQLAlchemy-2.0.37-cp312-cp312-win_amd64.whl", hash = "sha256:635d8a21577341dfe4f7fa59ec394b346da12420b86624a69e466d446de16aff"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8c4096727193762e72ce9437e2a86a110cf081241919ce3fab8e89c02f6b6658"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e4fb5ac86d8fe8151966814f6720996430462e633d225497566b3996966b9bdb"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e56a139bfe136a22c438478a86f8204c1eb5eed36f4e15c4224e4b9db01cb3e4"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f95fc8e3f34b5f6b3effb49d10ac97c569ec8e32f985612d9b25dd12d0d2e94"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c505edd429abdfe3643fa3b2e83efb3445a34a9dc49d5f692dd087be966020e0"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:12b0f1ec623cccf058cf21cb544f0e74656618165b083d78145cafde156ea7b6"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-win32.whl", hash = "sha256:293f9ade06b2e68dd03cfb14d49202fac47b7bb94bffcff174568c951fbc7af2"}, - {file = "SQLAlchemy-2.0.37-cp313-cp313-win_amd64.whl", hash = "sha256:d70f53a0646cc418ca4853da57cf3ddddbccb8c98406791f24426f2dd77fd0e2"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:44f569d0b1eb82301b92b72085583277316e7367e038d97c3a1a899d9a05e342"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2eae3423e538c10d93ae3e87788c6a84658c3ed6db62e6a61bb9495b0ad16bb"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfff7be361048244c3aa0f60b5e63221c5e0f0e509f4e47b8910e22b57d10ae7"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:5bc3339db84c5fb9130ac0e2f20347ee77b5dd2596ba327ce0d399752f4fce39"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:84b9f23b0fa98a6a4b99d73989350a94e4a4ec476b9a7dfe9b79ba5939f5e80b"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-win32.whl", hash = "sha256:51bc9cfef83e0ac84f86bf2b10eaccb27c5a3e66a1212bef676f5bee6ef33ebb"}, - {file = "SQLAlchemy-2.0.37-cp37-cp37m-win_amd64.whl", hash = "sha256:8e47f1af09444f87c67b4f1bb6231e12ba6d4d9f03050d7fc88df6d075231a49"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6b788f14c5bb91db7f468dcf76f8b64423660a05e57fe277d3f4fad7b9dcb7ce"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521ef85c04c33009166777c77e76c8a676e2d8528dc83a57836b63ca9c69dcd1"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75311559f5c9881a9808eadbeb20ed8d8ba3f7225bef3afed2000c2a9f4d49b9"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cce918ada64c956b62ca2c2af59b125767097ec1dca89650a6221e887521bfd7"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9d087663b7e1feabea8c578d6887d59bb00388158e8bff3a76be11aa3f748ca2"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cf95a60b36997dad99692314c4713f141b61c5b0b4cc5c3426faad570b31ca01"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-win32.whl", hash = "sha256:d75ead7dd4d255068ea0f21492ee67937bd7c90964c8f3c2bea83c7b7f81b95f"}, - {file = "SQLAlchemy-2.0.37-cp38-cp38-win_amd64.whl", hash = "sha256:74bbd1d0a9bacf34266a7907d43260c8d65d31d691bb2356f41b17c2dca5b1d0"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:648ec5acf95ad59255452ef759054f2176849662af4521db6cb245263ae4aa33"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:35bd2df269de082065d4b23ae08502a47255832cc3f17619a5cea92ce478b02b"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f581d365af9373a738c49e0c51e8b18e08d8a6b1b15cc556773bcd8a192fa8b"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82df02816c14f8dc9f4d74aea4cb84a92f4b0620235daa76dde002409a3fbb5a"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94b564e38b344d3e67d2e224f0aec6ba09a77e4582ced41e7bfd0f757d926ec9"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:955a2a765aa1bd81aafa69ffda179d4fe3e2a3ad462a736ae5b6f387f78bfeb8"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-win32.whl", hash = "sha256:03f0528c53ca0b67094c4764523c1451ea15959bbf0a8a8a3096900014db0278"}, - {file = "SQLAlchemy-2.0.37-cp39-cp39-win_amd64.whl", hash = "sha256:4b12885dc85a2ab2b7d00995bac6d967bffa8594123b02ed21e8eb2205a7584b"}, - {file = "SQLAlchemy-2.0.37-py3-none-any.whl", hash = "sha256:a8998bf9f8658bd3839cbc44ddbe982955641863da0c1efe5b00c1ab4f5c16b1"}, - {file = "sqlalchemy-2.0.37.tar.gz", hash = "sha256:12b28d99a9c14eaf4055810df1001557176716de0167b91026e648e65229bffb"}, +groups = ["main"] +files = [ + {file = "SQLAlchemy-2.0.42-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7ee065898359fdee83961aed5cf1fb4cfa913ba71b58b41e036001d90bebbf7a"}, + {file = "SQLAlchemy-2.0.42-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56bc76d86216443daa2e27e6b04a9b96423f0b69b5d0c40c7f4b9a4cdf7d8d90"}, + {file = "SQLAlchemy-2.0.42-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89143290fb94c50a8dec73b06109ccd245efd8011d24fc0ddafe89dc55b36651"}, + {file = "SQLAlchemy-2.0.42-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:4efbdc9754c7145a954911bfeef815fb0843e8edab0e9cecfa3417a5cbd316af"}, + {file = "SQLAlchemy-2.0.42-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:88f8a8007a658dfd82c16a20bd9673ae6b33576c003b5166d42697d49e496e61"}, + {file = "SQLAlchemy-2.0.42-cp37-cp37m-win32.whl", hash = "sha256:c5dd245e6502990ccf612d51f220a7b04cbea3f00f6030691ffe27def76ca79b"}, + {file = "SQLAlchemy-2.0.42-cp37-cp37m-win_amd64.whl", hash = "sha256:5651eb19cacbeb2fe7431e4019312ed00a0b3fbd2d701423e0e2ceaadb5bcd9f"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:172b244753e034d91a826f80a9a70f4cbac690641207f2217f8404c261473efe"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be28f88abd74af8519a4542185ee80ca914933ca65cdfa99504d82af0e4210df"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98b344859d282fde388047f1710860bb23f4098f705491e06b8ab52a48aafea9"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97978d223b11f1d161390a96f28c49a13ce48fdd2fed7683167c39bdb1b8aa09"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e35b9b000c59fcac2867ab3a79fc368a6caca8706741beab3b799d47005b3407"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bc7347ad7a7b1c78b94177f2d57263113bb950e62c59b96ed839b131ea4234e1"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-win32.whl", hash = "sha256:739e58879b20a179156b63aa21f05ccacfd3e28e08e9c2b630ff55cd7177c4f1"}, + {file = "sqlalchemy-2.0.42-cp310-cp310-win_amd64.whl", hash = "sha256:1aef304ada61b81f1955196f584b9e72b798ed525a7c0b46e09e98397393297b"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c34100c0b7ea31fbc113c124bcf93a53094f8951c7bf39c45f39d327bad6d1e7"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad59dbe4d1252448c19d171dfba14c74e7950b46dc49d015722a4a06bfdab2b0"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9187498c2149919753a7fd51766ea9c8eecdec7da47c1b955fa8090bc642eaa"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f092cf83ebcafba23a247f5e03f99f5436e3ef026d01c8213b5eca48ad6efa9"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc6afee7e66fdba4f5a68610b487c1f754fccdc53894a9567785932dbb6a265e"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:260ca1d2e5910f1f1ad3fe0113f8fab28657cee2542cb48c2f342ed90046e8ec"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-win32.whl", hash = "sha256:2eb539fd83185a85e5fcd6b19214e1c734ab0351d81505b0f987705ba0a1e231"}, + {file = "sqlalchemy-2.0.42-cp311-cp311-win_amd64.whl", hash = "sha256:9193fa484bf00dcc1804aecbb4f528f1123c04bad6a08d7710c909750fa76aeb"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:09637a0872689d3eb71c41e249c6f422e3e18bbd05b4cd258193cfc7a9a50da2"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3cb3ec67cc08bea54e06b569398ae21623534a7b1b23c258883a7c696ae10df"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e87e6a5ef6f9d8daeb2ce5918bf5fddecc11cae6a7d7a671fcc4616c47635e01"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b718011a9d66c0d2f78e1997755cd965f3414563b31867475e9bc6efdc2281d"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:16d9b544873fe6486dddbb859501a07d89f77c61d29060bb87d0faf7519b6a4d"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21bfdf57abf72fa89b97dd74d3187caa3172a78c125f2144764a73970810c4ee"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-win32.whl", hash = "sha256:78b46555b730a24901ceb4cb901c6b45c9407f8875209ed3c5d6bcd0390a6ed1"}, + {file = "sqlalchemy-2.0.42-cp312-cp312-win_amd64.whl", hash = "sha256:4c94447a016f36c4da80072e6c6964713b0af3c8019e9c4daadf21f61b81ab53"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:941804f55c7d507334da38133268e3f6e5b0340d584ba0f277dd884197f4ae8c"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:95d3d06a968a760ce2aa6a5889fefcbdd53ca935735e0768e1db046ec08cbf01"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cf10396a8a700a0f38ccd220d940be529c8f64435c5d5b29375acab9267a6c9"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cae6c2b05326d7c2c7c0519f323f90e0fb9e8afa783c6a05bb9ee92a90d0f04"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f50f7b20677b23cfb35b6afcd8372b2feb348a38e3033f6447ee0704540be894"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d88a1c0d66d24e229e3938e1ef16ebdbd2bf4ced93af6eff55225f7465cf350"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-win32.whl", hash = "sha256:45c842c94c9ad546c72225a0c0d1ae8ef3f7c212484be3d429715a062970e87f"}, + {file = "sqlalchemy-2.0.42-cp313-cp313-win_amd64.whl", hash = "sha256:eb9905f7f1e49fd57a7ed6269bc567fcbbdac9feadff20ad6bd7707266a91577"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed5a6959b1668d97a32e3fd848b485f65ee3c05a759dee06d90e4545a3c77f1e"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ddbaafe32f0dd12d64284b1c3189104b784c9f3dba8cc1ba7e642e2b14b906f"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37f4f42568b6c656ee177b3e111d354b5dda75eafe9fe63492535f91dfa35829"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb57923d852d38671a17abda9a65cc59e3e5eab51fb8307b09de46ed775bcbb8"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:437c2a8b0c780ff8168a470beb22cb4a25e1c63ea6a7aec87ffeb07aa4b76641"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:480f7df62f0b3ad6aa011eefa096049dc1770208bb71f234959ee2864206eefe"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-win32.whl", hash = "sha256:d119c80c614d62d32e236ae68e21dd28a2eaf070876b2f28a6075d5bae54ef3f"}, + {file = "sqlalchemy-2.0.42-cp38-cp38-win_amd64.whl", hash = "sha256:be3a02f963c8d66e28bb4183bebab66dc4379701d92e660f461c65fecd6ff399"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:78548fd65cd76d4c5a2e6b5f245d7734023ee4de33ee7bb298f1ac25a9935e0d"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cf4bf5a174d8a679a713b7a896470ffc6baab78e80a79e7ec5668387ffeccc8b"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c7ff7ba08b375f8a8fa0511e595c9bdabb5494ec68f1cf69bb24e54c0d90f2"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b3c117f65d64e806ce5ce9ce578f06224dc36845e25ebd2554b3e86960e1aed"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:27e4a7b3a7a61ff919c2e7caafd612f8626114e6e5ebbe339de3b5b1df9bc27e"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b01e0dd39f96aefda5ab002d8402db4895db871eb0145836246ce0661635ce55"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-win32.whl", hash = "sha256:49362193b1f43aa158deebf438062d7b5495daa9177c6c5d0f02ceeb64b544ea"}, + {file = "sqlalchemy-2.0.42-cp39-cp39-win_amd64.whl", hash = "sha256:636ec3dc83b2422a7ff548d0f8abf9c23742ca50e2a5cdc492a151eac7a0248b"}, + {file = "sqlalchemy-2.0.42-py3-none-any.whl", hash = "sha256:defcdff7e661f0043daa381832af65d616e060ddb54d3fe4476f51df7eaa1835"}, + {file = "sqlalchemy-2.0.42.tar.gz", hash = "sha256:160bedd8a5c28765bd5be4dec2d881e109e33b34922e50a3b881a7681773ac5f"}, ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = ">=1", markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} mypy = {version = ">=0.910", optional = true, markers = "extra == \"mypy\""} typing-extensions = ">=4.6.0" [package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (>=1)"] +aioodbc = ["aioodbc", "greenlet (>=1)"] +aiosqlite = ["aiosqlite", "greenlet (>=1)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (>=1)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (>=1)"] mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] mssql = ["pyodbc"] mssql-pymssql = ["pymssql"] @@ -1931,7 +2153,7 @@ mysql-connector = ["mysql-connector-python"] oracle = ["cx_oracle (>=8)"] oracle-oracledb = ["oracledb (>=1.0.1)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-asyncpg = ["asyncpg", "greenlet (>=1)"] postgresql-pg8000 = ["pg8000 (>=1.29.1)"] postgresql-psycopg = ["psycopg (>=3.0.7)"] postgresql-psycopg2binary = ["psycopg2-binary"] @@ -1942,13 +2164,14 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "termcolor" -version = "2.5.0" +version = "3.1.0" description = "ANSI color formatting for output in terminal" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8"}, - {file = "termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f"}, + {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"}, + {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"}, ] [package.extras] @@ -1960,61 +2183,22 @@ version = "1.3" description = "The most basic Text::Unidecode port" optional = false python-versions = "*" +groups = ["testing"] files = [ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, ] [[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - -[[package]] name = "tomlkit" -version = "0.13.2" +version = "0.13.3" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" +groups = ["linters"] files = [ - {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, - {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, ] [[package]] @@ -2023,6 +2207,7 @@ version = "5.0" description = "Transaction management for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "transaction-5.0-py3-none-any.whl", hash = "sha256:b4c0b2d49a042d86235fa76531c3356b66d7635bb0e9f29ba2512915fc7b7a42"}, {file = "transaction-5.0.tar.gz", hash = "sha256:106e7bd782bcc0cb5119fc9225b0c9a71dfc53adb938be905223adaef22b1174"}, @@ -2041,6 +2226,7 @@ version = "1.4" description = "Utility library for i18n relied on by various Repoze and Pyramid packages" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "translationstring-1.4-py2.py3-none-any.whl", hash = "sha256:5f4dc4d939573db851c8d840551e1a0fb27b946afe3b95aafc22577eed2d6262"}, {file = "translationstring-1.4.tar.gz", hash = "sha256:bf947538d76e69ba12ab17283b10355a9ecfbc078e6123443f43f2107f6376f3"}, @@ -2055,6 +2241,7 @@ version = "2.11.0.15" description = "Typing stubs for babel" optional = false python-versions = "*" +groups = ["types"] files = [ {file = "types-babel-2.11.0.15.tar.gz", hash = "sha256:282c184c8c9d81e8269212c1b8fa0d39ee88fb8bc43be47980412781c9c85f7e"}, {file = "types_babel-2.11.0.15-py3-none-any.whl", hash = "sha256:d0579f2e8adeaef3fbe2eb63e5a2ecf01767fc018e5f3f36a3c9d8b723bd62c7"}, @@ -2066,13 +2253,14 @@ types-setuptools = "*" [[package]] name = "types-cffi" -version = "1.16.0.20241221" +version = "1.17.0.20250801" description = "Typing stubs for cffi" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["types"] files = [ - {file = "types_cffi-1.16.0.20241221-py3-none-any.whl", hash = "sha256:e5b76b4211d7a9185f6ab8d06a106d56c7eb80af7cdb8bfcb4186ade10fb112f"}, - {file = "types_cffi-1.16.0.20241221.tar.gz", hash = "sha256:1c96649618f4b6145f58231acb976e0b448be6b847f7ab733dabe62dfbff6591"}, + {file = "types_cffi-1.17.0.20250801-py3-none-any.whl", hash = "sha256:67c6b271253efcc7f37938873cb3d56ee47dc3536cfa3dd75e14aec64358abe0"}, + {file = "types_cffi-1.17.0.20250801.tar.gz", hash = "sha256:3c401b22f45b7873e66586c00c2722415d6138e8e89cdc9b030e68337796d66a"}, ] [package.dependencies] @@ -2080,13 +2268,14 @@ types-setuptools = "*" [[package]] name = "types-markdown" -version = "3.7.0.20241204" +version = "3.8.0.20250708" description = "Typing stubs for Markdown" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["types"] files = [ - {file = "types_Markdown-3.7.0.20241204-py3-none-any.whl", hash = "sha256:f96146c367ea9c82bfe9903559d72706555cc2a1a3474c58ebba03b418ab18da"}, - {file = "types_markdown-3.7.0.20241204.tar.gz", hash = "sha256:ecca2b25cd23163fd28ed5ba34d183d731da03e8a5ed3a20b60daded304c5410"}, + {file = "types_markdown-3.8.0.20250708-py3-none-any.whl", hash = "sha256:d1f634931b463adf7603c012724b7e9e5eff976eb517dc700ebece2d6189b1ce"}, + {file = "types_markdown-3.8.0.20250708.tar.gz", hash = "sha256:28690251fe90757f5a99cd671c79502bc2de07aef2d35fe54117c3b1c799804a"}, ] [[package]] @@ -2095,6 +2284,7 @@ version = "24.1.0.20240722" description = "Typing stubs for pyOpenSSL" optional = false python-versions = ">=3.8" +groups = ["types"] files = [ {file = "types-pyOpenSSL-24.1.0.20240722.tar.gz", hash = "sha256:47913b4678a01d879f503a12044468221ed8576263c1540dcb0484ca21b08c39"}, {file = "types_pyOpenSSL-24.1.0.20240722-py3-none-any.whl", hash = "sha256:6a7a5d2ec042537934cfb4c9d4deb0e16c4c6250b09358df1f083682fe6fda54"}, @@ -2106,13 +2296,14 @@ types-cffi = "*" [[package]] name = "types-pytz" -version = "2024.2.0.20241221" +version = "2025.2.0.20250516" description = "Typing stubs for pytz" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["types"] files = [ - {file = "types_pytz-2024.2.0.20241221-py3-none-any.whl", hash = "sha256:8fc03195329c43637ed4f593663df721fef919b60a969066e22606edf0b53ad5"}, - {file = "types_pytz-2024.2.0.20241221.tar.gz", hash = "sha256:06d7cde9613e9f7504766a0554a270c369434b50e00975b3a4a0f6eed0f2c1a9"}, + {file = "types_pytz-2025.2.0.20250516-py3-none-any.whl", hash = "sha256:e0e0c8a57e2791c19f718ed99ab2ba623856b11620cb6b637e5f62ce285a7451"}, + {file = "types_pytz-2025.2.0.20250516.tar.gz", hash = "sha256:e1216306f8c0d5da6dafd6492e72eb080c9a166171fa80dd7a1990fd8be7a7b3"}, ] [[package]] @@ -2121,6 +2312,7 @@ version = "4.6.0.20241004" description = "Typing stubs for redis" optional = false python-versions = ">=3.8" +groups = ["types"] files = [ {file = "types-redis-4.6.0.20241004.tar.gz", hash = "sha256:5f17d2b3f9091ab75384153bfa276619ffa1cf6a38da60e10d5e6749cc5b902e"}, {file = "types_redis-4.6.0.20241004-py3-none-any.whl", hash = "sha256:ef5da68cb827e5f606c8f9c0b49eeee4c2669d6d97122f301d3a55dc6a63f6ed"}, @@ -2132,13 +2324,14 @@ types-pyOpenSSL = "*" [[package]] name = "types-requests" -version = "2.32.0.20241016" +version = "2.32.4.20250611" description = "Typing stubs for requests" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["types"] files = [ - {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, - {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, + {file = "types_requests-2.32.4.20250611-py3-none-any.whl", hash = "sha256:ad2fe5d3b0cb3c2c902c8815a70e7fb2302c4b8c1f77bdcd738192cdb3878072"}, + {file = "types_requests-2.32.4.20250611.tar.gz", hash = "sha256:741c8777ed6425830bf51e54d6abe245f79b4dcb9019f1622b773463946bf826"}, ] [package.dependencies] @@ -2146,39 +2339,57 @@ urllib3 = ">=2" [[package]] name = "types-setuptools" -version = "75.8.0.20250110" +version = "80.9.0.20250801" description = "Typing stubs for setuptools" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["types"] files = [ - {file = "types_setuptools-75.8.0.20250110-py3-none-any.whl", hash = "sha256:a9f12980bbf9bcdc23ecd80755789085bad6bfce4060c2275bc2b4ca9f2bc480"}, - {file = "types_setuptools-75.8.0.20250110.tar.gz", hash = "sha256:96f7ec8bbd6e0a54ea180d66ad68ad7a1d7954e7281a710ea2de75e355545271"}, + {file = "types_setuptools-80.9.0.20250801-py3-none-any.whl", hash = "sha256:ec908f825134af3964932e6b011dce90f54c291015139cd9cdf79741b7d31b3c"}, + {file = "types_setuptools-80.9.0.20250801.tar.gz", hash = "sha256:e1e92682fa07226415396bb4e2d31f116a16ffbe583b05b01f9910fcdea3b7e8"}, ] [[package]] name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.14.1" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main", "testing", "types"] +files = [ + {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, + {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.1" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, + {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, ] +[package.dependencies] +typing-extensions = ">=4.12.0" + [[package]] name = "urllib3" -version = "2.3.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main", "docs", "testing", "types"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -2189,6 +2400,7 @@ version = "3.1.1" description = "A library for deferring decorator actions" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "venusian-3.1.1-py3-none-any.whl", hash = "sha256:0845808a985976acbceaa1fbb871c7fac4fb28ae75453232970e9c2c2866dbf4"}, {file = "venusian-3.1.1.tar.gz", hash = "sha256:534fb3b355669283eb3954581931e5d1d071fce61d029d58f3219a5e3a6f0c41"}, @@ -2204,6 +2416,7 @@ version = "3.0.2" description = "Waitress WSGI server" optional = false python-versions = ">=3.9.0" +groups = ["main", "testing"] files = [ {file = "waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e"}, {file = "waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f"}, @@ -2219,6 +2432,7 @@ version = "1.8.9" description = "WSGI request and response object" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main", "testing"] files = [ {file = "WebOb-1.8.9-py2.py3-none-any.whl", hash = "sha256:45e34c58ed0c7e2ecd238ffd34432487ff13d9ad459ddfd77895e67abba7c1f9"}, {file = "webob-1.8.9.tar.gz", hash = "sha256:ad6078e2edb6766d1334ec3dee072ac6a7f95b1e32ce10def8ff7f0f02d56589"}, @@ -2233,13 +2447,14 @@ testing = ["coverage", "pytest (>=3.1.0)", "pytest-cov", "pytest-xdist"] [[package]] name = "webtest" -version = "3.0.3" +version = "3.0.6" description = "Helper to test WSGI applications" optional = false python-versions = ">=3.9" +groups = ["testing"] files = [ - {file = "WebTest-3.0.3-py3-none-any.whl", hash = "sha256:25a2f3b1ad273655ed22fdb4f9eff62d02f22d9d5ffeb0b58627759b1f62edff"}, - {file = "webtest-3.0.3.tar.gz", hash = "sha256:b635f6fe6584bc9737496b687155e9373f3d01bcb1b46169007da0f7ba6238f9"}, + {file = "webtest-3.0.6-py3-none-any.whl", hash = "sha256:0c4a5a3dcf745a78c7905b803d6a520a2cf241c1f00ccdf4351f52916d555543"}, + {file = "webtest-3.0.6.tar.gz", hash = "sha256:4256fd5242448f56c575bcb9afe275e305a6f0723c4b01438dbdd4dd5344944b"}, ] [package.dependencies] @@ -2257,6 +2472,7 @@ version = "5.1" description = "Zope Deprecation Infrastructure" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "zope.deprecation-5.1-py3-none-any.whl", hash = "sha256:60f957b964d8f947a4a592c647d51ce0f4f844d1f041657956ddde0d9fa9a76a"}, {file = "zope_deprecation-5.1.tar.gz", hash = "sha256:46bed4611fb53edc731aadeb64b28308bcb848f4cc150c60c948d078f7108721"}, @@ -2275,6 +2491,7 @@ version = "7.2" description = "Interfaces for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, @@ -2329,6 +2546,7 @@ version = "3.1" description = "Minimal Zope/SQLAlchemy transaction integration" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "zope.sqlalchemy-3.1-py3-none-any.whl", hash = "sha256:fdc7d65d8da335a34b90fb993e8217ef12808bad3025d2e3a6720db4138e4985"}, {file = "zope.sqlalchemy-3.1.tar.gz", hash = "sha256:d9c2c3be695c213c5e22b7f7c6a4a214fa8eb5940b033465ba1c10a9d8b346db"}, @@ -2345,6 +2563,6 @@ transaction = ">=1.6.0" test = ["zope.testing"] [metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "5862b6f9b19b0b451a9c0a01e37ed6a68bd241d43f16a7d2e40d3d25104880f5" +lock-version = "2.1" +python-versions = ">=3.11" +content-hash = "e35a5cd2b50b9643fbca57a4c55384041263bd8fffb5b53f118b5040c3fa2b83" diff --git a/pyproject.toml b/pyproject.toml index 3338057..be56bfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,15 +2,54 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.poetry] +[project] name = "fietsboek" description = "GPX file sharing website" -version = "0.10.0" +version = "0.11.0" license = "AGPL-3.0-or-later" readme = "README.md" authors = [ - "Daniel Schadt <fietsboek@kingdread.de>", + { name = "Daniel Schadt", email = "fietsboek@kingdread.de>" }, +] +keywords = ["web", "gpx"] +requires-python = ">=3.11" +dependencies = [ + "pyramid (>=2, <3)", + "pyramid_jinja2 (>=2.10, <3.0)", + "pyramid_debugtoolbar (>=4.9, <5.0)", + "pyramid_retry (>=2.1, <3.0)", + "pyramid_tm (>=2.5, <3.0)", + "waitress (>=3, <4)", + + "SQLAlchemy[mypy] (>=2.0.15, <3.0.0)", + "alembic (>=1.8, <2.0)", + "transaction (>=5, <6)", + "zope.sqlalchemy (>=3.0, <4.0)", + "redis (>=6, <7)", + + "Babel (>=2.11, <3.0)", + "cryptography (>=45, <46)", + "gpxpy (>=1.5, <2.0)", + "markdown (>=3.4, <4.0)", + "nh3 (>=0.3.0,<0.4.0)", + "Click (>=8.1, <9.0)", + "requests (>=2.28.1, <3.0.0)", + + "pydantic (>=2, <3)", + "termcolor (>=3.1.0, <4.0.0)", + "filelock (>=3.8.2, <4.0.0)", + "brotli (>=1.0.9, <2.0.0)", + "click-option-group (>=0.5.5, <0.6.0)", + "fitparse (>=1.2.0, <2.0.0)", + "pillow (>=11.2.1, <12.0.0)", ] + +[project.urls] +documentation = "https://docs.fietsboek.org/" +homepage = "https://fietsboek.org/" +repository = "https://gitlab.com/dunj3/fietsboek" + +[tool.poetry] classifiers = [ 'Development Status :: 3 - Alpha', 'Framework :: Pyramid', @@ -19,41 +58,6 @@ classifiers = [ 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', ] -documentation = "https://docs.fietsboek.org/" -homepage = "https://fietsboek.org/" -repository = "https://gitlab.com/dunj3/fietsboek" -keywords = ["web", "gpx"] - -[tool.poetry.dependencies] -python = "^3.10" - -pyramid = "^2" -pyramid_jinja2 = "^2.10" -pyramid_debugtoolbar = "^4.9" -pyramid_retry = "^2.1" -pyramid_tm = "^2.5" -waitress = "^3" - -SQLAlchemy = { version = "^2.0.15", extras = ["mypy"] } -alembic = "^1.8" -transaction = "^5" -"zope.sqlalchemy" = "^3.0" -redis = "^5" - -Babel = "^2.11" -cryptography = "^44" -gpxpy = "^1.5" -markdown = "^3.4" -nh3 = "^0.2.9" -Click = "^8.1" -requests = "^2.28.1" - -pydantic = "^2" -termcolor = "^2.1.1" -filelock = "^3.8.2" -brotli = "^1.0.9" -click-option-group = "^0.5.5" -fitparse = "^1.2.0" [tool.poetry.group.docs] optional = true @@ -69,7 +73,7 @@ optional = true pytest = "^8.0.0" webtest = "^3.0.0" pytest-cov = "^6.0.0" -pytest-playwright = "^0.6.0" +pytest-playwright = "^0.7.0" [tool.poetry.group.linters] optional = true @@ -88,12 +92,12 @@ types-requests = "^2.28.11.5" types-babel = "^2.11.0.7" types-redis = "^4.3.21.6" -[tool.poetry.scripts] +[project.scripts] fietsctl = "fietsboek.scripts.fietsctl:cli" fietscron = "fietsboek.scripts.fietscron:cli" fietsupdate = "fietsboek.updater.cli:cli" -[tool.poetry.plugins."paste.app_factory"] +[project.entry-points."paste.app_factory"] main = "fietsboek:main" [tool.black] diff --git a/release-checklist.md b/release-checklist.md index 3b9632f..67e4ca4 100644 --- a/release-checklist.md +++ b/release-checklist.md @@ -7,7 +7,7 @@ - [ ] Commit those changes (`git add ... && git commit -m 'bump version to X.Y.Z'`) - [ ] Make sure the directory is clean (no uncommited changes) - [ ] Make sure the tests & lints pass - - [ ] Make sure they also do so on **Python 3.10** (current minimum version) + - [ ] Make sure they also do so on **Python 3.11** (current minimum version) - [ ] Create a new git tag: `git tag -a vX.Y.Z` - [ ] Push the tag to the remote repositories - [ ] Publish to PyPI: `poetry publish` diff --git a/testing.ini b/testing.ini index 82fddfd..ed53bdc 100644 --- a/testing.ini +++ b/testing.ini @@ -12,6 +12,8 @@ pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en +# The sqlalchemy.url is overwritten by the test setup script for different +# database engines. We leave sqlite here as default so a local tox run works fine. sqlalchemy.url = sqlite:///%(here)s/testing.sqlite # The pytest tests usually overwrite this with a temporary directory. Since # this is cleaned on test teardown, we don't want to accidentally delete data diff --git a/tests/bootstrap/test_new_instance.py b/tests/bootstrap/test_new_instance.py index dc3076e..05076f4 100644 --- a/tests/bootstrap/test_new_instance.py +++ b/tests/bootstrap/test_new_instance.py @@ -2,6 +2,7 @@ script, as described in the documentation. """ +import configparser import contextlib import logging import os @@ -10,6 +11,8 @@ import subprocess import venv from pathlib import Path +import sqlalchemy + LOGGER = logging.getLogger(__name__) REPO_BASE = Path(__file__).parent.parent.parent @@ -51,31 +54,66 @@ def create_config(config_name: Path): Path("data").mkdir() +def cleanup_database(config_name: Path): + """Connects to the database and ensures everything is reset. + + :param config_name: Path to the config file. + """ + if not config_name.exists(): + return + + parser = configparser.ConfigParser() + parser["DEFAULT"]["here"] = str(config_name.parent) + parser.read(config_name) + + db_url = parser["app:main"]["sqlalchemy.url"] + engine = sqlalchemy.create_engine(db_url) + + match engine.name: + case "sqlite": + pass + case "postgresql": + with engine.connect() as connection: + connection.execute(sqlalchemy.text("DROP SCHEMA public CASCADE;")) + connection.execute(sqlalchemy.text("CREATE SCHEMA public;")) + connection.commit() + + def test_setup_via_fietsupdate(tmpdir): with chdir(tmpdir): - # We create a new temporary virtual environment with a fresh install, just - # to be sure there's as little interference as possible. - LOGGER.info("Installing Fietsboek into clean env") - binaries_path = install_fietsboek(tmpdir / "venv") - - LOGGER.info("Creating a test configuration") - create_config(Path("testing.ini")) - - # Try to run the migrations - subprocess.check_call( - [binaries_path / "fietsupdate", "update", "-c", "testing.ini", "-f"] - ) - - # Also try to add an administrator - subprocess.check_call([ - binaries_path / "fietsctl", - "user", - "add", - "-c", "testing.ini", - "--email", "foobar@example.com", - "--name", "Foo Bar", - "--password", "raboof", - "--admin", - ]) - - assert True + try: + # We create a new temporary virtual environment with a fresh install, just + # to be sure there's as little interference as possible. + LOGGER.info("Installing Fietsboek into clean env") + binaries_path = install_fietsboek(tmpdir / "venv") + + LOGGER.info("Installing additional SQL engines") + subprocess.check_call( + [binaries_path / "pip", "install", "psycopg2"] + ) + + LOGGER.info("Creating a test configuration") + create_config(Path("testing.ini")) + + # Try to run the migrations + subprocess.check_call( + [binaries_path / "fietsupdate", "update", "-c", "testing.ini", "-f"] + ) + + # Also try to add an administrator + subprocess.check_call([ + binaries_path / "fietsctl", + "user", + "add", + "-c", "testing.ini", + "--email", "foobar@example.com", + "--name", "Foo Bar", + "--password", "raboof", + "--admin", + ]) + + assert True + finally: + # Clean up the database. This is important with anything but SQLite, as + # the tables would otherwise persist and interfere with the other tests. + cleanup_database(Path("testing.ini")) diff --git a/tests/conftest.py b/tests/conftest.py index cd74b0b..652d443 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,6 +52,7 @@ def dbengine(app_settings, ini_file): yield engine + engine.dispose() Base.metadata.drop_all(bind=engine) alembic.command.stamp(alembic_cfg, None, purge=True) diff --git a/tests/integration/test_browse.py b/tests/integration/test_browse.py index 875821d..83218cc 100644 --- a/tests/integration/test_browse.py +++ b/tests/integration/test_browse.py @@ -22,6 +22,7 @@ def added_tracks(tm, dbsession, owner, data_manager): tm.abort() tracks = [] + track_ids = [] with tm: track = models.Track( owner=owner, @@ -37,6 +38,7 @@ def added_tracks(tm, dbsession, owner, data_manager): dbsession.flush() data_manager.initialize(track.id).compress_gpx(load_gpx_asset("MyTourbook_1.gpx.gz")) tracks.append(track) + track_ids.append(track.id) track = models.Track( owner=owner, @@ -52,12 +54,62 @@ def added_tracks(tm, dbsession, owner, data_manager): dbsession.flush() data_manager.initialize(track.id).compress_gpx(load_gpx_asset("Teasi_1.gpx.gz")) tracks.append(track) + track_ids.append(track.id) tm.begin() tm.doom() try: - yield tracks + yield track_ids + finally: + tm.abort() + with tm: + for track in tracks: + dbsession.delete(track) + tm.begin() + tm.doom() + + +@contextmanager +def a_lot_of_tracks(tm, dbsession, owner, data_manager): + """Adds some tracks to the database session. + + This function should be used as a context manager and it ensures that the + added tracks are deleted again after the test, to make a clean slate for + the next test. + """ + # The normal transaction is "doomed", so we need to abort it, start a fresh + # one, and then explicitely commit it, otherwise we will not persist the + # objects to the database. + tm.abort() + + gpx_data = load_gpx_asset("MyTourbook_1.gpx.gz") + + tracks = [] + track_ids = [] + with tm: + for index in range(50): + track = models.Track( + owner=owner, + title=f"Traxi {index}", + visibility=Visibility.PUBLIC, + description="One of many", + badges=[], + link_secret="foobar", + tagged_people=[], + ) + track.date = datetime(2022 - index, 3, 14, 9, 26, 59) + dbsession.add(track) + dbsession.flush() + data_manager.initialize(track.id).compress_gpx(gpx_data) + tracks.append(track) + track_ids.append(track.id) + + tm.begin() + tm.doom() + + try: + yield track_ids finally: tm.abort() with tm: @@ -78,15 +130,43 @@ def test_browse(testapp, dbsession, route_path, logged_in, tm, data_manager): assert "Barfoo" in browse.text +def test_browse_paged(testapp, dbsession, route_path, logged_in, tm, data_manager): + # pylint: disable=too-many-positional-arguments + with a_lot_of_tracks(tm, dbsession, logged_in, data_manager): + page_1 = testapp.get(route_path("browse", _query=[("page", 1)])) + assert "Traxi 0" in page_1.text + assert "Traxi 10" in page_1.text + assert "Traxi 20" not in page_1.text + assert "Traxi 30" not in page_1.text + assert "Traxi 40" not in page_1.text + + page_2 = testapp.get(route_path("browse", _query=[("page", 2)])) + assert "Traxi 0" not in page_2.text + assert "Traxi 10" not in page_2.text + assert "Traxi 20" in page_2.text + assert "Traxi 30" in page_2.text + assert "Traxi 40" not in page_2.text + + page_3 = testapp.get(route_path("browse", _query=[("page", 3)])) + assert "Traxi 0" not in page_3.text + assert "Traxi 10" not in page_3.text + assert "Traxi 20" not in page_3.text + assert "Traxi 30" not in page_3.text + assert "Traxi 40" in page_3.text + + def test_archive(testapp, dbsession, route_path, logged_in, tm, data_manager): # pylint: disable=too-many-positional-arguments - with added_tracks(tm, dbsession, logged_in, data_manager): + with added_tracks(tm, dbsession, logged_in, data_manager) as tracks: archive = testapp.get( - route_path('track-archive', _query=[("track_id[]", "1"), ("track_id[]", "2")]) + route_path( + 'track-archive', + _query=[("track_id[]", tracks[0]), ("track_id[]", tracks[1])], + ) ) result = io.BytesIO(archive.body) with zipfile.ZipFile(result, 'r') as zipped: assert len(zipped.namelist()) == 2 - assert "track_1.gpx" in zipped.namelist() - assert "track_2.gpx" in zipped.namelist() + assert f"track_{tracks[0]}.gpx" in zipped.namelist() + assert f"track_{tracks[1]}.gpx" in zipped.namelist() diff --git a/tests/playwright/conftest.py b/tests/playwright/conftest.py index 4e7f5a4..adf5ef3 100644 --- a/tests/playwright/conftest.py +++ b/tests/playwright/conftest.py @@ -7,10 +7,12 @@ from wsgiref import simple_server from pyramid.authentication import AuthTktCookieHelper from pyramid.testing import DummyRequest +import fietsboek.config from testutils import load_gpx_asset from fietsboek import models, util, actions from fietsboek.models.track import Visibility, TrackType from fietsboek.config import Config +from fietsboek.views.tileproxy import TileRequester import pytest @@ -55,7 +57,11 @@ def dbaccess(app): through and the running WSGI app cannot read them. """ session_factory = app.registry["dbsession_factory"] - return session_factory() + factory = session_factory() + + yield factory + + factory.close() class Helper: @@ -112,11 +118,14 @@ class Helper: """ if user is None: user = self.john_doe() + config = fietsboek.config.parse(self.app_settings) with self.dbaccess: user = self.dbaccess.merge(user) track = actions.add_track( self.dbaccess, self.data_manager, + TileRequester(None), + config.public_tile_layers()[0], owner=user, title="Another awesome track", visibility=Visibility.PRIVATE, diff --git a/tests/playwright/test_basic.py b/tests/playwright/test_basic.py index 231962e..3ae0f58 100644 --- a/tests/playwright/test_basic.py +++ b/tests/playwright/test_basic.py @@ -101,6 +101,33 @@ def test_edit(page: Page, playwright_helper, dbaccess): assert track.description == "Not so descriptive anymore" +def test_edit_change_gpx(page: Page, playwright_helper, tmp_path, dbaccess): + playwright_helper.login() + track_id = playwright_helper.add_track().id + + track = dbaccess.execute(select(models.Track).filter_by(id=track_id)).scalar_one() + old_cache = track.cache.length, track.cache.uphill, track.cache.downhill + + page.goto(f"/track/{track_id}") + page.locator(".btn", has_text="Edit").click() + + gpx_data = load_gpx_asset("Synthetic_BRouter_1.gpx.gz") + gpx_path = tmp_path / "NewGPX.gpx" + with open(gpx_path, "wb") as gpx_fobj: + gpx_fobj.write(gpx_data) + + page.get_by_label("New file for this track").set_input_files(gpx_path) + page.locator(".btn", has_text="Save").click() + + track = dbaccess.execute(select(models.Track).filter_by(id=track_id)).scalar_one() + new_cache = track.cache + dbaccess.refresh(new_cache) + + assert old_cache[0] != new_cache.length + assert old_cache[1] != new_cache.uphill + assert old_cache[2] != new_cache.downhill + + def test_browse(page: Page, playwright_helper, dbaccess): playwright_helper.login() track = playwright_helper.add_track() diff --git a/tests/playwright/test_profiles.py b/tests/playwright/test_profiles.py index 7e5fb3c..ffbaab0 100644 --- a/tests/playwright/test_profiles.py +++ b/tests/playwright/test_profiles.py @@ -5,7 +5,7 @@ def test_forbidden(page: Page, playwright_helper): john = playwright_helper.john_doe() with page.expect_response(lambda resp: resp.status == 403): - page.goto(f"/user/{john.id}") + page.goto(f"/user/{john.id}/") def test_profile(page: Page, playwright_helper): diff --git a/tests/playwright/test_share.py b/tests/playwright/test_share.py index de288a0..dcba899 100644 --- a/tests/playwright/test_share.py +++ b/tests/playwright/test_share.py @@ -29,7 +29,8 @@ def test_view_wrong_link(page: Page, playwright_helper, dbaccess): with page.expect_response(lambda resp: resp.status == 403): page.goto(f"/track/{track.id}?secret=foobar") - assert "Forbidden" in page.content() + assert "No entry" in page.content() + assert "not allowed to access" in page.content() def test_change_link(page: Page, playwright_helper, dbaccess): diff --git a/tests/playwright/test_tileproxy.py b/tests/playwright/test_tileproxy.py index d4d3389..2a2bdc0 100644 --- a/tests/playwright/test_tileproxy.py +++ b/tests/playwright/test_tileproxy.py @@ -14,7 +14,7 @@ def test_tileproxy(page: Page, playwright_helper, caplog): # If we're too fast, the log entry might not be there yet, wait 2 more # seconds - if "Skipping tile proxy request for testing URL" not in caplog.messages: + if "Skipping tile request for testing URL" not in caplog.messages: time.sleep(2) - assert "Skipping tile proxy request for testing URL" in caplog.messages + assert "Skipping tile request for testing URL" in caplog.messages @@ -11,7 +11,9 @@ envlist = python,pylint,pylint-tests,flake,mypy,black,isort isolated_build = true [testenv] -deps = poetry +deps = + poetry + psycopg2 skip_install = true passenv = TERM @@ -51,7 +53,7 @@ changedir={toxinidir}{/}doc commands_pre = poetry install -v --with docs commands = - sphinx-apidoc -d 1 -f -M -e -o developer/module/ ../fietsboek "upd_*" + sphinx-apidoc -d 1 -M -e -o developer/module/ ../fietsboek "upd_*" make html mkdir -p _build/man rst2man man/fietsctl.rst _build/man/fietsctl.1 |